Skip to content

MCP

The autotel monorepo ships two Model Context Protocol packages with different jobs. Pick the one you need:

PackagePurpose
autotel-mcp-instrumentationInstrument your own MCP servers and clients with OpenTelemetry.
autotel-mcpA standalone MCP server that lets AI agents query your traces, metrics, and logs.

If you’re building an MCP-based product → use autotel-mcp-instrumentation. If you want Claude / Cursor / Windsurf to investigate observability data → install autotel-mcp.


Automatic instrumentation for MCP servers and clients. W3C Trace Context propagates through the _meta field, which works across stdio, HTTP, SSE, or any custom transport. Bundle ~7KB total.

Terminal window
npm install autotel-mcp-instrumentation @modelcontextprotocol/sdk autotel
import { Server } from '@modelcontextprotocol/sdk/server/index';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio';
import { instrumentMcpServer } from 'autotel-mcp-instrumentation/server';
import { init } from 'autotel';
init({ service: 'mcp-weather-server', endpoint: 'http://localhost:4318' });
const server = new Server({ name: 'weather', version: '1.0.0' });
const instrumented = instrumentMcpServer(server, {
captureArgs: true,
captureResults: false, // off by default; PII concern
});
instrumented.registerTool({
name: 'get_weather',
description: 'Get current weather for a location',
inputSchema: {
type: 'object',
properties: { location: { type: 'string' } },
required: ['location'],
},
handler: async (args) => {
// Auto-traced; parent context extracted from _meta
const weather = await fetchWeather(args.location);
return { content: [{ type: 'text', text: `Temp: ${weather.temp}°F` }] };
},
});
await server.connect(new StdioServerTransport());
import { Client } from '@modelcontextprotocol/sdk/client/index';
import { instrumentMcpClient } from 'autotel-mcp-instrumentation/client';
import { init } from 'autotel';
init({ service: 'mcp-weather-client', endpoint: 'http://localhost:4318' });
const client = new Client({ name: 'weather-client', version: '1.0.0' });
const instrumented = instrumentMcpClient(client, {
captureArgs: true,
captureResults: false,
});
// _meta is injected with traceparent/tracestate/baggage automatically
const result = await instrumented.callTool('get_weather', { location: 'NYC' });
interface McpInstrumentationConfig {
captureArgs?: boolean; // default true
captureResults?: boolean; // default false (PII risk)
captureErrors?: boolean; // default true
customAttributes?: (ctx: { type, name, args, result }) => Attributes;
}

Server spans:

  • mcp.typetool, resource, prompt
  • mcp.tool.name / mcp.resource.name / mcp.prompt.name
  • mcp.tool.args (when captureArgs: true)
  • mcp.tool.result (when captureResults: true)

Client spans:

  • mcp.client.operationcallTool, getResource, getPrompt
  • mcp.client.name, mcp.client.args, mcp.client.result

Because trace context lives in the JSON payload’s _meta field, the same propagation works across stdio, HTTP, SSE, WebSocket, or any custom transport without any header plumbing:

import {
injectOtelContextToMeta,
extractOtelContextFromMeta,
activateTraceContext,
} from 'autotel-mcp-instrumentation/context';
import { context } from '@opentelemetry/api';
// Client side: inject
const request = {
method: 'tools/call',
params: { name: 'my-tool', arguments: {} },
_meta: injectOtelContextToMeta({}),
};
// Server side: extract and activate
const ctx = activateTraceContext(request._meta);
return context.with(ctx, () => runHandler(request));

customAttributes runs per span. Use it to redact PII or attach business-specific tags:

const instrumented = instrumentMcpServer(server, {
captureArgs: false, // disable raw arg capture
customAttributes: ({ type, name, args, result }) => ({
'tool.location': args?.location, // safe to log
'tenant.id': args?.tenantId,
...(type === 'tool' && name === 'search'
? { 'search.results.count': result?.items?.length ?? 0 }
: {}),
}),
});

MCP is where untrusted data crosses into your agent. The agentic-web threat model (Chrome/Google, June 2026) has two vectors: malicious manifests (hidden instructions in a tool’s name/description/annotations) and contaminated outputs (injection smuggled inside legitimate tool results). Detecting these in production is an observability problem — and this package makes it observable at the MCP boundary.

On by default (no extra config), every tool span carries:

  • Annotation hints → mcp.tool.read_only, mcp.tool.destructive, mcp.tool.idempotent, mcp.tool.open_world, mcp.tool.untrusted_content
  • Payload sizes → mcp.tool.arguments.size, mcp.tool.result.size (sizes only, no content) — a token-exhaustion / contaminated-output tell

Opt-in — a pluggable classifier (Model Armor, Promptfoo, an LLM critic, or the built-in heuristic) plus output budgets:

import {
instrumentMcpServer,
heuristicInjectionClassifier,
MCP_CHAR_BUDGETS,
} from 'autotel-mcp-instrumentation';
instrumentMcpServer(server, {
securityClassifier: heuristicInjectionClassifier(), // or your own (sync/async)
outputCharBudget: MCP_CHAR_BUDGETS.TOOL_OUTPUT, // 1500 (WebMCP recommendation)
});

The classifier scans tool arguments (server + client) and results (the contaminated-output vector the agent receives), recording mcp.security.injection.* and emitting a mcp.security.injection_suspected event on non-clean verdicts. Classifier failures never break the traced call. The built-in heuristic is a cheap tripwire — feed its signal to a real classifier, don’t gate actions on it.

Helpers (autotel-mcp-instrumentation/security, runtime-agnostic — works in Workers/edge):

import { spotlight, validateToolBudget } from 'autotel-mcp-instrumentation/security';
const safe = spotlight(userComment); // <untrusted>…</untrusted> (or { method: 'base64' })
const violations = validateToolBudget({ name, description, parameters }); // WebMCP char limits

Guard bridge — detection → enforcement. Pass an autotel-genai guard and every tool call is recorded as a step; a stop rule throws to halt a runaway run:

import { createGenAiBudget } from 'autotel-genai/guard';
const guard = createGenAiBudget({ maxToolCalls: 50, maxCostUsd: 5 });
instrumentMcpClient(client, { guard }); // duck-typed — no genai dependency added

Signals reference

SignalKindMeaning
mcp.tool.* (hints)span attrtool trust profile / manifest vector
mcp.tool.{arguments,result}.sizespan attrpayload size (token-exhaustion tell)
mcp.security.injection.*span attrclassifier verdict / score / categories
mcp.security.injection_suspectedeventnon-clean classifier verdict
mcp.security.budget_exceededeventoutput over outputCharBudget
mcp.security.eventscounteraggregate security-signal count

Query them in production with autotel security mcp (see the investigate CLI).

ImportContentsSize
autotel-mcp-instrumentationEverything~7KB
autotel-mcp-instrumentation/serverinstrumentMcpServer~5KB
autotel-mcp-instrumentation/clientinstrumentMcpClient~4KB
autotel-mcp-instrumentation/contextinject*/extract*/activate*~2KB
autotel-mcp-instrumentation/securityclassifier, spotlight, budgets~2KB

A standalone MCP server that gives AI agents (Claude Code, Claude Desktop, Cursor, Windsurf, VS Code, Goose) the ability to investigate your OpenTelemetry traces, metrics, and logs. It ships with a built-in OTLP collector on port 4318, so any instrumented app can send data directly. You don’t need Jaeger, Tempo, or Grafana.

  • Backend-agnostic. Built-in OTLP collector accepts data from any OTel-instrumented app.
  • All three signals. Traces, metrics, and logs with cross-signal correlation.
  • Agent-optimised. 33 tools designed for progressive investigation: discover → diagnose → correlate → root-cause.
  • Zero infrastructure. In-memory by default; persistent with --persist.
  • Node.js 20+
  • An MCP-compatible client
{
"mcpServers": {
"autotel": {
"command": "npx",
"args": ["autotel-mcp"]
}
}
}
Terminal window
claude mcp add autotel npx autotel-mcp

Once installed, ask the agent things like “What slowed down checkout in the last hour?” or “Show traces with errors for service.name=api. The agent will discover services, query traces, correlate logs, and return a written investigation.