Skip to Content

How do I set the tool call attribute on an OpenTelemetry span in JavaScript?

Rius reads the tool call id from the gen_ai.tool.call.id attribute on an execute_tool span, which you set in JavaScript with span.setAttributes from @opentelemetry/api. Set it with the tool’s name in gen_ai.tool.name and the span shows in Rius as a tool call. The tool’s arguments and result render as the span’s input and output.

How it works

A tool call in Rius is a span whose operation is execute_tool. These attributes describe it:

  • gen_ai.tool.call.id: the id of the model’s tool call that this execution answers. Copy it from the tool_call part in the model’s output messages.
  • gen_ai.tool.name: the tool’s own name, never the span name.
  • gen_ai.tool.type: function, extension, datastore and so on.
  • gen_ai.operation.name set to execute_tool, or openinference.span.kind set to TOOL. Set either one and Rius derives the other. Setting both is clearest.
  • gen_ai.tool.call.arguments and gen_ai.tool.call.result: what the tool was called with and what it returned.

Name the span the way the GenAI conventions ask, the operation followed by what it acted on, for example execute_tool get_weather. Rius does not require it, but it is what the SDKs emit, so mixed fleets read alike.

For a tool called over MCP, also set mcp.method.name to tools/call. Rius uses it to tell MCP tools from local ones when it attributes context tokens in a trace. The full attribute tables are in the span attribute reference.

Set it up

  1. Register a tracer provider that exports to Rius. The vanilla OpenTelemetry SDKs page covers it in full. In Node.js, the setup is NodeSDK with the protobuf exporter, configured by the standard exporter variables:

    export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.eu.console.rius-glassflow.com" export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf" export OTEL_EXPORTER_OTLP_HEADERS="authorization=Bearer ri_..." export OTEL_SERVICE_NAME="my-agent"
    import { NodeSDK } from '@opentelemetry/sdk-node' import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto' const sdk = new NodeSDK({ // No url or headers in code, so the exporter reads OTEL_EXPORTER_OTLP_* traceExporter: new OTLPTraceExporter(), // Rius ingests traces only metricReaders: [], logRecordProcessors: [], }) sdk.start() // ... your agent code ... // Flushes queued spans before the process exits await sdk.shutdown()
  2. Start a span around each tool execution and set the tool attributes. Here toolCall is the tool call object your own code received from the model, and getWeather is your own tool function:

    import { trace } from '@opentelemetry/api' const tracer = trace.getTracer('my-agent') const span = tracer.startSpan('execute_tool get_weather') span.setAttributes({ 'openinference.span.kind': 'TOOL', 'gen_ai.operation.name': 'execute_tool', 'gen_ai.tool.name': 'get_weather', 'gen_ai.tool.type': 'function', 'gen_ai.tool.call.id': toolCall.id, 'gen_ai.tool.call.arguments': JSON.stringify({ city: 'Paris' }), }) const result = await getWeather('Paris') span.setAttribute('gen_ai.tool.call.result', JSON.stringify(result)) span.end()
  3. Nest the span under your agent run the normal OpenTelemetry way, with parent context, so the tool call arrives in the same trace as the model call that requested it. If the tool fails, set error.type to a low-cardinality class such as tool_error on the span.

For how the exporter and attribute limit work, see Trace LLM calls with OpenTelemetry.

What you get

  • The span is classified as a tool call in trace views and per-kind analytics, because openinference.span.kind is TOOL and the operation is execute_tool.
  • Rius moves gen_ai.tool.call.arguments and gen_ai.tool.call.result to input.value and output.value, so the tool’s input and output render like any other span’s. Search and filter on input.value and output.value, because the source keys are removed.
  • The call id links the execution to the model’s tool call it answers.
  • Because the span is a tool span, it is not counted as an LLM call.
  • With mcp.method.name set on MCP tool spans, a trace’s context attribution can report the MCP tools’ share of context tokens.
  • Rius computes cost on model spans from the model and token counts, so you never send a cost attribute on a tool span.

When something else fits

If you write in TypeScript or Python and want integrations that record tool calls for you, the Rius SDKs add auto-instrumentation and message normalization on top of the same attributes. Code written against raw OpenTelemetry keeps working unchanged if you adopt them later. For a language other than JavaScript, set the same keys with that language’s OpenTelemetry API.

Still stuck? Get help

Last updated on