Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .cz.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[tool.commitizen]
name = "cz_conventional_commits"
version = "0.1.0"
tag_format = "v$version"
update_changelog_on_bump = true
major_version_zero = true
version_scheme = "pep440"

# Files where Commitizen will update the version
version_files = [
"pyproject.toml:version",
"src/opentelemetry_mcp/__init__.py:__version__"
]

# Changelog configuration
[tool.commitizen.changelog]
# Categorize commits by type
file = "CHANGELOG.md"
103 changes: 103 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: Release

on:
workflow_dispatch: # Manual trigger via GitHub Actions UI

permissions:
contents: write # Required for pushing commits, tags, and creating releases
id-token: write # Required for PyPI OIDC (Trusted Publishing)

jobs:
bump-version:
name: Bump Version & Create Release
runs-on: ubuntu-latest
outputs:
new_version: ${{ steps.cz.outputs.version }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history required for Commitizen
token: ${{ secrets.GH_ACCESS_TOKEN }} # Use PAT for pushing

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install Commitizen
run: pip install commitizen

- name: Bump version
id: cz
uses: commitizen-tools/commitizen-action@master
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pin the commitizen action version instead of using @master to avoid unexpected changes.

Suggested change
uses: commitizen-tools/commitizen-action@master
uses: commitizen-tools/commitizen-action@v3

with:
github_token: ${{ secrets.GH_ACCESS_TOKEN }}
changelog_increment_filename: body.md

- name: Print version info
run: |
echo "New version: ${{ steps.cz.outputs.version }}"
echo "Version bump: ${{ steps.cz.outputs.version }}"
- name: Create GitHub Release
env:
GITHUB_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }}
run: |
gh release create "${{ steps.cz.outputs.version }}" \
--title "Release ${{ steps.cz.outputs.version }}" \
--notes-file "body.md"
build-and-publish:
name: Build and Publish to PyPI
needs: bump-version
runs-on: ubuntu-latest
permissions:
id-token: write # Required for PyPI OIDC
steps:
- name: Checkout code (with new version)
uses: actions/checkout@v4
with:
ref: main # Get the version-bumped code

- name: Setup UV
uses: astral-sh/setup-uv@v3
with:
version: "latest"
enable-cache: true

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Build package
run: |
uv build --wheel --sdist
ls -lh dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
# No credentials needed - uses OIDC/Trusted Publishing!

test-installation:
name: Test PyPI Installation
needs: [bump-version, build-and-publish]
runs-on: ubuntu-latest
steps:
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Wait for package availability
run: sleep 60 # Wait for PyPI to index the package

- name: Test installation from PyPI
run: |
pip install opentelemetry-mcp==${{ needs.bump-version.outputs.new_version }}
python -c "import opentelemetry_mcp; print(f'Installed version: {opentelemetry_mcp.__version__}')"
opentelemetry-mcp --help
- name: Installation successful
run: echo "✅ Package successfully published and verified on PyPI!"
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ cython_debug/
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
# and can be added to the global gitignore or merged into this file. However, if you prefer,
# you could uncomment the following to ignore the entire vscode folder
# .vscode/
.vscode/

# Ruff stuff:
.ruff_cache/
Expand Down
36 changes: 25 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# OpenTelemetry-MCP-Server

Unified MCP server for querying OpenTelemetry traces across multiple backends (Jaeger, Tempo, Traceloop, etc.), enabling AI agents to analyze distributed traces for automated debugging and observability.

An MCP (Model Context Protocol) server for querying OpenTelemetry traces from LLM applications, with specialized support for OpenLLMetry semantic conventions.
Expand Down Expand Up @@ -57,12 +58,14 @@ The easiest way to run the server locally is using the provided startup script:
```

The script will:

- Auto-detect the project directory (works from anywhere)
- Verify `uv` is installed
- Set up your backend configuration
- Start the MCP server in stdio mode (ready for Claude Desktop)

**Supported Backends:**

- **Jaeger** (local): `http://localhost:16686`
- **Traceloop** (cloud): `https://api.traceloop.com` (requires API key)
- **Tempo** (local): `http://localhost:3200`
Expand Down Expand Up @@ -177,12 +180,14 @@ uv run openllmetry-mcp --transport http --host 127.0.0.1 --port 9000
The HTTP server will be accessible at `http://localhost:8000/sse` by default.

**Transport Use Cases:**

- **stdio transport**: Local use, Claude Desktop integration, single process
- **HTTP transport**: Remote access, multiple clients, network deployment, sample applications

### Integrating with Claude Desktop

Configure the MCP server in your Claude Desktop config file:

- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`

Expand Down Expand Up @@ -210,11 +215,13 @@ Choose the approach that fits your workflow. See [Best Practices](#best-practice
```

**Pros:**

- Switch backends by editing one file (`start_locally.sh`)
- Centralized configuration
- Includes validation (checks if `uv` is installed)

**Cons:**

- Requires absolute path
- macOS/Linux only (no Windows support yet)

Expand Down Expand Up @@ -292,29 +299,31 @@ Choose the approach that fits your workflow. See [Best Practices](#best-practice
```

**Pros:**

- Standard MCP ecosystem pattern
- Works on all platforms (Windows/macOS/Linux)
- Can configure multiple backends simultaneously (use different server names)
- No wrapper script dependency

**Cons:**

- Must edit JSON config to switch backends
- Backend configuration split between script and config file

**Tip:** You can configure multiple backends at once (e.g., `opentelemetry-mcp-jaeger` and `opentelemetry-mcp-tempo`) and Claude will show both as available MCP servers.

### Best Practices: Choosing an Approach

| Scenario | Recommended Approach | Why |
|----------|---------------------|-----|
| **Development & Testing** | Wrapper Script (`start_locally.sh`) | Easy to switch backends, centralized config |
| **Testing multiple backends** | Wrapper Script | Edit one file to switch, no JSON editing |
| **Production deployment** | Direct Configuration | Standard MCP pattern, explicit configuration |
| **Single backend only** | Direct Configuration | Simpler, no wrapper needed |
| **Windows users** | Direct Configuration | Wrapper script not yet supported on Windows |
| **macOS/Linux users** | Either approach | Choose based on your workflow |
| **Multiple backends simultaneously** | Direct Configuration | Configure all backends with different names |
| **Shared team configuration** | Direct Configuration | More portable, follows MCP conventions |
| Scenario | Recommended Approach | Why |
| ------------------------------------ | ----------------------------------- | -------------------------------------------- |
| **Development & Testing** | Wrapper Script (`start_locally.sh`) | Easy to switch backends, centralized config |
| **Testing multiple backends** | Wrapper Script | Edit one file to switch, no JSON editing |
| **Production deployment** | Direct Configuration | Standard MCP pattern, explicit configuration |
| **Single backend only** | Direct Configuration | Simpler, no wrapper needed |
| **Windows users** | Direct Configuration | Wrapper script not yet supported on Windows |
| **macOS/Linux users** | Either approach | Choose based on your workflow |
| **Multiple backends simultaneously** | Direct Configuration | Configure all backends with different names |
| **Shared team configuration** | Direct Configuration | More portable, follows MCP conventions |

**General Guidelines:**

Expand Down Expand Up @@ -349,6 +358,7 @@ Search for traces with flexible filtering:
```

**Parameters:**

- `service_name` - Filter by service
- `operation_name` - Filter by operation
- `start_time` / `end_time` - ISO 8601 timestamps
Expand All @@ -372,6 +382,7 @@ Get complete trace details including all spans and OpenLLMetry attributes:
```

**Returns:** Full trace tree with:

- All spans with attributes
- Parsed OpenLLMetry data for LLM spans
- Token usage per span
Expand All @@ -392,6 +403,7 @@ Get aggregated token usage metrics:
```

**Returns:** Aggregated metrics with:

- Total prompt/completion/total tokens
- Breakdown by model
- Breakdown by service
Expand Down Expand Up @@ -420,6 +432,7 @@ Find traces with errors:
```

**Returns:** Error traces with:

- Error messages and types
- Stack traces (truncated)
- LLM-specific error info
Expand Down Expand Up @@ -559,7 +572,7 @@ Contributions are welcome! Please ensure:

## License

MIT License - see LICENSE file for details
Apache 2.0 License - see LICENSE file for details

## Related Projects

Expand All @@ -570,5 +583,6 @@ MIT License - see LICENSE file for details
## Support

For issues and questions:

- GitHub Issues: https://github.com/yourusername/openllmetry-mcp/issues
- Traceloop Community: https://traceloop.com/slack
33 changes: 33 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,33 @@ authors = [
{name = "Nir Gazit", email = "nir@traceloop.com"}
]
readme = "README.md"
license = {text = ""Apache-2.0"}
requires-python = ">=3.11"
keywords = [
"mcp",
"opentelemetry",
"observability",
"tracing",
"llm",
"model-context-protocol",
"debugging",
"telemetry",
"jaeger",
"tempo",
"traceloop"
]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Topic :: System :: Monitoring",
"Topic :: Software Development :: Libraries :: Python Modules",
"License :: OSI Approved :: "Apache-2.0 License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Framework :: FastAPI",
]
dependencies = [
"mcp~=1.20.0",
"fastmcp~=2.13.0",
Expand All @@ -22,6 +48,13 @@ dependencies = [
"opentelemetry-semantic-conventions>=0.48b0",
]

[project.urls]
Homepage = "https://github.com/traceloop/opentelemetry-mcp-server"
Documentation = "https://github.com/traceloop/opentelemetry-mcp-server#readme"
Repository = "https://github.com/traceloop/opentelemetry-mcp-server"
Issues = "https://github.com/traceloop/opentelemetry-mcp-server/issues"
Changelog = "https://github.com/traceloop/opentelemetry-mcp-server/blob/main/CHANGELOG.md"

[project.scripts]
opentelemetry-mcp = "opentelemetry_mcp.server:main"

Expand Down
7 changes: 5 additions & 2 deletions start_locally.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@ fi
# Uncomment and configure ONE of the backends below:

## Jaeger (local)
export BACKEND_TYPE="jaeger"
export BACKEND_URL="http://localhost:16686"
# export BACKEND_TYPE="jaeger"
# export BACKEND_URL="http://localhost:16686"

## Traceloop (cloud)
export BACKEND_TYPE="traceloop"
export BACKEND_URL="https://api-staging.traceloop.com"
export BACKEND_API_KEY="tl_9981e7218948437584e08e7b724304d8" # Set your API key here or via environment
# export BACKEND_TYPE="traceloop"
# export BACKEND_URL="https://api.traceloop.com"
# export BACKEND_API_KEY="your-api-key-here" # Set your API key here or via environment
Expand Down
Loading
Loading