Skip to main content
Observatory provides automatic instrumentation for Anthropic’s Claude Agent SDK, enabling comprehensive observability for Claude-powered agents without modifying your core agent logic.

Overview

The @contextcompany/claude package wraps your Claude Agent SDK instance to automatically capture:
  • Agent queries and responses
  • Streaming messages and events
  • Tool calls and results
  • Run metadata and session tracking
  • Errors and exceptions

Installation

1

Install the package

npm install @contextcompany/claude
This package requires @anthropic-ai/claude-agent-sdk as a peer dependency.
2

Set your API key

Add your Observatory API key to your environment variables:
.env
TCC_API_KEY=your_api_key_here
3

Wrap your agent

Wrap your Claude Agent SDK instance with the instrumentation:
import { Claude } from '@anthropic-ai/claude-agent-sdk';
import { instrumentClaudeAgent } from '@contextcompany/claude';

const claude = new Claude({ apiKey: process.env.ANTHROPIC_API_KEY });
const agent = instrumentClaudeAgent(claude);

// Use agent as normal
const stream = agent.query({ prompt: 'Hello, Claude!' });

Basic Usage

Once wrapped, use your agent exactly as you would normally. The instrumentation is transparent:
import { Claude } from '@anthropic-ai/claude-agent-sdk';
import { instrumentClaudeAgent } from '@contextcompany/claude';

const claude = new Claude({ apiKey: process.env.ANTHROPIC_API_KEY });
const agent = instrumentClaudeAgent(claude);

// Basic query
const stream = agent.query({
  prompt: 'What is the capital of France?',
});

for await (const message of stream) {
  if (message.type === 'text') {
    console.log(message.text);
  }
}
Observatory automatically captures:
  • The user prompt
  • All streaming messages
  • The complete response
  • Timing information

Tracking Runs and Sessions

You can track runs and sessions by passing metadata through the tcc parameter:
const stream = agent.query({
  prompt: 'Tell me about the weather',
  tcc: {
    runId: 'run_abc123',           // Optional: custom run ID
    sessionId: 'session_xyz',      // Optional: session ID for conversational context
    metadata: {                     // Optional: custom metadata
      userId: 'user_456',
      source: 'web-app',
    },
  },
});

for await (const message of stream) {
  // Handle messages
}
If you don’t provide a runId, Observatory will automatically generate one using UUID v4.

Run IDs

Run IDs uniquely identify a single agent execution:
const runId = crypto.randomUUID();

const stream = agent.query({
  prompt: 'Search for recent news',
  tcc: { runId },
});

// Later, submit feedback for this run
await submitFeedback({ runId, score: 'thumbs_up' });

Session IDs

Session IDs group multiple runs together (e.g., a conversation):
const sessionId = crypto.randomUUID();

// First message in conversation
await processQuery({
  prompt: 'Hello, I need help with Python',
  tcc: { sessionId },
});

// Follow-up message in same conversation
await processQuery({
  prompt: 'Can you show me an example?',
  tcc: { sessionId },
});

Working with Tools

The instrumentation automatically captures tool definitions and executions:
import { Claude } from '@anthropic-ai/claude-agent-sdk';
import { instrumentClaudeAgent } from '@contextcompany/claude';

const claude = new Claude({ apiKey: process.env.ANTHROPIC_API_KEY });
const agent = instrumentClaudeAgent(claude);

// Define a tool
const weatherTool = agent.tool({
  name: 'get_weather',
  description: 'Get current weather for a location',
  parameters: {
    type: 'object',
    properties: {
      location: { type: 'string', description: 'City name' },
    },
    required: ['location'],
  },
  handler: async (args) => {
    const { location } = args;
    // Fetch weather data
    return { temperature: 72, conditions: 'sunny', location };
  },
});

// Use the agent with tools
const stream = agent.query({
  prompt: 'What is the weather in San Francisco?',
  options: {
    tools: [weatherTool],
  },
  tcc: {
    metadata: { toolsEnabled: true },
  },
});

for await (const message of stream) {
  if (message.type === 'tool_use') {
    console.log('Tool called:', message.name);
  } else if (message.type === 'tool_result') {
    console.log('Tool result:', message.content);
  }
}
Observatory captures:
  • Tool definitions passed to the agent
  • Tool call arguments
  • Tool execution results
  • Tool execution timing

Custom Metadata

Add custom metadata to track additional context:
const stream = agent.query({
  prompt: 'Analyze this data',
  tcc: {
    metadata: {
      userId: 'user_123',
      environment: 'production',
      version: '2.0.0',
      feature: 'data-analysis',
      experimentGroup: 'variant-b',
    },
  },
});
Metadata is searchable and filterable in the Observatory dashboard.

Debug Mode

Enable debug mode to see detailed logging:
const stream = agent.query({
  prompt: 'Debug this query',
  tcc: {
    debug: true,
  },
});
You’ll see console logs for:
  • Query initialization
  • Message collection
  • Telemetry transmission
  • API responses

Error Handling

Errors are automatically captured and reported:
try {
  const stream = agent.query({
    prompt: 'This might fail',
    tcc: { runId: 'run_123' },
  });

  for await (const message of stream) {
    // Process messages
  }
} catch (error) {
  // Error is automatically sent to Observatory
  console.error('Agent error:', error);
}
Partial data is sent even if the stream fails midway.

Submitting Feedback

Collect user feedback for specific runs:
import { submitFeedback } from '@contextcompany/claude';

const runId = crypto.randomUUID();

// Run the agent
const stream = agent.query({
  prompt: 'Help me write code',
  tcc: { runId },
});

for await (const message of stream) {
  // Handle messages
}

// Later, when user provides feedback
await submitFeedback({
  runId,
  score: 'thumbs_up', // or 'thumbs_down'
});

Environment Variables

TCC_API_KEY
string
required
Your Observatory API key. Required for sending telemetry data.
TCC_URL
string
Custom ingestion endpoint. Only needed for self-hosted instances.

API Reference

instrumentClaudeAgent<T>(sdk: T): WrappedSDK<T>

Wraps a Claude Agent SDK instance with Observatory instrumentation. Parameters:
  • sdk: Your Claude Agent SDK instance
Returns:
  • A wrapped SDK with the same interface, plus support for the tcc parameter in query() calls
Example:
const claude = new Claude({ apiKey: process.env.ANTHROPIC_API_KEY });
const agent = instrumentClaudeAgent(claude);

TCC Configuration

The tcc parameter accepts the following options:
runId
string
Unique identifier for this agent run. Auto-generated if not provided.
sessionId
string
Session identifier for grouping related runs (e.g., a conversation).
metadata
Record<string, unknown>
Custom key-value metadata to attach to the run. All values must be JSON-serializable.
debug
boolean
default:"false"
Enable debug logging to console.

How It Works

The instrumentation uses JavaScript Proxies to transparently wrap:
  1. Query function: Intercepts the agent.query() calls
  2. Message stream: Collects all messages as they’re yielded
  3. Tool handlers: Wraps tool handlers to capture inputs and outputs
Data is sent to Observatory after the stream completes. If the stream fails, partial data is sent with error information.

Performance Considerations

  • Zero latency impact: Messages are collected in-memory and sent asynchronously after the stream completes
  • Memory overhead: Minimal - only stores messages until the stream ends
  • Network: Single HTTP request per agent query after completion

Troubleshooting

  1. Verify TCC_API_KEY is set correctly
  2. Enable debug mode to see transmission logs:
    agent.query({ prompt: '...', tcc: { debug: true } })
    
  3. Check for errors in the console
  4. Ensure the stream completes (data is sent after completion)
If you get type errors about the tcc parameter, ensure:
  1. Your TypeScript version is 5.0 or higher
  2. You’re importing the correct types:
    import type { TCCConfig, WrappedSDK } from '@contextcompany/claude';
    
Tool instrumentation happens automatically, but:
  1. Ensure tools are defined using agent.tool()
  2. Enable debug mode to verify tool calls are being captured
  3. Check that tools are actually being invoked by the agent
Metadata is stored exactly as provided. Ensure:
  1. Values are JSON-serializable (no functions, circular references, etc.)
  2. Keys don’t start with tcc. (reserved prefix)
  3. Debug mode is enabled to see what’s being sent

Next Steps

Configuration

Learn about configuration options

Session Tracking

Learn more about tracking conversational sessions

Feedback Collection

Set up user feedback collection

API Reference

Complete API documentation

Build docs developers (and LLMs) love