AgentStack
MCP verified MIT Self-run

Opentelemetry Instrumentation Mcp

mcp-theharithsa-opentelemetry-instrumentation-mcp · by theharithsa

OpenTelemetry Instrumentation for Model Context Protocol (MCP) A TypeScript package that provides automatic OpenTelemetry instrumentation for the Model Context Protocol SDK, enabling observability and telemetry collection for MCP-based applications.

No reviews yet
0 installs
13 views
0.0% view→install

Install

$ agentstack add mcp-theharithsa-opentelemetry-instrumentation-mcp

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Opentelemetry Instrumentation Mcp? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

OpenTelemetry Instrumentation for Model Context Protocol (MCP)

[](https://www.npmjs.com/package/@theharithsa/opentelemetry-instrumentation-mcp) [](https://www.npmjs.com/package/@theharithsa/opentelemetry-instrumentation-mcp) [](https://opensource.org/licenses/ISC) [](https://github.com/theharithsa/opentelemetry-instrumentation-mcp/actions)

Automatic OpenTelemetry instrumentation for the Model Context Protocol SDK, enabling observability and telemetry collection for MCP-based applications with zero configuration required.

Version 1.0.4 - Production ready with enhanced OTEL headers parsing and streamlined configuration.

> 🆕 What's New in v1.0.4: Removed deprecated DYNATRACE_API_TOKEN support for cleaner configuration. Now uses only standard OpenTelemetry OTEL_EXPORTER_OTLP_HEADERS with improved parsing and validation.

Features

  • 🔄 Automatic instrumentation of MCP tool calls
  • 📊 Built-in OpenTelemetry setup with NodeSDK
  • 🚀 Drop-in solution - no manual OpenTelemetry configuration needed
  • 📈 OTLP export with Dynatrace support out of the box
  • 🔍 Comprehensive tracing of tool execution with error handling
  • Zero-code integration - just import and go
  • 🔗 Parent span stitching - maintains trace context across tool executions

Installation

npm install @theharithsa/opentelemetry-instrumentation-mcp

Quick Start

Option 1: Auto-Registration (Recommended)

Simply import the register module at the very top of your application entry point:

// At the top of your src/index.ts or main file
import '@theharithsa/opentelemetry-instrumentation-mcp/register';

// Your existing MCP server code...
import { McpServer } from '@modelcontextprotocol/sdk';
// ... rest of your application

Option 2: Manual Setup

If you need more control over the OpenTelemetry configuration:

import { McpInstrumentation } from '@theharithsa/opentelemetry-instrumentation-mcp';
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';

const sdk = new NodeSDK({
  instrumentations: [
    getNodeAutoInstrumentations(),
    new McpInstrumentation(),
  ],
  // Your custom configuration...
});

sdk.start();

Environment Variables

When using the auto-registration approach, configure these environment variables:

# Required: OTLP endpoint for trace export
OTEL_EXPORTER_OTLP_ENDPOINT=https://your-dynatrace-endpoint.com/api/v2/otlp/v1/traces

# Required: OTLP headers including authorization
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Api-Token 

Header Format

The OTEL_EXPORTER_OTLP_HEADERS environment variable supports comma-separated key=value pairs:

# Single header
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Api-Token your-token-here

# Multiple headers
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Api-Token your-token,Custom-Header=value,Another=header-value

What Gets Instrumented

The instrumentation automatically creates spans for:

  • MCP Tool Calls: Each tool invocation gets its own span named mcp.tool:{toolName}
  • Error Tracking: Exceptions are recorded and spans are marked with error status
  • Execution Context: Full OpenTelemetry context propagation

Parent Span Stitching Solution

The Challenge

By default, MCP tool executions may not maintain proper parent-child span relationships, leading to disconnected traces in complex applications.

The Solution: Tool Wrapper Pattern

Create a custom wrapper function around your tool definitions that establishes parent spans. The McpInstrumentation will automatically create child spans within this context:

import { trace, context, SpanStatusCode, SpanKind } from '@opentelemetry/api';

const tracer = trace.getTracer('your-application', '1.0.0');

const tool = (
  name: string,
  description: string,
  paramsSchema: ZodRawShape,
  cb: (args: z.infer>, _extra?: any) => Promise
) => {
  server.tool(name, description, paramsSchema, async (args, _extra) => {
    return await tracer.startActiveSpan(
      `Tool.${name}`,
      {
        kind: SpanKind.SERVER,
        attributes: {
          'Tool.name': name,
          'Tool.args': JSON.stringify(args),
        },
      },
      async (span) => {
        try {
          const result = await context.with(trace.setSpan(context.active(), span), async () => {
            return await cb(args, _extra);
          });
          
          span.setStatus({ code: SpanStatusCode.OK });
          span.setAttributes({
            'mcp.tool.result.length': result.length,
            'mcp.tool.success': true,
          });
          
          return {
            content: [{ type: 'text', text: result }],
          };
        } catch (error: any) {
          span.recordException(error);
          span.setStatus({
            code: SpanStatusCode.ERROR,
            message: error.message,
          });
          span.setAttributes({
            'mcp.tool.success': false,
            'mcp.tool.error': error.message,
          });
          
          return {
            content: [{ type: 'text', text: `Unexpected error: ${error.message}` }],
            isError: true,
          };
        } finally {
          span.end();
        }
      }
    );
  });
};

How It Works

  1. Parent Span Creation: The wrapper creates a parent span named Tool.${name}
  2. Automatic Child Spans: McpInstrumentation detects the active context and creates child spans mcp.tool:${name}
  3. Context Propagation: All operations within the tool callback inherit the proper trace context
  4. No Manual Span Management: Individual tools don't need to create or manage spans manually

Benefits

  1. Automatic Hierarchy: Parent-child span relationships are established automatically
  2. Zero Manual Work: Individual tools don't need span management code
  3. Consistent Tracing: Every tool gets proper instrumentation without code duplication
  4. Error Handling: Exception recording and span status management is centralized
  5. Context Inheritance: All async operations within tools inherit the correct trace context

Adding Custom Span Attributes

Since the wrapper creates an active span context, you can easily add custom attributes in your tool implementations:

tool('create_workflow', 'Create a workflow', { model: z.string() }, async ({ model }) => {
  // Get the active span (created by the wrapper)
  const span = trace.getSpan(context.active());
  if (span) {
    span.setAttribute('model.name', model);
    span.setAttribute('operation.category', 'workflow');
  }

  // Perform the actual work
  const result = await createWorkflow({ model });
  return `Workflow created: ${result.id}`;
});

Example Trace Output

With the tool wrapper pattern, you'll see properly structured traces:

Tool.get_environment_info (Parent Span - from wrapper)
  ├── mcp.tool:get_environment_info (Child Span - from McpInstrumentation)
  │   ├── Duration: 45ms
  │   ├── Status: OK
  │   └── Attributes: tool execution details
  └── Additional child spans from API calls...

Tool.execute_dql (Parent Span - from wrapper)
  ├── mcp.tool:execute_dql (Child Span - from McpInstrumentation)
  │   ├── Duration: 120ms
  │   ├── Status: ERROR
  │   └── Exception: QuerySyntaxError
  └── Additional context...

Requirements

  • Node.js >= 16.0.0
  • MCP SDK (@modelcontextprotocol/sdk)
  • Environment variables for OTLP export

Package Structure

@theharithsa/opentelemetry-instrumentation-mcp/
├── index.js          # McpInstrumentation class
└── register.js       # Auto-registration with NodeSDK

Migration from Manual Setup

If you were previously setting up OpenTelemetry manually:

  1. Remove your custom OpenTelemetry setup code
  2. Delete manual instrumentation imports
  3. Add the single import line at the top of your entry point:

``typescript import '@theharithsa/opentelemetry-instrumentation-mcp/register'; ``

  1. Set the required environment variables
  2. Implement the tool wrapper pattern for proper span hierarchy
  3. Restart your application

Best Practices

Tool Wrapper Implementation

  • Create the wrapper once and reuse it for all tools
  • Include essential attributes like tool name and arguments
  • Let McpInstrumentation handle the detailed MCP-specific instrumentation
  • Add custom attributes within tool callbacks as needed

Error Handling

  • The wrapper handles top-level errors and span status
  • Individual tools should focus on business logic
  • Thrown exceptions are automatically recorded and propagated

License

ISC

Changelog

See [CHANGELOG.md](./CHANGELOG.md) for detailed version history and breaking changes.

Contributing

Issues and pull requests are welcome on GitHub.

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.