Skip to content

Commit 6e267e0

Browse files
codydeclaude
andcommitted
refactor(node): Simplify Claude Code integration with OTEL auto-instrumentation
- Add OpenTelemetry-based automatic instrumentation via SentryClaudeCodeAgentSdkInstrumentation - Extract ClaudeCodeOptions to dedicated types.ts file - Remove backwards compatibility exports (patchClaudeCodeQuery, createInstrumentedClaudeQuery) - Rename integration to claudeCodeAgentSdkIntegration - Register instrumentation in OTEL preload for automatic patching - Update NextJS re-exports to match simplified API Users now only need: ```typescript Sentry.init({ integrations: [Sentry.claudeCodeAgentSdkIntegration()] }); import { query } from '@anthropic-ai/claude-agent-sdk'; // Auto-instrumented ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 0f8bb2c commit 6e267e0

File tree

8 files changed

+214
-231
lines changed

8 files changed

+214
-231
lines changed

packages/nextjs/src/index.types.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,7 @@ export declare const contextLinesIntegration: typeof clientSdk.contextLinesInteg
2626
export declare const vercelAIIntegration: typeof serverSdk.vercelAIIntegration;
2727

2828
// Claude Code integration (server-only)
29-
export declare const claudeCodeIntegration: typeof serverSdk.claudeCodeIntegration;
30-
export declare const createInstrumentedClaudeQuery: typeof serverSdk.createInstrumentedClaudeQuery;
31-
export declare const patchClaudeCodeQuery: typeof serverSdk.patchClaudeCodeQuery;
29+
export declare const claudeCodeAgentSdkIntegration: typeof serverSdk.claudeCodeAgentSdkIntegration;
3230

3331
export declare const getDefaultIntegrations: (options: Options) => Integration[];
3432
export declare const defaultStackParser: StackParser;

packages/nextjs/src/server/index.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,7 @@ import {
3030
getDefaultIntegrations,
3131
httpIntegration,
3232
init as nodeInit,
33-
claudeCodeIntegration,
34-
createInstrumentedClaudeQuery,
35-
patchClaudeCodeQuery,
33+
claudeCodeAgentSdkIntegration,
3634
} from '@sentry/node';
3735
import { getScopesFromContext } from '@sentry/opentelemetry';
3836
import { DEBUG_BUILD } from '../common/debug-build';
@@ -52,12 +50,12 @@ import { distDirRewriteFramesIntegration } from './distDirRewriteFramesIntegrati
5250

5351
export * from '@sentry/node';
5452

55-
// Explicit re-exports for Claude Code integration
56-
// We re-export these explicitly to ensure rollup doesn't tree-shake them
57-
export { claudeCodeIntegration, createInstrumentedClaudeQuery, patchClaudeCodeQuery };
53+
// Explicit re-export for Claude Code integration
54+
// We re-export this explicitly to ensure rollup doesn't tree-shake it
55+
export { claudeCodeAgentSdkIntegration };
5856

59-
// Force rollup to keep the imports by "using" them
60-
const _forceInclude = { claudeCodeIntegration, createInstrumentedClaudeQuery, patchClaudeCodeQuery };
57+
// Force rollup to keep the import by "using" it
58+
const _forceInclude = { claudeCodeAgentSdkIntegration };
6159
if (false as boolean) {
6260
console.log(_forceInclude);
6361
}

packages/node/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ export { amqplibIntegration } from './integrations/tracing/amqplib';
2626
export { vercelAIIntegration } from './integrations/tracing/vercelai';
2727
export { openAIIntegration } from './integrations/tracing/openai';
2828
export { anthropicAIIntegration } from './integrations/tracing/anthropic-ai';
29-
export { claudeCodeIntegration, patchClaudeCodeQuery } from './integrations/tracing/claude-code';
30-
export { createInstrumentedClaudeQuery } from './integrations/tracing/claude-code/helpers';
29+
export { claudeCodeAgentSdkIntegration } from './integrations/tracing/claude-code';
30+
export type { ClaudeCodeOptions } from './integrations/tracing/claude-code';
3131
export { googleGenAIIntegration } from './integrations/tracing/google-genai';
3232
export { langChainIntegration } from './integrations/tracing/langchain';
3333
export { langGraphIntegration } from './integrations/tracing/langgraph';

packages/node/src/integrations/tracing/claude-code/helpers.ts

Lines changed: 0 additions & 140 deletions
This file was deleted.
Lines changed: 48 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,81 @@
11
import type { IntegrationFn } from '@sentry/core';
22
import { defineIntegration } from '@sentry/core';
3-
import { patchClaudeCodeQuery } from './instrumentation';
3+
import { generateInstrumentOnce } from '@sentry/node-core';
4+
import { SentryClaudeCodeAgentSdkInstrumentation } from './otel-instrumentation';
5+
import type { ClaudeCodeOptions } from './types';
46

5-
export interface ClaudeCodeOptions {
6-
/**
7-
* Whether to record prompt messages.
8-
* Defaults to Sentry client's `sendDefaultPii` setting.
9-
*/
10-
recordInputs?: boolean;
7+
export type { ClaudeCodeOptions } from './types';
118

12-
/**
13-
* Whether to record response text, tool calls, and tool outputs.
14-
* Defaults to Sentry client's `sendDefaultPii` setting.
15-
*/
16-
recordOutputs?: boolean;
9+
export const CLAUDE_CODE_AGENT_SDK_INTEGRATION_NAME = 'ClaudeCodeAgentSdk';
1710

18-
/**
19-
* Custom agent name to use for this integration.
20-
* This allows you to differentiate between multiple Claude Code agents in your application.
21-
* Defaults to 'claude-code'.
22-
*
23-
* @example
24-
* ```typescript
25-
* const query = createInstrumentedClaudeQuery({ name: 'app-builder' });
26-
* ```
27-
*/
28-
agentName?: string;
29-
}
30-
31-
const CLAUDE_CODE_INTEGRATION_NAME = 'ClaudeCode';
11+
/**
12+
* Instruments the Claude Code Agent SDK using OpenTelemetry.
13+
* This is called automatically when the integration is added to Sentry.
14+
*/
15+
export const instrumentClaudeCodeAgentSdk = generateInstrumentOnce<ClaudeCodeOptions>(
16+
CLAUDE_CODE_AGENT_SDK_INTEGRATION_NAME,
17+
options => new SentryClaudeCodeAgentSdkInstrumentation(options),
18+
);
3219

33-
const _claudeCodeIntegration = ((options: ClaudeCodeOptions = {}) => {
20+
const _claudeCodeAgentSdkIntegration = ((options: ClaudeCodeOptions = {}) => {
3421
return {
35-
name: CLAUDE_CODE_INTEGRATION_NAME,
22+
name: CLAUDE_CODE_AGENT_SDK_INTEGRATION_NAME,
3623
options,
3724
setupOnce() {
38-
// Note: Automatic patching via require hooks doesn't work for ESM modules
39-
// or webpack-bundled dependencies. Users must manually patch using patchClaudeCodeQuery()
40-
// in their route files.
25+
instrumentClaudeCodeAgentSdk(options);
4126
},
4227
};
4328
}) satisfies IntegrationFn;
4429

4530
/**
46-
* Adds Sentry tracing instrumentation for the Claude Code SDK.
31+
* Adds Sentry tracing instrumentation for the Claude Code Agent SDK.
4732
*
48-
* **Important**: Due to ESM module and bundler limitations, this integration requires
49-
* using the `createInstrumentedClaudeQuery()` helper function in your code.
50-
* See the example below for proper usage.
33+
* This integration automatically instruments the `query` function from
34+
* `@anthropic-ai/claude-agent-sdk` to capture telemetry data following
35+
* OpenTelemetry Semantic Conventions for Generative AI.
5136
*
52-
* This integration captures telemetry data following OpenTelemetry Semantic Conventions
53-
* for Generative AI, including:
54-
* - Agent invocation spans (`invoke_agent`)
55-
* - LLM chat spans (`chat`)
56-
* - Tool execution spans (`execute_tool`)
57-
* - Token usage, model info, and session tracking
37+
* **Important**: Sentry must be initialized BEFORE importing `@anthropic-ai/claude-agent-sdk`.
5838
*
5939
* @example
6040
* ```typescript
61-
* // Step 1: Configure the integration
41+
* // Initialize Sentry FIRST
6242
* import * as Sentry from '@sentry/node';
6343
*
6444
* Sentry.init({
6545
* dsn: 'your-dsn',
6646
* integrations: [
67-
* Sentry.claudeCodeIntegration({
47+
* Sentry.claudeCodeAgentSdkIntegration({
6848
* recordInputs: true,
6949
* recordOutputs: true
7050
* })
7151
* ],
7252
* });
7353
*
74-
* // Step 2: Use the helper in your routes
75-
* import { createInstrumentedClaudeQuery } from '@sentry/node';
76-
*
77-
* const query = createInstrumentedClaudeQuery();
54+
* // THEN import the SDK - it will be automatically instrumented!
55+
* import { query } from '@anthropic-ai/claude-agent-sdk';
7856
*
79-
* // Use query as normal - automatically instrumented!
57+
* // Use query as normal - spans are created automatically
8058
* for await (const message of query({
8159
* prompt: 'Hello',
82-
* options: { model: 'claude-sonnet-4-5' }
60+
* options: { model: 'claude-sonnet-4-20250514' }
8361
* })) {
8462
* console.log(message);
8563
* }
8664
* ```
8765
*
66+
* ## Captured Telemetry
67+
*
68+
* This integration captures:
69+
* - Agent invocation spans (`gen_ai.invoke_agent`)
70+
* - LLM chat spans (`gen_ai.chat`)
71+
* - Tool execution spans (`gen_ai.execute_tool`)
72+
* - Token usage, model info, and session tracking
73+
*
8874
* ## Options
8975
*
9076
* - `recordInputs`: Whether to record prompt messages (default: respects `sendDefaultPii` client option)
9177
* - `recordOutputs`: Whether to record response text, tool calls, and outputs (default: respects `sendDefaultPii` client option)
78+
* - `agentName`: Custom agent name for differentiation (default: 'claude-code')
9279
*
9380
* ### Default Behavior
9481
*
@@ -101,7 +88,7 @@ const _claudeCodeIntegration = ((options: ClaudeCodeOptions = {}) => {
10188
* // Record inputs and outputs when sendDefaultPii is false
10289
* Sentry.init({
10390
* integrations: [
104-
* Sentry.claudeCodeIntegration({
91+
* Sentry.claudeCodeAgentSdkIntegration({
10592
* recordInputs: true,
10693
* recordOutputs: true
10794
* })
@@ -112,31 +99,23 @@ const _claudeCodeIntegration = ((options: ClaudeCodeOptions = {}) => {
11299
* Sentry.init({
113100
* sendDefaultPii: true,
114101
* integrations: [
115-
* Sentry.claudeCodeIntegration({
102+
* Sentry.claudeCodeAgentSdkIntegration({
116103
* recordInputs: false,
117104
* recordOutputs: false
118105
* })
119106
* ],
120107
* });
108+
*
109+
* // Custom agent name
110+
* Sentry.init({
111+
* integrations: [
112+
* Sentry.claudeCodeAgentSdkIntegration({
113+
* agentName: 'my-coding-assistant'
114+
* })
115+
* ],
116+
* });
121117
* ```
122118
*
123119
* @see https://docs.sentry.io/platforms/javascript/guides/node/ai-monitoring/
124120
*/
125-
export const claudeCodeIntegration = defineIntegration(_claudeCodeIntegration);
126-
127-
/**
128-
* Manually patch the Claude Code SDK query function with Sentry instrumentation.
129-
*
130-
* **Note**: Most users should use `createInstrumentedClaudeQuery()` instead,
131-
* which is simpler and handles option retrieval automatically.
132-
*
133-
* This low-level function is exported for advanced use cases where you need
134-
* explicit control over the patching process.
135-
*
136-
* @param queryFunction - The original query function from @anthropic-ai/claude-agent-sdk
137-
* @param options - Instrumentation options (recordInputs, recordOutputs)
138-
* @returns Instrumented query function
139-
*
140-
* @see createInstrumentedClaudeQuery for the recommended high-level helper
141-
*/
142-
export { patchClaudeCodeQuery };
121+
export const claudeCodeAgentSdkIntegration = defineIntegration(_claudeCodeAgentSdkIntegration);

0 commit comments

Comments
 (0)