Skip to content

Commit a68e453

Browse files
committed
feat(node): Add mcp server instrumentation
1 parent 8046e14 commit a68e453

File tree

2 files changed

+126
-0
lines changed

2 files changed

+126
-0
lines changed

Diff for: packages/node/src/integrations/mcp-server.ts

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// import { isWrapped } from '@opentelemetry/core';
2+
import type { InstrumentationConfig } from '@opentelemetry/instrumentation';
3+
import { InstrumentationNodeModuleFile } from '@opentelemetry/instrumentation';
4+
import { InstrumentationBase, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';
5+
import { SDK_VERSION } from '@sentry/core';
6+
import { generateInstrumentOnce } from '../otel/instrument';
7+
import { defineIntegration } from '@sentry/core';
8+
9+
const supportedVersions = ['> 0.0.1'];
10+
11+
interface MCPServerInstance {
12+
tool: (toolName: string, toolSchema: unknown, handler: (...args: unknown[]) => unknown) => void;
13+
}
14+
15+
interface MCPSdkModuleDef {
16+
McpServer: new (...args: unknown[]) => MCPServerInstance;
17+
}
18+
19+
const perms = [
20+
'@modelcontextprotocol/sdk',
21+
'@modelcontextprotocol/sdk/server/mcp',
22+
'@modelcontextprotocol/sdk/server/mcp.js',
23+
'@modelcontextprotocol/sdk/dist/esm/server/mcp.js',
24+
'@modelcontextprotocol/sdk/dist/cjs/server/mcp.js',
25+
'@modelcontextprotocol/sdk/dist/esm/server/mcp',
26+
'@modelcontextprotocol/sdk/dist/cjs/server/mcp',
27+
'./server/mcp',
28+
'./server/mcp.js',
29+
];
30+
31+
/**
32+
* Todo
33+
*/
34+
export class McpInstrumentation extends InstrumentationBase {
35+
public constructor(config: InstrumentationConfig = {}) {
36+
super('sentry-modelcontextprotocol-sdk', SDK_VERSION, config);
37+
}
38+
39+
/**
40+
* Initializes the instrumentation by defining the modules to be patched.
41+
*/
42+
public init(): InstrumentationNodeModuleDefinition[] {
43+
const moduleDefs: InstrumentationNodeModuleDefinition[] = [];
44+
45+
perms.forEach(modulePath => {
46+
const moduleDef = new InstrumentationNodeModuleDefinition(
47+
modulePath,
48+
supportedVersions,
49+
(moduleExports: MCPSdkModuleDef) => {
50+
console.log('module wrap', modulePath, moduleExports);
51+
return moduleExports;
52+
},
53+
(moduleExports: MCPSdkModuleDef) => {
54+
this._unwrap(moduleExports, 'McpServer');
55+
},
56+
);
57+
58+
perms.forEach(filePath => {
59+
moduleDef.files.push(
60+
new InstrumentationNodeModuleFile(
61+
filePath,
62+
supportedVersions,
63+
(moduleExports: MCPSdkModuleDef) => {
64+
console.log('file wrap', filePath, moduleExports);
65+
return moduleExports;
66+
},
67+
(moduleExports: MCPSdkModuleDef) => {
68+
this._unwrap(moduleExports, 'McpServer');
69+
},
70+
),
71+
);
72+
});
73+
74+
moduleDefs.push(moduleDef);
75+
});
76+
77+
return moduleDefs;
78+
}
79+
}
80+
const INTEGRATION_NAME = 'MCP';
81+
82+
const instrumentMcp = generateInstrumentOnce('MCP', () => {
83+
return new McpInstrumentation();
84+
});
85+
86+
/**
87+
* Integration capturing tracing data for MCP servers.
88+
*/
89+
export const mcpIntegration = defineIntegration(() => {
90+
return {
91+
name: INTEGRATION_NAME,
92+
setupOnce() {
93+
instrumentMcp();
94+
},
95+
};
96+
});
97+
98+
// console.log('FILE called', moduleExports);
99+
100+
// if (isWrapped(moduleExports.McpServer)) {
101+
// this._unwrap(moduleExports, 'McpServer');
102+
// }
103+
104+
// console.log('WRAP called');
105+
106+
// this._wrap(moduleExports, 'McpServer', originalMcpServerClass => {
107+
// console.log('WRAPPING', originalMcpServerClass);
108+
// return new Proxy(originalMcpServerClass, {
109+
// construct(McpServerClass, mcpServerConstructorArgArray) {
110+
// const mcpServerInstance = new McpServerClass(...mcpServerConstructorArgArray);
111+
112+
// console.log('MCP Server constructed');
113+
114+
// mcpServerInstance.tool = new Proxy(mcpServerInstance.tool, {
115+
// apply(toolTarget, toolThisArg, toolArgArray) {
116+
// console.log('Tool handler called!: ', toolArgArray[0]);
117+
// return toolTarget.apply(toolThisArg, toolArgArray);
118+
// },
119+
// });
120+
121+
// return mcpServerInstance;
122+
// },
123+
// });
124+
// });

Diff for: packages/node/src/sdk/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import { envToBool } from '../utils/envToBool';
3939
import { defaultStackParser, getSentryRelease } from './api';
4040
import { NodeClient } from './client';
4141
import { initOpenTelemetry, maybeInitializeEsmLoader } from './initOtel';
42+
import { mcpIntegration } from '../integrations/mcp-server';
4243

4344
function getCjsOnlyIntegrations(): Integration[] {
4445
return isCjs() ? [modulesIntegration()] : [];
@@ -69,6 +70,7 @@ export function getDefaultIntegrationsWithoutPerformance(): Integration[] {
6970
nodeContextIntegration(),
7071
childProcessIntegration(),
7172
processSessionIntegration(),
73+
mcpIntegration(),
7274
...getCjsOnlyIntegrations(),
7375
];
7476
}

0 commit comments

Comments
 (0)