Changelog
Version history for opentel-mcp, current version v0.10.0, sourced from the project's real commit history, README, ADRs, and known-gaps tracker — cross-checked against CHANGELOG.md where it agreed, not reconstructed from memory.
v0.10.0MCP v2 support, hardened server detectionAugust 11, 2026
- Changed:
detectServerKind()now additionally requires anMcpServer-shaped object's.serverproperty to beinstanceofa real, resolvedServerclass from a supported SDK — not merely an object exposing asetRequestHandlermethod. An object that passes the outer shape check but fails that instanceof check now throws atinstrumentMcpServer()call time, instead of succeeding and producing zero telemetry — a plainError, not a named or exported error class (internally tracked asUNWRAPPABLE_MCPSERVER_ERROR, but that identifier is neither exported nor catchable — only the message string is real), so it can't be caught by type or a.codeproperty. No escape hatch was added. Closes known-gaps entry 7. - Added:
@modelcontextprotocol/server(MCP v2, protocol revision 2026-07-28) support — two separate, OPTIONAL peer dependencies (@modelcontextprotocol/sdkfor v1,@modelcontextprotocol/serverfor v2), install whichever one(s) you actually use. SameServer/McpServerAPI shapes as v1; detection and wrapping resolved automatically, once perinstrumentMcpServer()call. Spans, standard attributes (includingjsonrpc.request.id, now read from v2'sctx.mcpReq.id), deep failure fingerprinting, andmcp.failure.channel/mcp.failure.validation_pathsclassification all work the same as v1. Full design: ADR 015. - Fixed:
isSingleConnectionTransport()no longer auto-detects the transportcreateMcpHandlerbuilds internally (PerRequestHTTPServerTransport) as single-connection — it now requires positive confirmation (transport.constructor.name === 'StdioServerTransport') for v2 specifically, closing a confirmed false positive.thrashConnectionFallbackSessionIdis also now registry-backed viainstanceKey, so the fallback session id is shared across repeatedinstrumentMcpServer()calls instead of regenerated fresh on every one. Together, these fix known-gaps entry 8 outright and close entry 6's fallback-id half — entry 6 itself is partially fixed, not closed; a structural limitation survives (see Tracker State Under Stateless HTTP). One narrower thing stays open: the internal flag tracking whether a server has ever proven itself session-aware isn't registry-backed yet — see Tracker State Under Stateless HTTP.
Behavior change on upgrade — read before updating
AnMcpServer-shaped object whose .server isn't a recognized Server instance from either supported SDK now throws — a plain Error, not a named or exported error class, so it can't be caught by type or a .code property — instead of silently instrumenting nothing. If you hit this on an object you believe genuinely is a Server/McpServer, suspect a duplicate or mismatched SDK install (check for multiple resolved copies — npm dedupe — or confirm normal node_modules resolution from wherever opentel-mcp itself is installed, if it's a monorepo/hoisting issue rather than a duplicate). A real Server/McpServer from a single, consistently-resolved SDK install is unaffected.v0.9.0instanceKey, broadened auth classifierAugust 10, 2026
- Fixed: the
authfailure classifier missed "permission denied" and "access denied" — standard Unix/git/AWS IAM/GCP phrasing for a permission failure — recognizing only HTTP-status-derived wording (unauthorized,forbidden,authenticat(e|ion)), 401/403 codes, and known auth-library error names. Now also matches "not authorized", "permission(s) denied", "access denied", "insufficient permission(s)", and Node'sEACCES/EPERM. Does not amend ADR 006's closed 8-category taxonomy —authalready existed; this is pattern coverage for when it fires. - Added:
instanceKey, a string option oninstrumentMcpServer()(or theOTEL_MCP_INSTANCE_KEYenv var) that lets repeatedinstrumentMcpServer()calls sharing the same key share Agent Thrash Detection, budget tracking, schema drift detection, and thetoolOutcomecounter's state, instead of each call constructing all four fresh. Fixes the gap under "stateless" Streamable HTTP deployments (a freshServer/McpServerre-instrumented per request). Backed by a bounded, TTL-evicting registry (1,000 keys/process, 24h TTL renewed on use). Omitting it (the default) is byte-identical to every prior version. Full design: ADR 012. See Tracker State Under Stateless HTTP.
Fingerprint values changed on upgrade for auth-classified messages
Becausecategory is a hashed input to computeFingerprint(), the broadened auth classifier above changes mcp.failure.fingerprint for any message that now classifies as auth instead of internal. If you alert or dashboard on a specific fingerprint value for a permission error, expect a new value after upgrading.v0.8.0Schema drift, observation contract, sampling signalAugust 7, 2026
- Added: tool schema drift detection. Watches every
tools/listresponse, hashes each tool'sinputSchema(neverdescription), and flags changes against the last-observed hash for that tool. Six drift kinds:field_added,field_removed,type_changed,required_changed,multiple,unknown. Newmcp.tool.schema_drift.detectedcounter and matching span event on a newtools/listspan, plus aschemaDriftconfig block (enabled, defaulttrue;maxTrackedTools, default1000), each overridable viaOTEL_MCP_SCHEMA_DRIFT_*env vars. Full design: ADR 010 (docs/adr/010-schema-drift.md). - Added:
getObservationState(), a two-axis observation contract returningtoolOutcome({ success, failure, unknown }) andobservationIntegrity('DEGRADED' | 'UNKNOWN') — detects the case where instrumentation silently no-ops because noTracerProvider/MeterProviderwas ever registered, so a failed tool call in that state doesn't read as indistinguishable from one that never failed. Prompted by external review from Massimiliano Brighindi. Full design: ADR 008 (docs/adr/008-observation-liveness.md, "Update (2026-08-05)" section). - Added:
mcp.tool.thrash_detectedboolean span attribute, set alongside the existingmcp.loop.detectedspan event — gives an OpenTelemetry Collectortailsamplingprocessoran attribute-level signal to key aboolean_attributepolicy on, deliberately named differently from themcp.tool.loop.detectedmetric counter. No new cost-threshold attribute or config —mcp.tool.cost.usd/mcp.tool.cost.budget_exceeded(v0.5.0) already suffice; the threshold itself lives in the Collector policy YAML. Full design: ADR 011 (docs/adr/011-cost-aware-sampling.md).
Behavior change on upgrade — read before updating
BecauseschemaDrift.enabled defaults to true, the existing instrument-first requirement now also covers tools/list. Low-level Server users who call setRequestHandler(ListToolsRequestSchema, ...) before instrumentMcpServer() will now get INSTRUMENT_FIRST_ERROR where they previously did not — no other code change required to hit it. McpServer users are unaffected (it registers tools/list and tools/call together, atomically). Migration: reorder the tools/list registration to after instrumentMcpServer(), or pass schemaDrift: { enabled: false }.v0.7.0Channel-aware thrash thresholdsAugust 3, 2026
- Added:
mcp.failure.channelspan attribute classifying where atools/callfailure originated —execution,protocol.input,protocol.not_found,protocol.output,protocol.other,unknown. Agent Thrash Detection picks a threshold per channel instead of treating every repeat identically. - Added:
thrashDetection.inputThreshold(envOTEL_MCP_THRASH_INPUT_THRESHOLD, default5) — a higher bar forprotocol.input, since an agent retrying with adjusted arguments may be converging on a correct call. - Added:
thrashDetection.notFoundThreshold(envOTEL_MCP_THRASH_NOT_FOUND_THRESHOLD, default1) — an immediate flag; retrying a tool name that doesn't exist is never convergence. - Added: a
classifyFailureChannel()recovery path for the high-levelMcpServer, reading theMCP error {code}:wrapper back out of errorsMcpServeralready converted toisError: truebefore this library ever sees them. - Fixed:
protocol.outputfailures — the tool's own output failing its declared output schema, the server author's bug, unfixable by any argument the agent supplies — are now excluded from Agent Thrash Detection entirely. This was a false positive present in every published version through v0.6.1. - Unchanged: the default
threshold(3) still applies toexecution,protocol.other, andunknown.
Reachability differs by server API
The low-levelServer reaches all six mcp.failure.channel values directly. The high-level McpServer reaches the protocol channels only through the recovery path above, which is coupled to the installed SDK's exact error-message prose and degrades safely to execution (never a wrong specific answer) if that prose changes.v0.6.1TypeScript declaration fixAugust 3, 2026
- Fixed:
src/index.d.tsre-exported values (computeFingerprint,toSpanAttributes,ATTRIBUTE_KEYS,METRIC_SAFE_ATTRIBUTES,DEFAULT_CLASSIFIERS,DEFAULT_PRICING,defaultExtractor,calculateCost) from six.jsmodules that had no corresponding.d.tsfile, so any consumer withstrict/noImplicitAnygot aTS7016error just from importing the package. Pre-existing since v0.4.0 (fingerprinting) and v0.5.0 (cost tracking) — first caught verifying the v0.6.0 published tarball.
v0.5.0Cost & Token AttributionJuly 29, 2026
- Major feature: automatic LLM cost and token attribution on MCP tool calls that wrap model calls. See Cost & Token Attribution.
costTrackingoption (default enabled) oninstrumentMcpServer()— recognizes Anthropic, OpenAI, and Bedrock usage field-name conventions, the MCP_meta.usageextension point, and JSON-in-text insidecontent[0].text. Never throws; unrecognized shapes resolve tonull.- New span attributes:
mcp.tool.tokens.input,mcp.tool.tokens.output,mcp.tool.tokens.total,mcp.tool.model,mcp.tool.cost.usd, andmcp.tool.cost.currency, plusgen_ai.response.modelco-emitted alongsidemcp.tool.modelfor GenAI semantic-convention dashboard compatibility. - Two new metric instruments, same
@opentelemetry/api-only pattern as the four existingmcp.tool.*metrics:mcp.tool.tokens.total(counter, unittokens) andmcp.tool.cost.total(counter, unitUSD). - Budget guardrails (
costTracking.budget:perSessionUsd,perToolUsd) — observability flags, not enforcement. Setsmcp.tool.cost.budget_exceeded/mcp.tool.cost.budget_scopeon the span once a configured limit is crossed; never blocks or throws. - New exports:
DEFAULT_PRICING(15+ models across five providers — Anthropic, OpenAI, Google, AWS Bedrock, DeepSeek),defaultExtractor,calculateCost. New types:CostTrackingOptions,TokenUsage,UsageExtractor,ModelPricing,PricingTable,BudgetConfig.
Pricing accuracy and budget scope
DEFAULT_PRICING is a convenience default, last verified 2026-07-29 — not a maintained price list. Provider pricing changes frequently; production users must override costTracking.pricingTable. Budget tracking is in-memory and per instrumentMcpServer() call — it resets on process restart, and session-scoped limits are skipped gracefully (not enforced against a fallback key) for transports with no session id, like stdio.v0.4.0Deep Failure FingerprintingJuly 24, 2026
- Adds
computeFingerprint()— a stable, 16-hex-character identity (SHA-256, truncated) for every thrown error and every tool-level failure (isError: true), so repeated occurrences of the same underlying bug group under one fingerprint. See Deep Failure Fingerprinting. - Five new
mcp.failure.*span attributes: fingerprint, signature, category (8 values), origin, and error class. METRIC_SAFE_ATTRIBUTES— a frozen array structurally limiting which fingerprint-derived values can reach a metric label tocategoryandorigin(24 combinations maximum), so nothing unbounded can leak into a metrics backend.fingerprintingoption (defaulttrue) oninstrumentMcpServer().computeFingerprint,toSpanAttributes,ATTRIBUTE_KEYS,METRIC_SAFE_ATTRIBUTES, andDEFAULT_CLASSIFIERSre-exported from the package root.
Type-declaration gap in v0.4.0 — since resolved
Thefingerprinting option was implemented and defaulted to true in src/config.js and src/instrument.js, but wasn't listed in the InstrumentOptions TypeScript interface in src/index.d.ts as of this version — it worked at runtime, but TypeScript consumers could see a type error passing { fingerprinting: false } anyway. Resolved in v0.6.0: fingerprinting?: boolean landed on InstrumentOptions. A narrower, related gap is still not resolved as of v0.10.0: computeFingerprint()'s classifiers/stackFrames options remain unwired through instrumentMcpServer()'s own options — no target version has been set for that part. See API Reference.v0.3.0MetricsJuly 20, 2026
- Four
mcp.tool.*OpenTelemetry metrics instruments via@opentelemetry/api's Metrics API — no bundled SDK or exporter, same host-app-provides-the-SDK pattern tracing already used:mcp.tool.calls(counter, every call),mcp.tool.errors(counter, thrown/rejected handlers),mcp.tool.silent_failures(counter,isError: true), andmcp.tool.duration(histogram, ms). See Metrics. mcp.tool.silent_failuresfires from the sameisErrorcheck that marks the spanERROR— extracted into one sharedisToolResultError()helper so the detection logic isn't duplicated between traces and metrics.enableMetricsoption (defaulttrue) to opt out of metric emission without affecting tracing.- Metrics are a zero-overhead no-op until the host application registers a
MeterProvider— default@opentelemetry/apibehavior, not special-cased. - README: new Metrics section with an instrument table and a SigNoz OTLP/HTTP wiring example.
v0.1.1Patch releaseJuly 12, 2026
- Fix:
serviceNamebecame optional — required (a non-empty string) only whensetupNodeSdk: true, throwing if missing in that mode; no effect otherwise, with a once-per-processdiag.warnif passed anyway. - Build: removed the published
workspacesarray frompackage.json; addedprepack/postpackhooks (strip-workspaces.js/restore-workspaces.js) to strip it before publish and restore it after. - Docs: README gained an ESM requirement note ("type": "module" or .mjs), an npm version badge, an Install section, and a 0.x semantic-conventions stability note.
v0.2.0TypeScript declarations and semantic-convention alignmentJuly 13, 2026
- Hand-written TypeScript declarations (.d.ts) so TypeScript consumers get accurate types without a build step.
- Span attributes realigned to the MCP semantic conventions:
mcp.tool.namerenamed togen_ai.tool.name,mcp.request.idrenamed tojsonrpc.request.id,mcp.tool.error.typerenamed toerror.type, and span name changed to{mcp.method.name} {tool}, kindSERVER. - Docs: ADR 004 documents the semantic-conventions alignment.
v0.1.0Initial releaseJuly 9, 2026
instrumentMcpServer()— supports both the low-levelServerand high-levelMcpServerAPIs from@modelcontextprotocol/sdk.- One OpenTelemetry span per tool invocation, marked
ERRORon a thrown exception or onCallToolResult.isError: true— the silent-failure detection this package exists for from the first release. See Silent Failures. - Respects an existing global
TracerProvider; optional zero-configsetupNodeSdkdev mode, exporting to stderr so stdio-transport JSON-RPC isn't corrupted. - Privacy by design: argument counts captured, never argument values.
- Cross-platform (Windows/macOS/Linux), pure JavaScript, zero native dependencies.
- 25 tests, 3 ADRs, 2 working examples at initial release.
Where do I go from here?
- Cost & Token Attribution — the full v0.5.0 feature.
- Deep Failure Fingerprinting — the full v0.4.0 feature.
- API Reference — current exported surface.