OpenTelemetry semantic conventions

mcpgw v1.x emits OpenTelemetry SemConv schema URL https://opentelemetry.io/schemas/1.26.0 and the experimental mcp.* namespace defined here. Mappings to OTel GenAI conventions track upstream stabilization; expect breaking attribute name changes only across major versions.

This page is the reference. For setup, see How-to: enable Datadog tracing. For the wire transport, see Configuration: telemetry.


Span names

Spans are named mcp.<method> with slashes replaced by dots.

JSON-RPC methodSpan name
initializemcp.initialize
tools/listmcp.tools.list
tools/callmcp.tools.call
resources/listmcp.resources.list
resources/readmcp.resources.read
prompts/listmcp.prompts.list
pingmcp.ping

The pattern is mechanical: replace / with . and prepend mcp..

Span kind

DirectionKind
Inbound request from MCP clientSERVER
Outbound forward to upstream MCP serverCLIENT

Each request produces one SERVER span (the gateway-side) and, if the request reached the upstream, one CLIENT child span.


Attributes

Set on every span unless noted otherwise.

AttributeTypeExampleNotes
mcp.methodstringtools/callOriginal JSON-RPC method, with the slash.
mcp.tool.namestringfs_readEmpty string for non-tools/call methods.
mcp.session.idstring01HXYZ...From the Mcp-Session-Id header; empty if absent.
mcp.transportstringhttp+sseSee enum below.
mcp.upstreamstringfilesystemLogical name from config. Never the URL.
mcp.policy.decisionstringallowSee enum below.
mcp.policy.rule_idstringdeny-shellSet only when a rule fired.
mcp.auth.key_idstringclaude-desktop-prodSet when auth.enabled and authentication succeeds via the API-key path.
mcp.auth.resultstringokSet when auth.enabled. See mcp.auth.result enum below.
mcp.auth.oauth_client_idstringmy-agentOAuth client_id (or azp fallback) of the verified Bearer token. Set on OAuth path only. Constant: telemetry.AttrOAuthClientID.
mcp.auth.scopesstringmcp:read mcp:writeSpace-separated scopes from the verified token. Set on OAuth path only. See audit-log.md for the same field on the audit envelope. Constant: telemetry.AttrAuthScopes.
mcp.auth.oauth_client_namestringClaude DesktopHuman-readable client name resolved from CIMD document. Set on OAuth path when CIMD is enabled and the fetch succeeded. Constant: telemetry.AttrOAuthClientName.
mcp.payload.bytes_inint312Length of the incoming JSON-RPC request body.
mcp.payload.bytes_outint1024Length of the response body. Success spans only.
mcp.error.codeint-32001Error spans only. JSON-RPC error code.
mcp.error.kindstringpolicy_denyError spans only. See enum below.
mcp.license.gracebooltrueSet only when the gateway is operating in the license grace window.
mcp.request.idstringreq_01HXYZ...Stable correlation id; matches the X-MCPGW-Request-ID response header and the audit log request_id.
mcp.synthesizedstringtools_list, tools_call_searchSet on synthesized responses to indicate which path produced them. tools_list for intercepted tools/list responses; tools_call_search for intercepted tools/call mcp_search responses. Only present when tool_search.mode: synthesize is active. Constant: telemetry.AttrSynthesized.
mcp.toolsearch.hitsint5Number of tools returned by a mcp_search call. Set on tools_call_search synthesized spans. Constant: telemetry.AttrToolSearchHits.
mcp.frames.totalint5Total SSE frames seen by the inspector (envelopes + opaque). Use mcp.frames.denied and mcp.frames.redacted to compute the policy-evaluated subset. Set on the proxy span for SSE responses. Constant: telemetry.AttrFramesTotal.
mcp.frames.deniedint1Number of SSE frames dropped by deny or rate_limit rules. Subset of mcp.frames.total. Constant: telemetry.AttrFramesDenied.
mcp.frames.redactedint2Number of SSE frames whose data: bytes were rewritten by redact rules. Subset of mcp.frames.total. Constant: telemetry.AttrFramesRedacted.
mcp.directionstringserver_to_clientDirection of the policy evaluation path. Set to "server_to_client" on the proxy span when the upstream response was an SSE stream. Also present on per-frame SSE audit lines. See enum below. Constant: telemetry.AttrDirection.
mcp.app.servedint3Number of UI blocks forwarded unmodified to the client. Set on tools/call spans when the upstream response contained at least one UI block and no strip_app rule removed it. 0 or absent means no UI blocks were present or all were stripped. Constant: telemetry.AttrAppServed.
mcp.app.strippedint2Number of UI blocks removed by a strip_app action. Set on tools/call spans when a strip_app rule fired. 0 means the rule matched but found no UI blocks to remove. Absent when no strip_app rule fired. Constant: telemetry.AttrAppStripped.

Enum values

mcp.transport

ValueDescription
http+sseHTTP request with SSE streaming response
sseServer-Sent Events transport
stdio-bridgeRequest arrived via the mcpgw stdio bridge
httpPlain HTTP request, no SSE

mcp.policy.decision

ValueCondition
allowNo rule fired, or a matching rule had action: allow
denyA rule with action: deny matched
redactA rule with action: redact matched and the payload was modified
rate_limit_blockedA rate-limit rule matched and the bucket was empty
strip_appA strip_app rule matched; UI blocks (if any) were removed from the response

mcp.error.kind

ValueCondition
parseJSON-RPC request could not be parsed
policy_denyA policy rule with action: deny matched
rate_limitedA rate_limit rule blocked the call
body_too_largePre-parse rejection at 16 MiB cap
license_invalidLicense missing, expired beyond grace, or signature invalid
upstream_timeoutUpstream did not respond within the deadline
upstream_unreachableTCP connection to upstream failed
upstream_protocolUpstream returned non-MCP content
internalUnhandled gateway error

mcp.direction

ValueDescription
client_to_serverPolicy evaluated on an inbound request from a client.
server_to_clientPolicy evaluated on an SSE frame from an upstream server.

mcp.auth.result

ValueCondition
okRequest authenticated successfully (API-key or OAuth path)
missingNo configured auth header was present
invalidHeader was present but did not match a configured key (API-key path), or JWT signature/format was rejected
expiredHeader matched a configured key whose expires_at is in the past (API-key path)
bad_audienceJWT aud claim did not match auth.oauth.audience
bad_issuerJWT iss claim did not match auth.oauth.issuer
insufficient_scopeJWT was valid but lacked a scope listed in auth.oauth.required_scopes

Resource attributes

Set on the OTel Resource (every span carries them).

AttributeSourceExample
service.nametelemetry.customer.service_namemcpgw
service.versiongateway version1.0.0
telemetry.sdk.nameOTelopentelemetry
telemetry.sdk.languageOTelgo
User-definedtelemetry.customer.resource_attrsenv=production, team=platform

See How-to: enable Datadog tracing for ready-to-build dashboard templates.


Notes on stability

  • mcp.upstream is always the logical name from upstreams[].name in mcpgw.yaml, not the URL. This keeps spans clean and avoids leaking internal addresses into telemetry.
  • mcp.tool.name is set on every span regardless of method; it is an empty string for methods other than tools/call. This keeps Datadog facet cardinality predictable.
  • mcp.policy.rule_id is only present when a rule fires. Absence means no rule matched (decision is allow).
  • The mcp.* namespace is experimental and may evolve. Breaking changes are reserved for major versions only.