Skip to main content

OpenTelemetry's GenAI Semantic Conventions: What Vendor-Neutral Agent Spans Buy Your Own Agent-Ops Telemetry

8 min readDora NodaDora Noda
Share

Here is a trace span your observability backend can now parse without a single line of custom code:

text
gen_ai.operation.name = "invoke_agent"
gen_ai.agent.name     = "release-manager"
gen_ai.tool.name      = "deploy"
mcp.method.name       = "tools/call"
gen_ai.usage.input_tokens  = 1204
gen_ai.usage.output_tokens = 86

Two years ago that span didn't exist in any standard form. Every agent framework logged its own shape of JSON, every vendor built its own parser for it, and "tracing an AI agent" meant picking a proprietary SDK and hoping it survived a framework migration. As of 2026, OpenTelemetry's GenAI semantic conventions give those six fields fixed names, fixed types, and a fixed place in a span tree — and Datadog, Grafana, Honeycomb, and every other OTel-speaking backend read them the same way. For a self-hosted PaaS whose whole pitch is "AI agents operate this like a human would," that standard is the missing half of an observability story that, until now, only covered the app the agent deployed — never the agent itself.


Where the spec actually stands in mid-2026

The GenAI Special Interest Group has been working on this since April 2024, and as of OpenTelemetry Semantic Conventions 1.40 the GenAI and MCP sections are still formally labeled Development, not Stable. That label undersells how usable it already is. The core client-call attributes — gen_ai.provider.name, gen_ai.request.model, gen_ai.usage.input_tokens, gen_ai.usage.output_tokens — stabilized in practice by late 2025 and haven't shifted since. Agent and tool spans are newer and still marked experimental, but they've held stable through Q1 2026 in every framework that emits them.

Adoption moved fast once the shape settled:

LayerStatus as of mid-2026
Client/LLM-call spans (gen_ai.request.*, gen_ai.usage.*)Practically stable since late 2025
Agent spans (gen_ai.agent.*)Experimental, stable in practice through Q1 2026
Tool-call spans (gen_ai.tool.*)Experimental, stable in practice through Q1 2026
MCP spans (mcp.*)Newer (introduced v1.39), tooling support still catching up
Backend supportDatadog (native since OTel v1.37), Grafana (Loki ingesting LLM traces), Honeycomb, New Relic, OpenObserve
Framework emittersLangChain, CrewAI, AutoGen, AG2 emit natively or via first-party instrumentation

That's a standard maturing the way most successful OTel conventions do: the spec stays "Development" for a long time after the field names that matter have already stopped moving.

The vocabulary: four span types, one hierarchy

The convention doesn't just name individual fields — it defines a span hierarchy that mirrors how an agent actually works: an agent span wraps one or more LLM-call spans and tool-call spans, and a tool-call span that goes over MCP wraps an mcp.* span underneath it.

AttributeMeaningExample value
gen_ai.operation.nameWhat kind of GenAI operation this span representsinvoke_agent, chat, execute_tool
gen_ai.agent.name / gen_ai.agent.idWhich agent is runningrelease-manager
gen_ai.provider.nameModel provider (replaces the older gen_ai.system)anthropic, aws.bedrock
gen_ai.request.modelModel invokedclaude-sonnet-5
gen_ai.usage.input_tokens / gen_ai.usage.output_tokensPrompt and completion token counts1204 / 86
gen_ai.tool.nameWhich tool the agent calleddeploy
gen_ai.tool.call.arguments / gen_ai.tool.call.resultTool call payload and return value(capture only under an explicit retention policy — see caveats)
mcp.method.nameJSON-RPC method on the MCP wiretools/call
mcp.session.idMCP session correlating the callsess_8f2a…
network.transport / network.protocol.nameTransport metadata for the MCP hoptcp / http

Note what's conspicuously absent: there is no gen_ai.usage.cost field. The spec standardizes token counts, not dollars — cost is something you derive downstream by multiplying gen_ai.usage.input_tokens / output_tokens against whatever price table you attach to gen_ai.provider.name + gen_ai.request.model. If you were hoping the convention hands you a cost line for free, it doesn't; it hands you the two numbers you need to compute one consistently, which is the more useful guarantee anyway — pricing changes constantly, span schemas shouldn't have to.

A worked example: instrumenting an MCP deploy tool

Here's what this looks like wired into the kind of tool a self-hosted PaaS actually ships: an MCP server exposing deploy/rollback/logs/scale so an agent can operate a running service through chat instead of a CLI.

Step 1 — turn on the current experimental schema. Until the spec stabilizes, exporters gate the newer attribute names behind an environment variable so you don't silently break dashboards mid-transition:

bash
export OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental

That single flag is what makes the gen_ai.systemgen_ai.provider.name rename (and the next one after it) a non-event instead of a dashboard-breaking surprise: it dual-emits both the legacy and current attribute names for as long as you need the overlap.

Step 2 — point the SDK's OTel exporter at a self-hosted Collector. No SaaS dependency required — an agent framework's OTel instrumentation talks OTLP, so it exports to whatever Collector endpoint you already run. A minimal pipeline that fans the agent's spans out to a self-hosted Tempo instance needs nothing GenAI-specific — it's the same receiver/exporter shape every other service on the platform already uses:

yaml
receivers:
  otlp:
    protocols:
      grpc:
      http:
 
exporters:
  otlp/tempo:
    endpoint: tempo.internal:4317
    tls:
      insecure: true
 
service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [otlp/tempo]

That's the whole integration surface. The Collector doesn't need to know gen_ai.* attributes exist — it forwards spans, and the backend renders whatever attributes arrive on them. The standardization work happens entirely on the emitting side (the agent framework's instrumentation), which is the point: a self-hosted PaaS doesn't have to build or maintain a GenAI-aware ingestion layer to get GenAI-aware traces.

Step 3 — the resulting trace. An operator asks an agent to "redeploy the API with the new image tag." The agent calls the platform's deploy MCP tool. The span tree that lands in your Collector looks like this:

text
Span: invoke_agent (agent-ops root span)
  gen_ai.operation.name = "invoke_agent"
  gen_ai.agent.name     = "release-manager"
  gen_ai.provider.name  = "anthropic"
  gen_ai.request.model  = "claude-sonnet-5"
  gen_ai.usage.input_tokens  = 1204
  gen_ai.usage.output_tokens = 86

  └─ Span: execute_tool
       gen_ai.operation.name = "execute_tool"
       gen_ai.tool.name      = "deploy"

       └─ Span: mcp.tools/call
            mcp.method.name    = "tools/call"
            mcp.session.id     = "sess_8f2a…"
            network.transport  = "tcp"
            network.protocol.name = "http"

            └─ Span: deploy.rollout   (the platform's own infra span — unchanged)
                 service.name = "api"
                 image.tag    = "v1.4.2"

The bottom span — deploy.rollout — is whatever infrastructure span your platform already emits for a normal rollout, no different from a kubectl apply triggered by a human. Nothing about the deploy pipeline changes. What changes is that it now has three GenAI-convention spans stacked above it, so a single trace answers "which agent triggered this, on whose behalf, using what model, at what token cost, calling which tool" — correlated with the exact same infra span an on-call engineer already knows how to read.

Why this specifically matters for agent-ops, not just app observability

Self-hosted platforms have generally solved observability for the tenant workload — logs, metrics, and traces for the app running on the platform are table stakes. What's been missing is the same rigor applied to the agent operating the platform. Before GenAI semconv existed, that gap got filled with a bespoke logging format: a JSON blob per tool call, shaped however the platform's engineers felt like shaping it, parseable only by a dashboard built specifically for that shape.

Vendor-neutral spans change the economics of that choice. Instrument once against the standard attribute names, and the same trace renders correctly in Datadog, Grafana Tempo, Honeycomb, or whatever an operator's team already standardized on — without re-instrumenting for each one, and without locking a platform's agent-ops story to a single vendor's proprietary tracing SDK. That's the same argument that justified adopting OpenTelemetry for application tracing a decade ago, applied to the newer half of the stack: "trace the agent, not just the app it deployed" is table stakes now, not a nice-to-have, because the agent is the thing making changes to production.

Three caveats before you wire this in

Attribute names can still move. "Development" status means the SIG can still rename or restructure fields — gen_ai.systemgen_ai.provider.name already happened once. Keep OTEL_SEMCONV_STABILITY_OPT_IN set and budget for one more rename before the spec locks.

gen_ai.tool.call.arguments and gen_ai.tool.call.result can leak secrets. A deploy tool's arguments might carry an image tag and a target environment; a rollback tool's might carry a previous release's config. Capture these two attributes only under a retention policy that treats trace storage the same way you'd treat application logs — not by default.

MCP-specific spans lag client spans. mcp.* attributes only landed in v1.39, and instrumentation coverage for the MCP hop specifically is younger than the LLM-call and agent spans that have been stable since late 2025. Expect the MCP layer of the vocabulary to be the part that still shifts in the next couple of spec releases.


Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, with deploy/rollback/logs/scale exposed as MCP tools an agent can call directly. Wiring those tools to emit GenAI-convention spans into a self-hosted OTel Collector is exactly the kind of agent-ops telemetry a platform built for AI operators should ship by default. Star the repo on GitHub and see it running end to end.

Related articles

Give your agents a chain backend

Autonomous agents hit RPC endpoints very differently than people do. See what bex router handles on their behalf.

Read the agents guide