Metadata-Version: 2.4
Name: jaeger-mcp
Version: 0.3.0
Summary: MCP server for Jaeger distributed tracing — search traces, inspect spans, compare traces, compute span statistics, map service dependencies.
Project-URL: Homepage, https://github.com/mshegolev/jaeger-mcp
Project-URL: Issues, https://github.com/mshegolev/jaeger-mcp/issues
Author: Mikhail Shchegolev
License-Expression: MIT
License-File: LICENSE
Keywords: anthropic,claude,jaeger,mcp,observability,opentelemetry,tracing
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Monitoring
Requires-Python: >=3.10
Requires-Dist: mcp>=1.2
Requires-Dist: pydantic>=2.0
Requires-Dist: requests>=2.31
Requires-Dist: typing-extensions>=4.5; python_version < '3.12'
Requires-Dist: urllib3>=2.0
Provides-Extra: dev
Requires-Dist: pytest-cov>=4; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: responses>=0.25; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Description-Content-Type: text/markdown

# jaeger-mcp

<!-- mcp-name: io.github.mshegolev/jaeger-mcp -->

[![PyPI version](https://img.shields.io/pypi/v/jaeger-mcp.svg)](https://pypi.org/project/jaeger-mcp/)
[![Python versions](https://img.shields.io/pypi/pyversions/jaeger-mcp.svg)](https://pypi.org/project/jaeger-mcp/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Tests](https://github.com/mshegolev/jaeger-mcp/actions/workflows/test.yml/badge.svg)](https://github.com/mshegolev/jaeger-mcp/actions/workflows/test.yml)

**MCP server for [Jaeger](https://www.jaegertracing.io/) distributed tracing.**
Give Claude (or any MCP-capable agent) read access to your trace data — search traces, inspect spans, compare traces, compute span statistics, map service dependencies — without leaving the conversation.

## Why another Jaeger MCP?

The existing Jaeger integrations require a running UI or custom scripts. This server:

- Speaks the standard [Model Context Protocol](https://modelcontextprotocol.io/) over **stdio** — works with Claude Desktop, Claude Code, Cursor, and any MCP client.
- Is **read-only**: all 7 tools carry `readOnlyHint: true` — zero risk of modifying trace data.
- Returns **dual-channel output**: structured JSON (`structuredContent`) for programmatic use + Markdown (`content`) for human-readable display.
- Has **actionable error messages** that name the exact env var to fix and suggest a next step.
- Supports **Bearer token**, **HTTP Basic auth**, or **no auth** (common for internal deployments).

## Tools

| Tool | Endpoint | Description |
|------|----------|-------------|
| `jaeger_list_services` | `GET /api/services` | List all instrumented services |
| `jaeger_list_operations` | `GET /api/services/{service}/operations` | List operation names for a service |
| `jaeger_search_traces` | `GET /api/traces` | Search traces with rich filters |
| `jaeger_get_trace` | `GET /api/traces/{traceID}` | Full trace detail with span tree |
| `jaeger_get_dependencies` | `GET /api/dependencies` | Service-to-service call graph |
| `jaeger_compare_traces` | `GET /api/traces/{traceID}` ×2 | Structural diff between two traces |
| `jaeger_span_statistics` | `GET /api/traces` | Per-operation latency and error stats |

## Installation

```bash
pip install jaeger-mcp
```

Or run directly without installing:

```bash
uvx jaeger-mcp
```

## Configuration

All configuration is via environment variables:

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `JAEGER_URL` | **Yes** | — | Jaeger query service URL, e.g. `https://jaeger.example.com` |
| `JAEGER_TOKEN` | No | — | Bearer token (takes precedence over Basic auth) |
| `JAEGER_USERNAME` | No | — | HTTP Basic auth username |
| `JAEGER_PASSWORD` | No | — | HTTP Basic auth password |
| `JAEGER_SSL_VERIFY` | No | `true` | Set `false` for self-signed certificates |
| `JAEGER_TIMEOUT` | No | `30` | HTTP request timeout in seconds |
| `JAEGER_RETRY_ATTEMPTS` | No | `3` | Retry count for transient failures (0 to disable) |
| `JAEGER_CACHE_TTL` | No | `120` | TTL in seconds for discovery endpoint cache (0 to disable) |

Copy `.env.example` to `.env` and fill in your values.

## Claude Desktop / Claude Code setup

Add to your MCP config (`claude_desktop_config.json` or `.claude/mcp.json`):

```json
{
  "mcpServers": {
    "jaeger": {
      "command": "jaeger-mcp",
      "env": {
        "JAEGER_URL": "https://jaeger.example.com",
        "JAEGER_TOKEN": "your-token-here"
      }
    }
  }
}
```

Or with `uvx` (no install required):

```json
{
  "mcpServers": {
    "jaeger": {
      "command": "uvx",
      "args": ["jaeger-mcp"],
      "env": {
        "JAEGER_URL": "https://jaeger.example.com"
      }
    }
  }
}
```

## Docker

```bash
docker run --rm -e JAEGER_URL=https://jaeger.example.com jaeger-mcp
```

## Example queries

Once configured, ask Claude:

- "What services does Jaeger know about?"
- "Find traces with HTTP 500 errors in `order-service` from the last hour"
- "Show me the slowest traces (over 2 seconds) for `GET /checkout`"
- "What caused the error in trace `abcdef1234567890`?"
- "Map the service dependency graph for the last 7 days"
- "Which services call `postgres` most frequently?"
- "Compare trace `abc123` against trace `def456` — what spans changed?"
- "What are the p95 latencies per operation in `order-service`?"

## Tool usage guide

### `jaeger_list_services`

Returns all service names Jaeger has seen. **Start here** when you don't know which services are instrumented. Output is capped at 500 services with a truncation hint.

### `jaeger_list_operations`

Returns all operation names for a given service (e.g. HTTP route names, gRPC method names). Use to discover valid operation names before filtering `jaeger_search_traces`.

### `jaeger_search_traces`

The main search tool. Filters:

- `service` (required) — service name from `jaeger_list_services`
- `operation` — narrow to a specific endpoint
- `tags` — JSON string of tag filters, e.g. `{"http.status_code":"500"}` or `{"error":"true"}`
- `start` / `end` — time range in **microseconds** UTC
- `min_duration` / `max_duration` — duration strings like `"100ms"`, `"1.5s"`, `"2m"`
- `limit` — default 20, max 1500

Returns trace summaries with `trace_id`, `duration_us`, `span_count`, `service_count`, `root_operation`, `errors_count`.

### `jaeger_get_trace`

Full trace detail. Accepts a `trace_id` (hex string, 16-32 chars) and returns:

- All spans with tags, service names, parent/child relationships
- Per-service statistics (span count, total duration, error count)
- Execution tree (each node lists its child span IDs)

Error spans are identified by `tags["error"] = "true"`.

### `jaeger_get_dependencies`

Service topology graph. Returns directed edges `(parent → child)` with `call_count`. Use `lookback_hours` (default 24, max 720) to control the window.

### `jaeger_compare_traces`

Structural diff between two traces. Accepts two `trace_id` hex strings and matches spans by `(operationName, serviceName, parentOperation)` — not span ID. Reports:

- **Added spans** — present in trace B but not trace A
- **Removed spans** — present in trace A but not trace B
- **Changed spans** — matched but differ in duration or tags (shows deltas)
- **Unchanged count** — number of identical spans

Use to compare a slow trace against a fast one, or to see what changed between deployments.

### `jaeger_span_statistics`

Per-operation latency percentiles and error rates. Fetches up to `limit` traces (default 20, max 100) for a service and aggregates all spans by operation name. Reports per operation:

- `count` — total spans observed
- `p50_duration_us`, `p95_duration_us`, `p99_duration_us` — latency percentiles
- `error_count`, `error_rate` — errors (identified by `tags["error"] = "true"`)

Use to find the slowest or most error-prone operations in a service.

## Library facade (in-process use)

`jaeger-mcp` can also be used as a Python library without an MCP server:

```python
from jaeger_mcp import JaegerClient

client = JaegerClient.from_env()  # reads JAEGER_URL from env
trace = client.get_trace("abcdef1234567890abcdef1234567890")

for span in trace.spans:
    if span.error:
        print(f"{span.service_name}: {span.operation} at {span.start_utc}")
        print(f"  tags: {span.tags}")
```

Available methods: `get_trace()`, `search_traces()`, `list_services()`, `get_dependencies()`, `compare_traces()`, `span_statistics()`.

Domain objects: `Span`, `Trace`, `TraceSummary`, `ServiceDep`, `TraceComparison`, `SpanIdentity`, `SpanChange`, `SpanStatisticsResult`, `OperationStatResult` — all with typed fields.

## Performance characteristics

- All tools use a single persistent `requests.Session` with connection pooling.
- The session has `trust_env = False` to bypass environment proxies (Jaeger is typically an internal service).
- Requests time out after 30 seconds (configurable via `JAEGER_TIMEOUT`).
- Transient HTTP errors (429/5xx) are retried with exponential backoff (configurable via `JAEGER_RETRY_ATTEMPTS`).
- `list_services` and `list_operations` responses are cached for 120 seconds (configurable via `JAEGER_CACHE_TTL`).
- `jaeger_search_traces` passes `limit` directly to Jaeger — avoid requesting more traces than needed.
- `jaeger_get_trace` fetches the full trace in one call — large traces (thousands of spans) may be slow.
- `jaeger_get_dependencies` aggregates over the full lookback window; large windows may be slow on busy clusters.

## Development

```bash
git clone https://github.com/mshegolev/jaeger-mcp
cd jaeger-mcp
pip install -e '.[dev]'
pytest tests/ -v
ruff check src tests
ruff format src tests
```

## License

MIT — see [LICENSE](LICENSE).
