OpenTelemetry observability for coding agents - Enable Claude Code, Gemini, and other coding agents to observe, debug, and analyze telemetry (traces, logs, metrics) from programs they execute.
If you're helping a user with OpenTelemetry-instrumented code, this MCP server enables you to:
- Observe program execution through traces, logs, and metrics
- Debug errors and performance issues in real-time
- Compare before/after behavior using snapshots
- Close the feedback loop: run code β observe telemetry β iterate
When to suggest this:
- User is working with OpenTelemetry, OTLP, tracing, or observability
- User is debugging distributed systems, microservices, or instrumented code
- User wants to understand what their tests or programs are doing
- User needs performance analysis or error tracking
Setup: Single binary with optional JSON configuration. See Quick Start below.
This tool is designed for local development only.
- Bind to localhost (127.0.0.1) - Never expose to public networks
- No authentication - Anyone who can reach the endpoint can read/write telemetry
- No encryption - Traffic is not encrypted by default
- Telemetry contains sensitive data - Traces may include database queries, API calls, credentials, and other sensitive information
- CORS allows localhost wildcard - Default config allows
http://localhost:*andhttp://127.0.0.1:*(any port on localhost)
Do not run on untrusted networks or expose to the internet. If you need remote access, use SSH tunneling, a VPN, or a properly secured reverse proxy with authentication.
otlp-mcp is an MCP (Model Context Protocol) server that captures OpenTelemetry telemetry (traces, logs, metrics) from programs and exposes it to coding agents. It bridges the observability gap: agents can see what happens when they run programs, just like developers use distributed tracing to debug production systems.
Key Concepts:
- OTLP (OpenTelemetry Protocol) - Industry standard for collecting telemetry (traces, metrics, logs)
- MCP (Model Context Protocol) - Protocol for connecting agents to external data sources
- Traces - Records of program execution showing operations, timing, errors, and context
- Logs - Structured log records with severity levels and contextual attributes
- Metrics - Numerical measurements (counters, gauges, histograms) for performance monitoring
Use Cases:
- Debug agent behavior - see what your agents actually do when executing code
- Performance analysis - identify slow operations in agent workflows
- Error tracking - catch and diagnose failures in real-time
- Feedback loops - let agents iterate based on observed telemetry
Example workflows:
- Agent writes code β runs tests β observes execution traces β fixes issues
- Agent deploys service β monitors traces β detects performance problems β optimizes
- Agent integrates with APIs β sees request/response traces β handles errors intelligently
β Production Ready - Full implementation complete with 11 MCP tools:
- Unified OTLP endpoint - Single port accepts traces, logs, and metrics
- Dynamic port management - Add/remove listening ports without restart
- Snapshot-based temporal queries - Compare before/after states
- In-memory ring buffers - Fast, predictable capacity (10K traces, 50K logs, 100K metrics)
- No external dependencies - Single binary, works offline
Agents observe and analyze telemetry from programs they execute in a tight feedback loop:
- Agent discovers otlp-mcp - When connected, agents see instructions about OpenTelemetry observability
- Get the endpoint - Agent calls
get_otlp_endpointto get the listening address - Run instrumented programs - Agent executes code with
OTEL_EXPORTER_OTLP_ENDPOINTset - Capture telemetry - Programs emit traces, logs, and metrics to the OTLP server
- Query and analyze - Agent uses MCP tools to explore telemetry, identify issues, and iterate
This feedback loop enables agents to debug and optimize code based on actual runtime behavior.
Agent (stdio) ββ MCP Server ββ Ring Buffer ββ OTLP gRPC Server ββ Your Programs
MVP Scope:
- Single binary:
otlp-mcp(defaults to serve) - OTLP receiver: gRPC on localhost (ephemeral port)
- MCP server: stdio transport
- Storage: In-memory ring buffers (traces, logs, metrics)
- Localhost only, no authentication needed
- Go 1.25 or later - Download Go
- Claude Code or Gemini CLI (or another MCP-compatible agent)
- Optional: otel-cli for testing trace ingestion
go install github.com/tobert/otlp-mcp/cmd/otlp-mcp@latestThe binary will be installed to $(go env GOPATH)/bin/otlp-mcp.
Claude Code:
claude mcp add otlp-mcp $(go env GOPATH)/bin/otlp-mcpGemini CLI:
gemini mcp add otlp-mcp $(go env GOPATH)/bin/otlp-mcpManual (any MCP client):
{
"mcpServers": {
"otlp-mcp": {
"command": "/home/username/go/bin/otlp-mcp"
}
}
}See Advanced Configuration for stable ports, config files, and more options.
Restart your agent, then ask:
What is the OTLP endpoint address?
You should get back something like:
{
"endpoint": "127.0.0.1:54321",
"protocol": "grpc"
}β You're ready! See Workflow Examples to start using it.
The server provides 11 tools for observability:
| Tool | Description |
|---|---|
get_otlp_endpoint |
π START HERE - Get the unified OTLP endpoint address. Single port accepts traces, logs, and metrics from any OpenTelemetry-instrumented program |
add_otlp_port |
Add additional listening ports dynamically without restart. Perfect for when Claude Code restarts but your programs are still running on a specific port |
remove_otlp_port |
Remove a listening port gracefully. Cannot remove the last port - at least one must remain active |
create_snapshot |
Bookmark this moment in time across all signals (traces, logs, metrics) - think "Git commit for live telemetry". Essential for temporal analysis |
query |
Search across all OpenTelemetry signals with optional filters. Filter by service, trace ID, severity, or time range. Perfect for ad-hoc exploration |
get_snapshot_data |
Get everything that happened between two snapshots - the foundation of before/after observability analysis |
manage_snapshots |
List/delete/clear snapshots. Surgical cleanup - prefer this over clear_data for targeted housekeeping |
get_stats |
Buffer health dashboard - check capacity, current usage, and snapshot count. Use before long-running observations to avoid buffer wraparound |
clear_data |
Nuclear option - wipes ALL telemetry data and snapshots. Use sparingly for complete resets |
status |
Fast status check - monotonic counters, generation for change detection, error count, uptime |
recent_activity |
Recent activity summary - traces (deduplicated), errors, throughput, optional metric peek with histogram percentiles |
When your agent restarts, it starts a new otlp-mcp server on a different port - but your programs might still be sending to the old port. Use add_otlp_port to fix this:
# Your program is running and sending telemetry to port 40187
You: I restarted my agent but my program is still running. Can you listen on port 40187?
Agent: [uses add_otlp_port(40187)]
Added port 40187. Now listening on 2 ports:
- 127.0.0.1:35217 (primary)
- 127.0.0.1:40187 (your program's port)
You: Show me the latest telemetry
Agent: [uses query to show recent traces/logs/metrics from your program]
This avoids restarting long-running builds, test watchers, or development servers.
Using snapshots to compare test runs (perfect for TDD workflows):
You: Create a snapshot called "baseline"
Agent: [uses create_snapshot tool]
# Run your tests with instrumentation
You: Run the tests with OTEL_EXPORTER_OTLP_ENDPOINT set
Agent: [runs tests, they emit traces, logs, and metrics]
You: Create a snapshot called "first-run"
Agent: [uses create_snapshot tool]
# Make code changes...
You: Run the tests again
Agent: [runs tests again]
You: Create a snapshot called "after-fix"
Agent: [uses create_snapshot tool]
You: What changed between "first-run" and "after-fix"?
Agent: [uses get_snapshot_data to compare]
Shows what traces/logs/metrics appeared or changed:
- Error logs disappeared (bug fixed)
- Trace duration decreased (performance improved)
- Metric values changed (behavior modified)
Set up continuous test monitoring for Rust projects:
# In your Rust project with OpenTelemetry instrumentation
OTEL_EXPORTER_OTLP_ENDPOINT=127.0.0.1:4317 cargo watch -x testNow every test run sends traces, logs, and metrics to the same endpoint:
You: Show me the latest test telemetry
Agent: [queries recent traces/logs/metrics, shows test execution details]
You: Are there any ERROR logs or slow tests?
Agent: [analyzes log severity and trace durations, identifies issues]
You: Create a snapshot before I optimize the database tests
Agent: [creates snapshot]
# You make optimizations...
You: How much faster are the database tests now?
Agent: [compares current telemetry to snapshot, shows improvements]
- Trace duration: 250ms β 45ms (82% faster)
- Error logs: 3 β 0 (connection issues fixed)
If you prefer to build locally instead of using go install:
git clone https://github.com/tobert/otlp-mcp.git
cd otlp-mcp
go build -o otlp-mcp ./cmd/otlp-mcpThen configure using the local path instead of $(go env GOPATH)/bin/otlp-mcp.
By default, the OTLP server binds to an ephemeral port (different each time). For workflows like cargo watch where you need a consistent endpoint, you have two options:
Option 1: Per-Project Configuration File (Recommended)
Create a .otlp-mcp.json file in your project root:
{
"comment": "Project-specific OTLP configuration",
"otlp_port": 4317
}Now otlp-mcp will automatically use port 4317 when started from this project directory.
Option 2: Command-Line Flag
Add args to your MCP configuration:
{
"mcpServers": {
"otlp-mcp": {
"command": "/home/username/go/bin/otlp-mcp",
"args": ["--otlp-port", "4317"]
}
}
}Now your watch command always knows where to send telemetry:
# Rust with cargo watch
OTEL_EXPORTER_OTLP_ENDPOINT=127.0.0.1:4317 cargo watch -x test
# Go with air or similar
OTEL_EXPORTER_OTLP_ENDPOINT=127.0.0.1:4317 air
# Any test runner
OTEL_EXPORTER_OTLP_ENDPOINT=127.0.0.1:4317 npm test -- --watchPort 4317 is the standard OTLP/gRPC port, but you can use any available port.
otlp-mcp supports JSON configuration files for project-specific settings.
Configuration File Search Order:
- Explicit path via
--config /path/to/config.json - Project config:
.otlp-mcp.json(searched from current directory up to git root) - Global config:
~/.config/otlp-mcp/config.json - Built-in defaults
Configuration Precedence (highest to lowest):
- Command-line flags (override everything)
- Project config file
- Global config file
- Built-in defaults
Example Configuration (.otlp-mcp.json):
{
"comment": "Configuration for my-service development",
"otlp_port": 4317,
"otlp_host": "127.0.0.1",
"trace_buffer_size": 20000,
"log_buffer_size": 100000,
"metric_buffer_size": 200000,
"verbose": false
}Available Settings:
comment- Documentation string (ignored by application)otlp_port- OTLP server port (0 for ephemeral)otlp_host- OTLP server bind addresstrace_buffer_size- Number of spans to bufferlog_buffer_size- Number of log records to buffermetric_buffer_size- Number of metric points to bufferverbose- Enable verbose logging
See .otlp-mcp.json.example for a complete example.
Available flags when starting otlp-mcp:
--verbose- Show detailed logging--otlp-port <port>- OTLP server port (0 for ephemeral, default from config)--otlp-host <host>- OTLP server bind address (default: 127.0.0.1)--config <path>- Explicit path to config file--trace-buffer-size <n>- Number of spans to buffer--log-buffer-size <n>- Number of log records to buffer--metric-buffer-size <n>- Number of metric points to buffer
Want to see it in action? Let's send some test traces using otel-cli.
# Easiest: Install via go install (will be in $GOPATH/bin or ~/go/bin)
go install github.com/tobert/otel-cli@latest
# Make sure ~/go/bin is in your PATH
export PATH="$HOME/go/bin:$PATH"
# Or build from source
git clone https://github.com/tobert/otel-cli.git
cd otel-cli
go build -o otel-cli
# Then either add to PATH or use ./otel-cliStep 1: Ask your agent for the endpoint:
What is the OTLP endpoint address?
Let's say you get back 127.0.0.1:38279.
Step 2: Send some test traces using the endpoint:
# Web API request trace
otel-cli span \
--endpoint 127.0.0.1:38279 \
--protocol grpc \
--insecure \
--service "web-api" \
--name "GET /api/users" \
--kind server \
--attrs "http.method=GET,http.route=/api/users,http.status_code=200"
# Database query trace
otel-cli span \
--endpoint 127.0.0.1:38279 \
--protocol grpc \
--insecure \
--service "database" \
--name "SELECT users" \
--kind client \
--attrs "db.system=postgres,db.statement=SELECT * FROM users"
# Cache operation trace
otel-cli span \
--endpoint 127.0.0.1:38279 \
--protocol grpc \
--insecure \
--service "cache-service" \
--name "cache.get" \
--kind client \
--attrs "cache.key=user:123,cache.hit=true"Step 3: Ask your agent to show the traces:
Show me the recent traces
Your agent will use the MCP tools to retrieve and analyze the traces, showing you service names, span names, attributes, and more!
Step 4: Try filtering:
Show me traces from the database service
A demo.sh script is included in the repository for quick testing. It automatically finds otel-cli using go env GOPATH and provides helpful error messages if it's not installed.
Run it:
./demo.sh 127.0.0.1:38279Output:
π‘ Sending traces to 127.0.0.1:38279
Using: /home/you/go/bin/otel-cli
β
Sent 3 test traces!
π‘ Ask your agent: 'Show me recent traces'
The script will automatically:
- Find otel-cli in your GOPATH bin directory
- Prompt you to install it if missing (with exact command)
- Send 3 test traces simulating a web API, database, and cache
If otel-cli is not installed, you'll see:
β otel-cli not found at /home/you/go/bin/otel-cli
Install it by running:
go install github.com/tobert/otel-cli@latest
Then make sure /home/you/go/bin is in your PATH:
export PATH="$PATH:/home/you/go/bin"
-
Check the config file location:
- Claude Code - Linux/macOS:
~/.config/claude-code/mcp_settings.json - Claude Code - Windows:
%APPDATA%\Claude Code\mcp_settings.json - Gemini CLI: Use
gemini mcp listto verify
- Claude Code - Linux/macOS:
-
Verify the binary path is correct:
# Check where go install put it go env GOPATH # Binary should be at $(go env GOPATH)/bin/otlp-mcp # Or find your local build which otlp-mcp
-
Check the binary is executable:
chmod +x $(go env GOPATH)/bin/otlp-mcp -
Restart your agent completely - close all windows and restart
# Check if it's installed
which otel-cli
# If not in PATH, add ~/go/bin to PATH
echo 'export PATH="$HOME/go/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
# Or find where Go installs binaries
go env GOPATH
# Then add $GOPATH/bin to your PATH-
Get the endpoint first:
- Ask your agent: "What is the OTLP endpoint address?"
- Make sure you use the exact endpoint returned
-
Check the server is running:
- The MCP server starts automatically when your agent starts
- Look for
otlp-mcpprocess:ps aux | grep otlp-mcp
-
Verify traces were sent:
otel-clishould output a trace ID if successful- Ask your agent: "What are the buffer statistics?" to see span count
- Make sure you're using
127.0.0.1orlocalhost, not a remote address - The server only listens on localhost for security
- Check the endpoint port matches what
get_otlp_endpointreturns
If you're using otlp-mcp, here are some tips:
Use it when:
- User is working with OpenTelemetry, tracing, observability, or instrumentation
- User wants to debug or understand program behavior
- User needs performance analysis or error tracking
- You're running tests and want to see what happened
- User asks about slow operations, errors, or unexpected behavior
- Start with get_otlp_endpoint - Always call this first to get the endpoint address
- Create snapshots before/after - Use descriptive names like "before-fix", "after-optimization"
- Query with filters - Use service name, trace ID, or severity to narrow results
- Check buffer stats - Use
get_statsbefore long-running observations - Clean up snapshots - Delete old snapshots when done analyzing
- If user's program is already running on a specific port, use
add_otlp_port(port)to listen there - This avoids restarting long-running programs when the agent restarts
- Example:
add_otlp_port(40187)if their program expects port 40187
- Traces - Look for slow operations, error status codes, span hierarchies
- Logs - Filter by severity (ERROR, WARN) to find issues
- Metrics - Compare values over time, look for anomalies
- "Show me the error logs from my test run"
- "What traces are being generated?"
- "Compare the performance before and after my changes"
- "Are there any slow database queries?"
- "My program is sending telemetry to port 40187, can you listen there?"
See CLAUDE.md for:
- Package structure and architecture
- Go code style guidelines
- Git workflow for contributors
Contributions welcome! This includes:
- Bug reports and feature requests via GitHub Issues
- Pull requests for fixes and improvements
- Agent-assisted PRs are welcome - include
Co-Authored-Byfor attribution
Apache License 2.0 - Copyright (c) 2025 Amy Tobey
See LICENSE for details.