Skip to main content
The this.telemetry API is available on all execution contexts (ToolContext, ResourceContext, PromptContext, AgentContext) when observability is enabled. It provides a simple interface for creating custom spans, recording events, and setting attributes — with automatic trace context propagation.
Requires @frontmcp/observability installed and observability config enabled. See the Observability Guide for setup.

TelemetryAccessor

Available as this.telemetry in all execution contexts. One instance per request (context-scoped). Automatically inherits the current request’s trace ID, session ID, and scope.

startSpan(name, attributes?)

Create a child span under the current execution span.
startSpan(name: string, attributes?: Record<string, string | number | boolean>): TelemetrySpan
You must call span.end() or span.endWithError() when done.
const span = this.telemetry.startSpan('fetch-user', { 'user.id': userId });
try {
  const user = await this.fetch(`/api/users/${userId}`);
  span.setAttribute('user.name', user.name);
  span.end();
} catch (err) {
  span.endWithError(err);
  throw err;
}

withSpan(name, fn, attributes?)

Run a function within a span. The span is automatically ended on success or error.
withSpan<T>(
  name: string,
  fn: (span: TelemetrySpan) => Promise<T>,
  attributes?: Record<string, string | number | boolean>,
): Promise<T>
const data = await this.telemetry.withSpan('query-database', async (span) => {
  span.addEvent('query-sent');
  const results = await db.query(sql);
  span.setAttribute('rows', results.length);
  return results;
});

addEvent(name, attributes?)

Add an event to the active flow execution span (e.g., the tool span during execute()).
addEvent(name: string, attributes?: Record<string, string | number | boolean>): void
Events are lightweight markers that appear on the parent span’s timeline. Use them for milestones rather than creating child spans.
this.telemetry.addEvent('cache-checked', { hit: false });
this.telemetry.addEvent('validation-complete', { fields: 5 });
Events go on the active execution span, not a new span. If called during tool.execute(), they appear on the "tool my_tool" span. If called outside an execution context, a short-lived child span is created as a fallback.

setAttributes(attrs)

Set attributes on the active flow execution span.
setAttributes(attrs: Record<string, string | number | boolean>): void
this.telemetry.setAttributes({
  'user.tier': 'premium',
  'request.size': body.length,
  'cache.hit': true,
});

traceId

Get the current request’s trace ID. Useful for including in external API calls or logs.
get traceId(): string
const response = await this.fetch('/api/data', {
  headers: { 'X-Trace-Id': this.telemetry.traceId },
});

sessionId

Get the privacy-safe session tracing ID (16-char SHA-256 hash).
get sessionId(): string

TelemetrySpan

Returned by startSpan() and passed to withSpan() callbacks. All setter methods return this for chaining.

Methods

MethodSignatureDescription
setAttribute(key: string, value: string | number | boolean) => thisSet a single attribute
setAttributes(attrs: Record<string, string | number | boolean>) => thisSet multiple attributes
addEvent(name: string, attributes?: Record<...>) => thisAdd a named event
recordError(error: Error) => thisRecord an exception on the span
end() => voidEnd the span with OK status
endWithError(error: Error | string) => voidEnd with ERROR status
rawSpan (getter)Access the underlying OTel Span
const span = this.telemetry.startSpan('process');
span
  .setAttribute('input.size', 1024)
  .addEvent('step-1-done')
  .addEvent('step-2-done', { items: 42 });

if (failed) {
  span.recordError(new Error('processing failed'));
}

span.end(); // Preserves ERROR status if recordError was called

Testing Utilities

Import from @frontmcp/observability:
import {
  createTestTracer,
  getFinishedSpans,
  assertSpanExists,
  assertSpanAttribute,
  findSpan,
  findSpansByAttribute,
} from '@frontmcp/observability';

createTestTracer(name?)

Create an isolated test tracer with in-memory span exporter. Does not register globally — safe for parallel tests.
function createTestTracer(name?: string): {
  tracer: Tracer;
  exporter: InMemorySpanExporter;
  provider: BasicTracerProvider;
  cleanup: () => Promise<void>;
}
const { tracer, exporter, cleanup } = createTestTracer();

afterEach(() => exporter.reset());
afterAll(() => cleanup());

assertSpanExists(spans, name)

Assert that a span with the given name exists. Returns the span or throws.
function assertSpanExists(spans: ReadableSpan[], name: string): ReadableSpan
const spans = getFinishedSpans(exporter);
const toolSpan = assertSpanExists(spans, 'tool get_weather');

assertSpanAttribute(span, key, value)

Assert a span has a specific attribute value.
function assertSpanAttribute(span: ReadableSpan, key: string, value: string | number | boolean): void

findSpan(spans, name) / findSpansByAttribute(spans, key, value)

Query helpers for finding spans in test assertions.
const dbSpan = findSpan(spans, 'database-query');
const toolSpans = findSpansByAttribute(spans, 'mcp.component.type', 'tool');

Configuration Types

ObservabilityOptionsInterface

The config object for @FrontMcp({ observability: { ... } }):
PropertyTypeDefaultDescription
tracingboolean | TracingOptionstrueEnable/configure OTel tracing
loggingboolean | LoggingOptionsfalseEnable/configure structured logging
requestLogsboolean | RequestLogOptionsfalseEnable/configure per-request log collection

TracingOptions

PropertyTypeDefaultDescription
httpSpansbooleantrueHTTP request spans
executionSpansbooleantrueTool/resource/prompt/agent spans
fetchSpansbooleantrueOutbound ctx.fetch() spans
flowStageEventsbooleantrueFlow stage events on execution spans
hookSpansbooleanfalseIndividual hook spans (verbose)
transportSpansbooleantrueSSE/HTTP transport spans
authSpansbooleantrueAuth/session verify spans
oauthSpansbooleantrueOAuth flow spans
elicitationSpansbooleantrueElicitation request/result spans
startupReportbooleantrueEmit startup telemetry on first request

SinkConfig

TypeDescriptionPlatform
{ type: 'stdout' }NDJSON to stdout (12-factor)Node.js
{ type: 'console' }console.log/warn/errorBrowser, Node.js
{ type: 'otlp', endpoint, headers? }OTLP HTTP to any backendNode.js
{ type: 'winston', logger }Forward to winston instanceNode.js
{ type: 'pino', logger }Forward to pino instanceNode.js
{ type: 'callback', fn }Custom callback functionAny

Observability Guide

Step-by-step setup with vendor integrations

Observability Feature

Overview of what FrontMCP observability provides