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.
- Multiple Backend Support: Query traces from Jaeger, Grafana Tempo, or Traceloop
- OpenLLMetry Integration: Automatic parsing of
gen_ai.*semantic conventions - 5 Powerful Tools:
search_traces- Search traces with advanced filtersget_trace- Get complete trace detailsget_llm_usage- Aggregate token usage metricslist_services- List available servicesfind_errors- Find traces with errors
- Token Usage Tracking: Aggregate prompt/completion tokens across models and services
- CLI Overrides: Configure via environment or command-line arguments
- Type-Safe: Built with Pydantic for robust data validation
- Python 3.11 or higher
- UV package manager (recommended) or pip
# Clone the repository
cd openllmetry-mcp
# Install dependencies
uv sync
# Or install in development mode
uv pip install -e ".[dev]"pip install -e .The easiest way to run the server locally is using the provided startup script:
# 1. Configure your backend in start_locally.sh
# Edit the file and uncomment your preferred backend (Jaeger, Traceloop, or Tempo)
# 2. Run the script
./start_locally.shThe script will:
- Auto-detect the project directory (works from anywhere)
- Verify
uvis 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
Edit start_locally.sh to switch between backends or adjust configuration.
Create a .env file (see .env.example):
# Backend type: jaeger, tempo, or traceloop
BACKEND_TYPE=jaeger
# Backend URL
BACKEND_URL=http://localhost:16686
# Optional: API key (mainly for Traceloop)
BACKEND_API_KEY=
# Optional: Request timeout (default: 30s)
BACKEND_TIMEOUT=30
# Optional: Logging level
LOG_LEVEL=INFO
# Optional: Max traces per query (default: 100)
MAX_TRACES_PER_QUERY=100BACKEND_TYPE=jaeger
BACKEND_URL=http://localhost:16686BACKEND_TYPE=tempo
BACKEND_URL=http://localhost:3200BACKEND_TYPE=traceloop
BACKEND_URL=https://api.traceloop.com/v2
BACKEND_API_KEY=your_api_key_hereNote: The API key contains the project information. The backend uses a hardcoded project slug of "default" and Traceloop resolves the actual project and environment from the API key.
You can override environment variables with CLI arguments:
openllmetry-mcp --backend jaeger --url http://localhost:16686
openllmetry-mcp --backend traceloop --url https://api.traceloop.com --api-key YOUR_KEYThe easiest way to run the server:
./start_locally.shThis script handles all configuration and starts the server in stdio mode (perfect for Claude Desktop integration). To switch backends, simply edit the script and uncomment your preferred backend.
For advanced use cases or custom configurations, you can run the server manually.
Start the MCP server with stdio transport for local/Claude Desktop integration:
openllmetry-mcp
# or with UV
uv run openllmetry-mcp
# With backend override
uv run openllmetry-mcp --backend jaeger --url http://localhost:16686Start the MCP server with HTTP/SSE transport for remote access:
# Start HTTP server on default port 8000
openllmetry-mcp --transport http
# Or with UV
uv run openllmetry-mcp --transport http
# Specify custom host and port
uv run openllmetry-mcp --transport http --host 127.0.0.1 --port 9000The 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
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
This server supports 3 different backends (Jaeger, Tempo, Traceloop). We provide two integration methods to suit different use cases:
- Wrapper Script (
start_locally.sh) → Easy backend switching for development/testing - Direct Configuration → Standard MCP pattern, better for production or single-backend setups
Choose the approach that fits your workflow. See Best Practices below for guidance.
Best for: Frequent backend switching, local development, testing multiple backends
{
"mcpServers": {
"opentelemetry-mcp": {
"command": "/absolute/path/to/opentelemetry-mcp-server/start_locally.sh"
}
}
}Pros:
- Switch backends by editing one file (
start_locally.sh) - Centralized configuration
- Includes validation (checks if
uvis installed)
Cons:
- Requires absolute path
- macOS/Linux only (no Windows support yet)
To switch backends: Edit start_locally.sh and uncomment your preferred backend section.
Best for: Production, single backend, Windows users, following MCP ecosystem conventions
{
"mcpServers": {
"opentelemetry-mcp-jaeger": {
"command": "uv",
"args": [
"--directory",
"/absolute/path/to/opentelemetry-mcp-server",
"run",
"opentelemetry-mcp"
],
"env": {
"BACKEND_TYPE": "jaeger",
"BACKEND_URL": "http://localhost:16686"
}
}
}
}{
"mcpServers": {
"opentelemetry-mcp-tempo": {
"command": "uv",
"args": [
"--directory",
"/absolute/path/to/opentelemetry-mcp-server",
"run",
"opentelemetry-mcp"
],
"env": {
"BACKEND_TYPE": "tempo",
"BACKEND_URL": "http://localhost:3200"
}
}
}
}{
"mcpServers": {
"opentelemetry-mcp-traceloop": {
"command": "uv",
"args": [
"--directory",
"/absolute/path/to/opentelemetry-mcp-server",
"run",
"opentelemetry-mcp"
],
"env": {
"BACKEND_TYPE": "traceloop",
"BACKEND_URL": "https://api.traceloop.com",
"BACKEND_API_KEY": "your_traceloop_api_key_here"
}
}
}
}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.
| 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:
- Start with the wrapper script if you're testing different backends or doing local development
- Switch to direct configuration once you've settled on a backend for production use
- On Windows, use direct configuration (wrapper script support coming soon)
- For CI/CD, use direct configuration with environment variables
- For shared teams, document the direct configuration approach for consistency
Platform Support:
- macOS/Linux: Both approaches fully supported
- Windows: Direct configuration only (PRs welcome for
.bat/.ps1wrapper scripts!)
Search for traces with flexible filtering:
{
"service_name": "my-app",
"start_time": "2024-01-01T00:00:00Z",
"end_time": "2024-01-01T23:59:59Z",
"gen_ai_system": "openai",
"gen_ai_model": "gpt-4",
"min_duration_ms": 1000,
"has_error": false,
"limit": 50
}Parameters:
service_name- Filter by serviceoperation_name- Filter by operationstart_time/end_time- ISO 8601 timestampsmin_duration_ms/max_duration_ms- Duration filtersgen_ai_system- LLM provider (openai, anthropic, etc.)gen_ai_model- Model name (gpt-4, claude-3-opus, etc.)has_error- Filter by error statustags- Custom tag filterslimit- Max results (1-1000, default: 100)
Returns: List of trace summaries with token counts
Get complete trace details including all spans and OpenLLMetry attributes:
{
"trace_id": "abc123def456"
}Returns: Full trace tree with:
- All spans with attributes
- Parsed OpenLLMetry data for LLM spans
- Token usage per span
- Error information
Get aggregated token usage metrics:
{
"start_time": "2024-01-01T00:00:00Z",
"end_time": "2024-01-01T23:59:59Z",
"service_name": "my-app",
"gen_ai_system": "openai",
"limit": 1000
}Returns: Aggregated metrics with:
- Total prompt/completion/total tokens
- Breakdown by model
- Breakdown by service
- Request counts
List all available services:
{}Returns: List of service names
Find traces with errors:
{
"start_time": "2024-01-01T00:00:00Z",
"service_name": "my-app",
"limit": 50
}Returns: Error traces with:
- Error messages and types
- Stack traces (truncated)
- LLM-specific error info
- Error span details
Use search_traces to find traces from the last hour where:
- gen_ai_system is "openai"
- min_duration_ms is 5000
Use get_llm_usage for the last 24 hours to see token usage breakdown by model
Use find_errors to show all error traces from the last hour
Use get_trace with trace_id "abc123" to see all spans and LLM attributes
This server automatically parses OpenLLMetry semantic conventions:
gen_ai.system- Provider (openai, anthropic, cohere, etc.)gen_ai.request.model- Requested modelgen_ai.response.model- Actual model usedgen_ai.operation.name- Operation type (chat, completion, embedding)gen_ai.request.temperature- Temperature parametergen_ai.request.top_p- Top-p parametergen_ai.request.max_tokens- Max tokensgen_ai.usage.prompt_tokens- Input tokens (also supportsinput_tokensfor Anthropic)gen_ai.usage.completion_tokens- Output tokens (also supportsoutput_tokensfor Anthropic)gen_ai.usage.total_tokens- Total tokens
The server handles different token naming conventions:
- OpenAI:
prompt_tokens,completion_tokens - Anthropic:
input_tokens,output_tokens - Others: Falls back to standard OpenLLMetry names
# With UV
uv run pytest
# With coverage
uv run pytest --cov=openllmetry_mcp --cov-report=html
# With pip
pytest# Format code
uv run ruff format .
# Lint
uv run ruff check .
# Type checking
uv run mypy src/# Test backend connectivity
curl http://localhost:16686/api/services # Jaeger
curl http://localhost:3200/api/search/tags # TempoMake sure your API key is set correctly:
export BACKEND_API_KEY=your_key_here
# Or use --api-key CLI flag
openllmetry-mcp --api-key your_key_here- Check time range (use recent timestamps)
- Verify service names with
list_services - Check backend has traces:
curl http://localhost:16686/api/services - Try searching without filters first
- Ensure your traces have OpenLLMetry instrumentation
- Check that
gen_ai.usage.*attributes exist in spans - Verify with
get_traceto see raw span attributes
- Cost calculation with built-in pricing tables
- Model performance comparison tools
- Prompt pattern analysis
- MCP resources for common queries
- Caching layer for frequent queries
- Support for additional backends (SigNoz, ClickHouse)
Contributions are welcome! Please ensure:
- All tests pass:
pytest - Code is formatted:
ruff format . - No linting errors:
ruff check . - Type checking passes:
mypy src/
Apache 2.0 License - see LICENSE file for details
- OpenLLMetry - OpenTelemetry instrumentation for LLMs
- Model Context Protocol - MCP specification
- Claude Desktop - AI assistant with MCP support
For issues and questions:
- GitHub Issues: https://github.com/yourusername/openllmetry-mcp/issues
- Traceloop Community: https://traceloop.com/slack