@posthog/mcp 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"instrument-highlevel.d.ts","sourceRoot":"","sources":["../../src/extensions/instrument-highlevel.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAiC,sBAAsB,EAAiC,MAAM,UAAU,CAAA;AAmRpH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAqB9E"}
1
+ {"version":3,"file":"instrument-highlevel.d.ts","sourceRoot":"","sources":["../../src/extensions/instrument-highlevel.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAiC,sBAAsB,EAAiC,MAAM,UAAU,CAAA;AAqPpH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAwB9E"}
@@ -63,7 +63,6 @@ function setupListenerToRegisteredTools(server) {
63
63
  return Reflect.set(target, property, value);
64
64
  }
65
65
  const nextValue = addTracingToToolCallbackInternal(value, property, server);
66
- (0, external_instrumentation_js_namespaceObject.instrumentToolsListHandler)(server.server);
67
66
  if ('function' == typeof nextValue.update) {
68
67
  const originalUpdate = nextValue.update;
69
68
  nextValue.update = function(...updateArgs) {
@@ -151,32 +150,11 @@ function addTracingToToolCallbackInternal(tool, toolName, server) {
151
150
  wrappedTool[MCP_ANALYTICS_PROCESSED] = true;
152
151
  return wrappedTool;
153
152
  }
154
- function setupToolsCallHandlerWrapping(server) {
155
- const lowLevelServer = server.server;
156
- const existingHandler = lowLevelServer._requestHandlers.get('tools/call');
157
- if (existingHandler) {
158
- const wrappedHandler = createToolsCallWrapper(existingHandler, lowLevelServer);
159
- lowLevelServer._requestHandlers.set('tools/call', wrappedHandler);
160
- }
161
- const originalSetRequestHandler = lowLevelServer.setRequestHandler.bind(lowLevelServer);
162
- lowLevelServer.setRequestHandler = (requestSchema, handler)=>{
163
- const shape = (0, external_mcp_sdk_compat_js_namespaceObject.getObjectShape)(requestSchema);
164
- const method = shape?.method ? (0, external_mcp_sdk_compat_js_namespaceObject.getLiteralValue)(shape.method) : void 0;
165
- if ('tools/call' === method) {
166
- const wrappedHandler = createToolsCallWrapper(handler, lowLevelServer);
167
- return originalSetRequestHandler(requestSchema, wrappedHandler);
168
- }
169
- return originalSetRequestHandler(requestSchema, handler);
170
- };
171
- }
172
- function createToolsCallWrapper(originalHandler, server) {
173
- return async (request, extra)=>await handleWrappedToolsCall(originalHandler, server, request, extra);
174
- }
175
- async function handleWrappedToolsCall(originalHandler, server, request, extra) {
153
+ async function handleToolCallRequest(server, originalCallToolHandler, request, extra) {
176
154
  const data = (0, external_internal_js_namespaceObject.getServerTrackingData)(server);
177
155
  if (!data) {
178
156
  (0, external_logger_js_namespaceObject.log)('Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called instrument(server, options) before using tool calls.');
179
- return await originalHandler(request, extra);
157
+ return await originalCallToolHandler(request, extra);
180
158
  }
181
159
  if (request.params?.name === (0, external_tools_js_namespaceObject.resolveMissingCapabilityToolName)(data.options)) {
182
160
  const context = (0, external_tracing_helpers_js_namespaceObject.getContextArgument)(request) || '';
@@ -197,7 +175,7 @@ async function handleWrappedToolsCall(originalHandler, server, request, extra) {
197
175
  data,
198
176
  request,
199
177
  extra,
200
- execute: ()=>originalHandler(request, extra),
178
+ execute: ()=>originalCallToolHandler(request, extra),
201
179
  takeCapturedError: ()=>{
202
180
  const captured = extra?.__mcp_analytics_error;
203
181
  if (extra) extra.__mcp_analytics_error = void 0;
@@ -207,15 +185,19 @@ async function handleWrappedToolsCall(originalHandler, server, request, extra) {
207
185
  }
208
186
  function instrumentHighLevelServer(server) {
209
187
  try {
210
- const mcpAnalyticsData = (0, external_internal_js_namespaceObject.getServerTrackingData)(server.server);
211
- setupToolsCallHandlerWrapping(server);
212
- (0, external_instrumentation_js_namespaceObject.instrumentInitializeHandler)(server.server);
188
+ const lowLevelServer = server.server;
189
+ const mcpAnalyticsData = (0, external_internal_js_namespaceObject.getServerTrackingData)(lowLevelServer);
190
+ const handlers = {
191
+ initialize: external_instrumentation_js_namespaceObject.handleInitializeRequest,
192
+ 'tools/list': external_instrumentation_js_namespaceObject.handleListToolsRequest,
193
+ 'tools/call': handleToolCallRequest
194
+ };
195
+ (0, external_instrumentation_js_namespaceObject.patchRequestHandlers)(lowLevelServer, handlers);
213
196
  server._registeredTools = addTracingToToolRegistry(server._registeredTools, server);
214
197
  if (mcpAnalyticsData) {
215
198
  seedToolDescriptionsFromRegistry(mcpAnalyticsData.toolDescriptions, server._registeredTools);
216
199
  seedToolCategoriesFromRegistry(mcpAnalyticsData.toolCategories, server._registeredTools);
217
200
  }
218
- (0, external_instrumentation_js_namespaceObject.instrumentToolsListHandler)(server.server);
219
201
  setupListenerToRegisteredTools(server);
220
202
  } catch (error) {
221
203
  (0, external_logger_js_namespaceObject.log)(`Warning: Failed to setup tool call instrumentation - ${error}`);
@@ -2,9 +2,9 @@ import { stripConversationId } from "./conversation-id.mjs";
2
2
  import { MCPAnalyticsEventType } from "./event-types.mjs";
3
3
  import { getServerTrackingData } from "./internal.mjs";
4
4
  import { log } from "./logger.mjs";
5
- import { createWrappedTool, getLiteralValue, getObjectShape, getToolFunction, hasToolFunction } from "./mcp-sdk-compat.mjs";
5
+ import { createWrappedTool, getToolFunction, hasToolFunction } from "./mcp-sdk-compat.mjs";
6
6
  import { handleReportMissing, resolveMissingCapabilityToolName } from "./tools.mjs";
7
- import { captureToolCall, instrumentInitializeHandler, instrumentToolsListHandler, readToolMetaCategory } from "./instrumentation.mjs";
7
+ import { captureToolCall, handleInitializeRequest, handleListToolsRequest, patchRequestHandlers, readToolMetaCategory } from "./instrumentation.mjs";
8
8
  import { getContextArgument } from "./tracing-helpers.mjs";
9
9
  const wrappedCallbacks = new WeakMap();
10
10
  const MCP_ANALYTICS_PROCESSED = Symbol('__posthog_mcp_analytics_processed__');
@@ -35,7 +35,6 @@ function setupListenerToRegisteredTools(server) {
35
35
  return Reflect.set(target, property, value);
36
36
  }
37
37
  const nextValue = addTracingToToolCallbackInternal(value, property, server);
38
- instrumentToolsListHandler(server.server);
39
38
  if ('function' == typeof nextValue.update) {
40
39
  const originalUpdate = nextValue.update;
41
40
  nextValue.update = function(...updateArgs) {
@@ -123,32 +122,11 @@ function addTracingToToolCallbackInternal(tool, toolName, server) {
123
122
  wrappedTool[MCP_ANALYTICS_PROCESSED] = true;
124
123
  return wrappedTool;
125
124
  }
126
- function setupToolsCallHandlerWrapping(server) {
127
- const lowLevelServer = server.server;
128
- const existingHandler = lowLevelServer._requestHandlers.get('tools/call');
129
- if (existingHandler) {
130
- const wrappedHandler = createToolsCallWrapper(existingHandler, lowLevelServer);
131
- lowLevelServer._requestHandlers.set('tools/call', wrappedHandler);
132
- }
133
- const originalSetRequestHandler = lowLevelServer.setRequestHandler.bind(lowLevelServer);
134
- lowLevelServer.setRequestHandler = (requestSchema, handler)=>{
135
- const shape = getObjectShape(requestSchema);
136
- const method = shape?.method ? getLiteralValue(shape.method) : void 0;
137
- if ('tools/call' === method) {
138
- const wrappedHandler = createToolsCallWrapper(handler, lowLevelServer);
139
- return originalSetRequestHandler(requestSchema, wrappedHandler);
140
- }
141
- return originalSetRequestHandler(requestSchema, handler);
142
- };
143
- }
144
- function createToolsCallWrapper(originalHandler, server) {
145
- return async (request, extra)=>await handleWrappedToolsCall(originalHandler, server, request, extra);
146
- }
147
- async function handleWrappedToolsCall(originalHandler, server, request, extra) {
125
+ async function handleToolCallRequest(server, originalCallToolHandler, request, extra) {
148
126
  const data = getServerTrackingData(server);
149
127
  if (!data) {
150
128
  log('Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called instrument(server, options) before using tool calls.');
151
- return await originalHandler(request, extra);
129
+ return await originalCallToolHandler(request, extra);
152
130
  }
153
131
  if (request.params?.name === resolveMissingCapabilityToolName(data.options)) {
154
132
  const context = getContextArgument(request) || '';
@@ -169,7 +147,7 @@ async function handleWrappedToolsCall(originalHandler, server, request, extra) {
169
147
  data,
170
148
  request,
171
149
  extra,
172
- execute: ()=>originalHandler(request, extra),
150
+ execute: ()=>originalCallToolHandler(request, extra),
173
151
  takeCapturedError: ()=>{
174
152
  const captured = extra?.__mcp_analytics_error;
175
153
  if (extra) extra.__mcp_analytics_error = void 0;
@@ -179,15 +157,19 @@ async function handleWrappedToolsCall(originalHandler, server, request, extra) {
179
157
  }
180
158
  function instrumentHighLevelServer(server) {
181
159
  try {
182
- const mcpAnalyticsData = getServerTrackingData(server.server);
183
- setupToolsCallHandlerWrapping(server);
184
- instrumentInitializeHandler(server.server);
160
+ const lowLevelServer = server.server;
161
+ const mcpAnalyticsData = getServerTrackingData(lowLevelServer);
162
+ const handlers = {
163
+ initialize: handleInitializeRequest,
164
+ 'tools/list': handleListToolsRequest,
165
+ 'tools/call': handleToolCallRequest
166
+ };
167
+ patchRequestHandlers(lowLevelServer, handlers);
185
168
  server._registeredTools = addTracingToToolRegistry(server._registeredTools, server);
186
169
  if (mcpAnalyticsData) {
187
170
  seedToolDescriptionsFromRegistry(mcpAnalyticsData.toolDescriptions, server._registeredTools);
188
171
  seedToolCategoriesFromRegistry(mcpAnalyticsData.toolCategories, server._registeredTools);
189
172
  }
190
- instrumentToolsListHandler(server.server);
191
173
  setupListenerToRegisteredTools(server);
192
174
  } catch (error) {
193
175
  log(`Warning: Failed to setup tool call instrumentation - ${error}`);
@@ -1 +1 @@
1
- {"version":3,"file":"instrument-lowlevel.d.ts","sourceRoot":"","sources":["../../src/extensions/instrument-lowlevel.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAiD,aAAa,EAAE,MAAM,UAAU,CAAA;AAc5F;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAcpE"}
1
+ {"version":3,"file":"instrument-lowlevel.d.ts","sourceRoot":"","sources":["../../src/extensions/instrument-lowlevel.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAiD,aAAa,EAAE,MAAM,UAAU,CAAA;AAmB5F;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAkBpE"}
@@ -35,8 +35,11 @@ const external_instrumentation_js_namespaceObject = require("./instrumentation.j
35
35
  const external_tracing_helpers_js_namespaceObject = require("./tracing-helpers.js");
36
36
  function instrumentLowLevelServer(server) {
37
37
  try {
38
- (0, external_instrumentation_js_namespaceObject.instrumentInitializeHandler)(server);
39
- (0, external_instrumentation_js_namespaceObject.instrumentToolsListHandler)(server);
38
+ const handlers = {
39
+ initialize: external_instrumentation_js_namespaceObject.handleInitializeRequest,
40
+ 'tools/list': external_instrumentation_js_namespaceObject.handleListToolsRequest
41
+ };
42
+ (0, external_instrumentation_js_namespaceObject.patchRequestHandlers)(server, handlers);
40
43
  const originalCallToolHandler = server._requestHandlers.get('tools/call');
41
44
  server.setRequestHandler(types_js_namespaceObject.CallToolRequestSchema, async (request, extra)=>await handleToolCallRequest(server, originalCallToolHandler, request, extra));
42
45
  } catch (error) {
@@ -3,12 +3,15 @@ import { MCPAnalyticsEventType } from "./event-types.mjs";
3
3
  import { getServerTrackingData } from "./internal.mjs";
4
4
  import { log } from "./logger.mjs";
5
5
  import { handleReportMissing, resolveMissingCapabilityToolName } from "./tools.mjs";
6
- import { captureToolCall, instrumentInitializeHandler, instrumentToolsListHandler } from "./instrumentation.mjs";
6
+ import { captureToolCall, handleInitializeRequest, handleListToolsRequest, patchRequestHandlers } from "./instrumentation.mjs";
7
7
  import { getContextArgument } from "./tracing-helpers.mjs";
8
8
  function instrumentLowLevelServer(server) {
9
9
  try {
10
- instrumentInitializeHandler(server);
11
- instrumentToolsListHandler(server);
10
+ const handlers = {
11
+ initialize: handleInitializeRequest,
12
+ 'tools/list': handleListToolsRequest
13
+ };
14
+ patchRequestHandlers(server, handlers);
12
15
  const originalCallToolHandler = server._requestHandlers.get('tools/call');
13
16
  server.setRequestHandler(CallToolRequestSchema, async (request, extra)=>await handleToolCallRequest(server, originalCallToolHandler, request, extra));
14
17
  } catch (error) {
@@ -1,6 +1,14 @@
1
- import { type ListToolsResult } from '@modelcontextprotocol/sdk/types.js';
1
+ import type { ListToolsResult } from '@modelcontextprotocol/sdk/types.js';
2
2
  import type { CompatibleRequestHandlerExtra, MCPAnalyticsData, MCPRequestLike, MCPServerLike } from '../types';
3
3
  import { MCPAnalyticsEventType } from './event-types';
4
+ /**
5
+ * Single instrumentation core shared by the low-level (`Server`) and high-level
6
+ * (`McpServer`) wrappers. The two entrypoints differ only in how they reach the
7
+ * underlying tool — they both funnel the tool-call lifecycle through
8
+ * {@link captureToolCall}, so error handling, conversation-id minting, session
9
+ * attribution, and event capture are defined exactly once.
10
+ */
11
+ type MCPRequestHandler = (request: MCPRequestLike, extra?: CompatibleRequestHandlerExtra) => Promise<unknown>;
4
12
  /**
5
13
  * Runs the underlying tool. Receives the request with the SDK-injected
6
14
  * `conversation_id` stripped; an adapter is free to ignore it (the high-level
@@ -43,12 +51,26 @@ interface TraceToolCallParams {
43
51
  */
44
52
  export declare function captureToolCall(params: TraceToolCallParams): Promise<unknown>;
45
53
  /**
46
- * Wraps the server's `tools/list` handler so each listing is captured and the
47
- * SDK-managed tools (context parameter, conversation id, `get_more_tools`) are
48
- * injected. Idempotent per server. Works for both low-level and high-level
49
- * servers — the high-level wrapper passes its underlying `server`.
54
+ * A method's patch: runs the original handler and captures analytics. `server`
55
+ * and `originalHandler` are bound by {@link patchRequestHandlers}; the SDK
56
+ * supplies `request` and `extra` per call.
57
+ */
58
+ export type HandlerPatch = (server: MCPServerLike, originalHandler: MCPRequestHandler, request: MCPRequestLike, extra: CompatibleRequestHandlerExtra | undefined) => Promise<unknown>;
59
+ /**
60
+ * Applies the `patches` (keyed by method, e.g. `initialize`, `tools/list`) to the
61
+ * handlers already registered, and patches `setRequestHandler` so matching
62
+ * handlers registered later are patched too. The latter is what makes adapters
63
+ * that register handlers post-construction work — e.g. `@rekog/mcp-nest` hands a
64
+ * bare server to instrument() and only then registers its handlers.
65
+ */
66
+ export declare function patchRequestHandlers(server: MCPServerLike, patches: Record<string, HandlerPatch>): void;
67
+ /**
68
+ * Captures each `tools/list` and injects the SDK-managed tools (context
69
+ * parameter, conversation id, `get_more_tools`) into the returned list.
50
70
  */
51
- export declare function instrumentToolsListHandler(server: MCPServerLike): void;
71
+ export declare function handleListToolsRequest(server: MCPServerLike, originalListToolsHandler: MCPRequestHandler, request: MCPRequestLike, extra?: CompatibleRequestHandlerExtra): Promise<{
72
+ tools: ListToolsResult['tools'];
73
+ }>;
52
74
  export declare function cacheToolDescriptions(cache: Map<string, string>, tools: ListToolsResult['tools'] | undefined): void;
53
75
  /**
54
76
  * Category declared on a tool's `_meta` block (the MCP spec allows arbitrary
@@ -58,9 +80,9 @@ export declare function cacheToolDescriptions(cache: Map<string, string>, tools:
58
80
  export declare function readToolMetaCategory(meta: unknown): string | undefined;
59
81
  export declare function cacheToolCategories(cache: Map<string, string>, tools: ListToolsResult['tools'] | undefined): void;
60
82
  /**
61
- * Wraps the server's `initialize` handler so the connection handshake is
62
- * captured (and identity resolved) before the original handler runs.
83
+ * Captures the connection handshake (and resolves identity) on `initialize`
84
+ * before the original handler runs.
63
85
  */
64
- export declare function instrumentInitializeHandler(server: MCPServerLike): void;
86
+ export declare function handleInitializeRequest(server: MCPServerLike, originalInitializeHandler: MCPRequestHandler, request: MCPRequestLike, extra?: CompatibleRequestHandlerExtra): Promise<unknown>;
65
87
  export {};
66
88
  //# sourceMappingURL=instrumentation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../src/extensions/instrumentation.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,oCAAoC,CAAA;AAC3C,OAAO,KAAK,EAAE,6BAA6B,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAY,MAAM,UAAU,CAAA;AAWxH,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAoBrD;;;;GAIG;AACH,KAAK,YAAY,GAAG,CAAC,iBAAiB,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AAE3E,UAAU,mBAAmB;IAC3B,MAAM,EAAE,aAAa,CAAA;IACrB,IAAI,EAAE,gBAAgB,CAAA;IACtB,OAAO,EAAE,cAAc,CAAA;IACvB,KAAK,CAAC,EAAE,6BAA6B,CAAA;IACrC,OAAO,EAAE,YAAY,CAAA;IACrB;;;;OAIG;IACH,SAAS,CAAC,EAAE,qBAAqB,CAAA;IACjC;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAA;CAClC;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CAuCnF;AAwHD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAsBtE;AAuGD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,CASnH;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAGtE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,CAUjH;AAID;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAiCvE"}
1
+ {"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../src/extensions/instrumentation.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,KAAK,EAAE,6BAA6B,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAY,MAAM,UAAU,CAAA;AAWxH,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAWrD;;;;;;GAMG;AAEH,KAAK,iBAAiB,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE,6BAA6B,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AAE7G;;;;GAIG;AACH,KAAK,YAAY,GAAG,CAAC,iBAAiB,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AAE3E,UAAU,mBAAmB;IAC3B,MAAM,EAAE,aAAa,CAAA;IACrB,IAAI,EAAE,gBAAgB,CAAA;IACtB,OAAO,EAAE,cAAc,CAAA;IACvB,KAAK,CAAC,EAAE,6BAA6B,CAAA;IACrC,OAAO,EAAE,YAAY,CAAA;IACrB;;;;OAIG;IACH,SAAS,CAAC,EAAE,qBAAqB,CAAA;IACjC;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAA;CAClC;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CAuCnF;AAgHD;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,CACzB,MAAM,EAAE,aAAa,EACrB,eAAe,EAAE,iBAAiB,EAClC,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,6BAA6B,GAAG,SAAS,KAC7C,OAAO,CAAC,OAAO,CAAC,CAAA;AAErB;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,IAAI,CAqBvG;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,aAAa,EACrB,wBAAwB,EAAE,iBAAiB,EAC3C,OAAO,EAAE,cAAc,EACvB,KAAK,CAAC,EAAE,6BAA6B,GACpC,OAAO,CAAC;IAAE,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC,CAwC9C;AAwDD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,CASnH;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAGtE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,CAUjH;AAID;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,aAAa,EACrB,yBAAyB,EAAE,iBAAiB,EAC5C,OAAO,EAAE,cAAc,EACvB,KAAK,CAAC,EAAE,6BAA6B,GACpC,OAAO,CAAC,OAAO,CAAC,CA0BlB"}
@@ -27,11 +27,11 @@ __webpack_require__.d(__webpack_exports__, {
27
27
  readToolMetaCategory: ()=>readToolMetaCategory,
28
28
  cacheToolDescriptions: ()=>cacheToolDescriptions,
29
29
  cacheToolCategories: ()=>cacheToolCategories,
30
+ handleInitializeRequest: ()=>handleInitializeRequest,
30
31
  captureToolCall: ()=>captureToolCall,
31
- instrumentInitializeHandler: ()=>instrumentInitializeHandler,
32
- instrumentToolsListHandler: ()=>instrumentToolsListHandler
32
+ handleListToolsRequest: ()=>handleListToolsRequest,
33
+ patchRequestHandlers: ()=>patchRequestHandlers
33
34
  });
34
- const types_js_namespaceObject = require("@modelcontextprotocol/sdk/types.js");
35
35
  const external_context_parameters_js_namespaceObject = require("./context-parameters.js");
36
36
  const external_conversation_id_js_namespaceObject = require("./conversation-id.js");
37
37
  const external_capture_js_namespaceObject = require("./capture.js");
@@ -41,6 +41,7 @@ const external_intent_js_namespaceObject = require("./intent.js");
41
41
  const external_internal_js_namespaceObject = require("./internal.js");
42
42
  const external_logger_js_namespaceObject = require("./logger.js");
43
43
  const external_mcp_payloads_js_namespaceObject = require("./mcp-payloads.js");
44
+ const external_mcp_sdk_compat_js_namespaceObject = require("./mcp-sdk-compat.js");
44
45
  const external_session_js_namespaceObject = require("./session.js");
45
46
  const external_tools_js_namespaceObject = require("./tools.js");
46
47
  const external_tracing_helpers_js_namespaceObject = require("./tracing-helpers.js");
@@ -118,18 +119,19 @@ function publishFailedToolEvent(server, event, error, startTime, conversation) {
118
119
  (0, external_logger_js_namespaceObject.log)(`Warning: PostHog MCP analytics failed to publish failed tool event - ${publishError}`);
119
120
  }
120
121
  }
121
- const listToolsTracingSetup = new WeakMap();
122
- function instrumentToolsListHandler(server) {
123
- if (!server._capabilities?.tools) return;
124
- if (listToolsTracingSetup.get(server)) return;
125
- const originalListToolsHandler = server._requestHandlers.get('tools/list');
126
- if (!originalListToolsHandler) return;
127
- try {
128
- server.setRequestHandler(types_js_namespaceObject.ListToolsRequestSchema, async (request, extra)=>await handleListToolsRequest(server, originalListToolsHandler, request, extra));
129
- listToolsTracingSetup.set(server, true);
130
- } catch (error) {
131
- (0, external_logger_js_namespaceObject.log)(`Warning: Failed to override list tools handler - ${error}`);
122
+ function patchRequestHandlers(server, patches) {
123
+ for (const [handlerName, patch] of Object.entries(patches)){
124
+ const originalHandler = server._requestHandlers.get(handlerName);
125
+ if (originalHandler) server._requestHandlers.set(handlerName, (request, extra)=>patch(server, originalHandler, request, extra));
132
126
  }
127
+ const originalSetRequestHandler = server.setRequestHandler.bind(server);
128
+ server.setRequestHandler = (requestSchema, originalHandler)=>{
129
+ const shape = (0, external_mcp_sdk_compat_js_namespaceObject.getObjectShape)(requestSchema);
130
+ const handlerName = shape?.method ? (0, external_mcp_sdk_compat_js_namespaceObject.getLiteralValue)(shape.method) : void 0;
131
+ const patch = 'string' == typeof handlerName ? patches[handlerName] : void 0;
132
+ if (!patch) return originalSetRequestHandler(requestSchema, originalHandler);
133
+ return originalSetRequestHandler(requestSchema, (request, extra)=>patch(server, originalHandler, request, extra));
134
+ };
133
135
  }
134
136
  async function handleListToolsRequest(server, originalListToolsHandler, request, extra) {
135
137
  const data = (0, external_internal_js_namespaceObject.getServerTrackingData)(server);
@@ -215,43 +217,41 @@ function cacheToolCategories(cache, tools) {
215
217
  if (category) cache.set(tool.name, category);
216
218
  }
217
219
  }
218
- function instrumentInitializeHandler(server) {
219
- const originalInitializeHandler = server._requestHandlers.get('initialize');
220
- if (!originalInitializeHandler) return;
221
- server.setRequestHandler(types_js_namespaceObject.InitializeRequestSchema, async (request, extra)=>{
222
- const data = (0, external_internal_js_namespaceObject.getServerTrackingData)(server);
223
- if (!data) {
224
- (0, external_logger_js_namespaceObject.log)('Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called instrument(server, options) before using tool calls.');
225
- return await originalInitializeHandler(request, extra);
226
- }
227
- const sessionId = (0, external_session_js_namespaceObject.getServerSessionId)(server, extra);
228
- await (0, external_internal_js_namespaceObject.handleIdentify)(server, data, sessionId, request, extra);
229
- const event = {
230
- sessionId,
231
- resourceName: request.params?.name || 'Unknown Tool Name',
232
- eventType: external_event_types_js_namespaceObject.MCPAnalyticsEventType.mcpInitialize,
233
- parameters: (0, external_mcp_payloads_js_namespaceObject.buildCapturedMcpParameters)(request),
234
- timestamp: new Date()
235
- };
236
- await (0, external_tracing_helpers_js_namespaceObject.applyResolvedMetadata)(event, data, request, extra);
237
- const result = await originalInitializeHandler(request, extra);
238
- event.response = result;
239
- (0, external_capture_js_namespaceObject.captureEvent)(server, event);
240
- return result;
241
- });
220
+ async function handleInitializeRequest(server, originalInitializeHandler, request, extra) {
221
+ const data = (0, external_internal_js_namespaceObject.getServerTrackingData)(server);
222
+ if (!data) {
223
+ (0, external_logger_js_namespaceObject.log)('Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called instrument(server, options) before using tool calls.');
224
+ return await originalInitializeHandler(request, extra);
225
+ }
226
+ const sessionId = (0, external_session_js_namespaceObject.getServerSessionId)(server, extra);
227
+ await (0, external_internal_js_namespaceObject.handleIdentify)(server, data, sessionId, request, extra);
228
+ const event = {
229
+ sessionId,
230
+ resourceName: request.params?.name || 'Unknown Tool Name',
231
+ eventType: external_event_types_js_namespaceObject.MCPAnalyticsEventType.mcpInitialize,
232
+ parameters: (0, external_mcp_payloads_js_namespaceObject.buildCapturedMcpParameters)(request),
233
+ timestamp: new Date()
234
+ };
235
+ await (0, external_tracing_helpers_js_namespaceObject.applyResolvedMetadata)(event, data, request, extra);
236
+ const result = await originalInitializeHandler(request, extra);
237
+ event.response = result;
238
+ (0, external_capture_js_namespaceObject.captureEvent)(server, event);
239
+ return result;
242
240
  }
243
241
  exports.cacheToolCategories = __webpack_exports__.cacheToolCategories;
244
242
  exports.cacheToolDescriptions = __webpack_exports__.cacheToolDescriptions;
245
243
  exports.captureToolCall = __webpack_exports__.captureToolCall;
246
- exports.instrumentInitializeHandler = __webpack_exports__.instrumentInitializeHandler;
247
- exports.instrumentToolsListHandler = __webpack_exports__.instrumentToolsListHandler;
244
+ exports.handleInitializeRequest = __webpack_exports__.handleInitializeRequest;
245
+ exports.handleListToolsRequest = __webpack_exports__.handleListToolsRequest;
246
+ exports.patchRequestHandlers = __webpack_exports__.patchRequestHandlers;
248
247
  exports.readToolMetaCategory = __webpack_exports__.readToolMetaCategory;
249
248
  for(var __webpack_i__ in __webpack_exports__)if (-1 === [
250
249
  "cacheToolCategories",
251
250
  "cacheToolDescriptions",
252
251
  "captureToolCall",
253
- "instrumentInitializeHandler",
254
- "instrumentToolsListHandler",
252
+ "handleInitializeRequest",
253
+ "handleListToolsRequest",
254
+ "patchRequestHandlers",
255
255
  "readToolMetaCategory"
256
256
  ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
257
257
  Object.defineProperty(exports, '__esModule', {
@@ -1,4 +1,3 @@
1
- import { InitializeRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
2
1
  import { addContextParameterToTools, getContextDescription, isContextEnabled } from "./context-parameters.mjs";
3
2
  import { addConversationIdToTools, canInjectConversationIdPromptBack, cloneRequestWithoutConversationId, injectConversationIdPromptBack, resolveConversationId } from "./conversation-id.mjs";
4
3
  import { captureEvent } from "./capture.mjs";
@@ -8,6 +7,7 @@ import { resolveToolCallIntent, setEventIntent, setExplicitContextIntent } from
8
7
  import { getServerTrackingData, handleIdentify } from "./internal.mjs";
9
8
  import { log } from "./logger.mjs";
10
9
  import { buildCapturedMcpParameters } from "./mcp-payloads.mjs";
10
+ import { getLiteralValue, getObjectShape } from "./mcp-sdk-compat.mjs";
11
11
  import { getServerSessionId } from "./session.mjs";
12
12
  import { getReportMissingToolDescriptor, resolveMissingCapabilityToolName } from "./tools.mjs";
13
13
  import { applyResolvedMetadata, isToolResultError } from "./tracing-helpers.mjs";
@@ -85,18 +85,19 @@ function publishFailedToolEvent(server, event, error, startTime, conversation) {
85
85
  log(`Warning: PostHog MCP analytics failed to publish failed tool event - ${publishError}`);
86
86
  }
87
87
  }
88
- const listToolsTracingSetup = new WeakMap();
89
- function instrumentToolsListHandler(server) {
90
- if (!server._capabilities?.tools) return;
91
- if (listToolsTracingSetup.get(server)) return;
92
- const originalListToolsHandler = server._requestHandlers.get('tools/list');
93
- if (!originalListToolsHandler) return;
94
- try {
95
- server.setRequestHandler(ListToolsRequestSchema, async (request, extra)=>await handleListToolsRequest(server, originalListToolsHandler, request, extra));
96
- listToolsTracingSetup.set(server, true);
97
- } catch (error) {
98
- log(`Warning: Failed to override list tools handler - ${error}`);
88
+ function patchRequestHandlers(server, patches) {
89
+ for (const [handlerName, patch] of Object.entries(patches)){
90
+ const originalHandler = server._requestHandlers.get(handlerName);
91
+ if (originalHandler) server._requestHandlers.set(handlerName, (request, extra)=>patch(server, originalHandler, request, extra));
99
92
  }
93
+ const originalSetRequestHandler = server.setRequestHandler.bind(server);
94
+ server.setRequestHandler = (requestSchema, originalHandler)=>{
95
+ const shape = getObjectShape(requestSchema);
96
+ const handlerName = shape?.method ? getLiteralValue(shape.method) : void 0;
97
+ const patch = 'string' == typeof handlerName ? patches[handlerName] : void 0;
98
+ if (!patch) return originalSetRequestHandler(requestSchema, originalHandler);
99
+ return originalSetRequestHandler(requestSchema, (request, extra)=>patch(server, originalHandler, request, extra));
100
+ };
100
101
  }
101
102
  async function handleListToolsRequest(server, originalListToolsHandler, request, extra) {
102
103
  const data = getServerTrackingData(server);
@@ -182,29 +183,25 @@ function cacheToolCategories(cache, tools) {
182
183
  if (category) cache.set(tool.name, category);
183
184
  }
184
185
  }
185
- function instrumentInitializeHandler(server) {
186
- const originalInitializeHandler = server._requestHandlers.get('initialize');
187
- if (!originalInitializeHandler) return;
188
- server.setRequestHandler(InitializeRequestSchema, async (request, extra)=>{
189
- const data = getServerTrackingData(server);
190
- if (!data) {
191
- log('Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called instrument(server, options) before using tool calls.');
192
- return await originalInitializeHandler(request, extra);
193
- }
194
- const sessionId = getServerSessionId(server, extra);
195
- await handleIdentify(server, data, sessionId, request, extra);
196
- const event = {
197
- sessionId,
198
- resourceName: request.params?.name || 'Unknown Tool Name',
199
- eventType: MCPAnalyticsEventType.mcpInitialize,
200
- parameters: buildCapturedMcpParameters(request),
201
- timestamp: new Date()
202
- };
203
- await applyResolvedMetadata(event, data, request, extra);
204
- const result = await originalInitializeHandler(request, extra);
205
- event.response = result;
206
- captureEvent(server, event);
207
- return result;
208
- });
186
+ async function handleInitializeRequest(server, originalInitializeHandler, request, extra) {
187
+ const data = getServerTrackingData(server);
188
+ if (!data) {
189
+ log('Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called instrument(server, options) before using tool calls.');
190
+ return await originalInitializeHandler(request, extra);
191
+ }
192
+ const sessionId = getServerSessionId(server, extra);
193
+ await handleIdentify(server, data, sessionId, request, extra);
194
+ const event = {
195
+ sessionId,
196
+ resourceName: request.params?.name || 'Unknown Tool Name',
197
+ eventType: MCPAnalyticsEventType.mcpInitialize,
198
+ parameters: buildCapturedMcpParameters(request),
199
+ timestamp: new Date()
200
+ };
201
+ await applyResolvedMetadata(event, data, request, extra);
202
+ const result = await originalInitializeHandler(request, extra);
203
+ event.response = result;
204
+ captureEvent(server, event);
205
+ return result;
209
206
  }
210
- export { cacheToolCategories, cacheToolDescriptions, captureToolCall, instrumentInitializeHandler, instrumentToolsListHandler, readToolMetaCategory };
207
+ export { cacheToolCategories, cacheToolDescriptions, captureToolCall, handleInitializeRequest, handleListToolsRequest, patchRequestHandlers, readToolMetaCategory };
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const version = "0.4.1";
1
+ export declare const version = "0.4.3";
2
2
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -26,7 +26,7 @@ __webpack_require__.r(__webpack_exports__);
26
26
  __webpack_require__.d(__webpack_exports__, {
27
27
  version: ()=>version
28
28
  });
29
- const version = '0.4.1';
29
+ const version = '0.4.3';
30
30
  exports.version = __webpack_exports__.version;
31
31
  for(var __webpack_i__ in __webpack_exports__)if (-1 === [
32
32
  "version"
package/dist/version.mjs CHANGED
@@ -1,2 +1,2 @@
1
- const version = '0.4.1';
1
+ const version = '0.4.3';
2
2
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@posthog/mcp",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "bugs": {
5
5
  "url": "https://github.com/PostHog/posthog-js/issues"
6
6
  },
@@ -28,7 +28,7 @@
28
28
  "module": "dist/index.mjs",
29
29
  "types": "dist/index.d.ts",
30
30
  "dependencies": {
31
- "@posthog/core": "^1.36.0"
31
+ "@posthog/core": "^1.38.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@babel/preset-env": "^7.27.1",
@@ -39,8 +39,8 @@
39
39
  "@types/node": "^20.0.0",
40
40
  "jest": "29.7.0",
41
41
  "zod": "^3.25.0",
42
- "@posthog-tooling/tsconfig-base": "1.1.1",
43
- "posthog-node": "^5.38.2"
42
+ "posthog-node": "^5.38.5",
43
+ "@posthog-tooling/tsconfig-base": "1.1.1"
44
44
  },
45
45
  "peerDependencies": {
46
46
  "@modelcontextprotocol/sdk": ">=1.26.0",
@@ -8,11 +8,12 @@ import { stripConversationId } from './conversation-id'
8
8
  import { MCPAnalyticsEventType } from './event-types'
9
9
  import { getServerTrackingData } from './internal'
10
10
  import { log } from './logger'
11
- import { createWrappedTool, getLiteralValue, getObjectShape, getToolFunction, hasToolFunction } from './mcp-sdk-compat'
11
+ import { createWrappedTool, getToolFunction, hasToolFunction } from './mcp-sdk-compat'
12
12
  import { handleReportMissing, resolveMissingCapabilityToolName } from './tools'
13
13
  import {
14
- instrumentInitializeHandler,
15
- instrumentToolsListHandler,
14
+ handleInitializeRequest,
15
+ handleListToolsRequest,
16
+ patchRequestHandlers,
16
17
  captureToolCall,
17
18
  readToolMetaCategory,
18
19
  } from './instrumentation'
@@ -72,8 +73,6 @@ function setupListenerToRegisteredTools(server: HighLevelMCPServerLike): void {
72
73
 
73
74
  const nextValue = addTracingToToolCallbackInternal(value, property, server)
74
75
 
75
- instrumentToolsListHandler(server.server as MCPServerLike)
76
-
77
76
  if (typeof nextValue.update === 'function') {
78
77
  const originalUpdate = nextValue.update
79
78
  nextValue.update = function (...updateArgs: unknown[]) {
@@ -130,7 +129,7 @@ function setupListenerToRegisteredTools(server: HighLevelMCPServerLike): void {
130
129
  * (the high-level SDK turns thrown errors into `isError` results otherwise).
131
130
  *
132
131
  * This is purely the tool-facing concern; event capture lives in
133
- * {@link captureToolCall} via {@link handleWrappedToolsCall}.
132
+ * {@link captureToolCall} via {@link handleToolCallRequest}.
134
133
  */
135
134
  function addTracingToToolCallbackInternal(
136
135
  tool: RegisteredTool,
@@ -202,38 +201,9 @@ function addTracingToToolCallbackInternal(
202
201
  return wrappedTool
203
202
  }
204
203
 
205
- function setupToolsCallHandlerWrapping(server: HighLevelMCPServerLike): void {
206
- const lowLevelServer = server.server as MCPServerLike
207
-
208
- const existingHandler = lowLevelServer._requestHandlers.get('tools/call')
209
- if (existingHandler) {
210
- const wrappedHandler = createToolsCallWrapper(existingHandler, lowLevelServer)
211
- lowLevelServer._requestHandlers.set('tools/call', wrappedHandler)
212
- }
213
-
214
- const originalSetRequestHandler = lowLevelServer.setRequestHandler.bind(lowLevelServer)
215
-
216
- lowLevelServer.setRequestHandler = ((requestSchema: unknown, handler: MCPRequestHandler) => {
217
- const shape = getObjectShape(requestSchema)
218
- const method = shape?.method ? getLiteralValue(shape.method) : undefined
219
-
220
- if (method === 'tools/call') {
221
- const wrappedHandler = createToolsCallWrapper(handler, lowLevelServer)
222
- return originalSetRequestHandler(requestSchema, wrappedHandler)
223
- }
224
-
225
- return originalSetRequestHandler(requestSchema, handler)
226
- }) as MCPServerLike['setRequestHandler']
227
- }
228
-
229
- function createToolsCallWrapper(originalHandler: MCPRequestHandler, server: MCPServerLike): MCPRequestHandler {
230
- return async (request: MCPRequest, extra: MCPRequestExtra) =>
231
- await handleWrappedToolsCall(originalHandler, server, request, extra)
232
- }
233
-
234
- async function handleWrappedToolsCall(
235
- originalHandler: MCPRequestHandler,
204
+ async function handleToolCallRequest(
236
205
  server: MCPServerLike,
206
+ originalCallToolHandler: MCPRequestHandler,
237
207
  request: MCPRequest,
238
208
  extra: MCPRequestExtra
239
209
  ): Promise<unknown> {
@@ -242,7 +212,7 @@ async function handleWrappedToolsCall(
242
212
  log(
243
213
  'Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called instrument(server, options) before using tool calls.'
244
214
  )
245
- return await originalHandler(request, extra)
215
+ return await originalCallToolHandler(request, extra)
246
216
  }
247
217
 
248
218
  if (request.params?.name === resolveMissingCapabilityToolName(data.options)) {
@@ -267,7 +237,7 @@ async function handleWrappedToolsCall(
267
237
  data,
268
238
  request,
269
239
  extra,
270
- execute: () => originalHandler(request, extra),
240
+ execute: () => originalCallToolHandler(request, extra),
271
241
  takeCapturedError: () => {
272
242
  const captured = extra?.__mcp_analytics_error
273
243
  if (extra) {
@@ -280,11 +250,16 @@ async function handleWrappedToolsCall(
280
250
 
281
251
  export function instrumentHighLevelServer(server: HighLevelMCPServerLike): void {
282
252
  try {
283
- const mcpAnalyticsData = getServerTrackingData(server.server)
284
-
285
- setupToolsCallHandlerWrapping(server)
286
-
287
- instrumentInitializeHandler(server.server)
253
+ const lowLevelServer = server.server
254
+ const mcpAnalyticsData = getServerTrackingData(lowLevelServer)
255
+
256
+ // Patch already existing handlers, and patch setRequestHandler to capture dynamically created handlers.
257
+ const handlers = {
258
+ initialize: handleInitializeRequest,
259
+ 'tools/list': handleListToolsRequest,
260
+ 'tools/call': handleToolCallRequest,
261
+ }
262
+ patchRequestHandlers(lowLevelServer, handlers)
288
263
 
289
264
  server._registeredTools = addTracingToToolRegistry(server._registeredTools, server)
290
265
 
@@ -293,8 +268,6 @@ export function instrumentHighLevelServer(server: HighLevelMCPServerLike): void
293
268
  seedToolCategoriesFromRegistry(mcpAnalyticsData.toolCategories, server._registeredTools)
294
269
  }
295
270
 
296
- instrumentToolsListHandler(server.server)
297
-
298
271
  setupListenerToRegisteredTools(server)
299
272
  } catch (error) {
300
273
  log(`Warning: Failed to setup tool call instrumentation - ${error}`)
@@ -8,7 +8,12 @@ import { MCPAnalyticsEventType } from './event-types'
8
8
  import { getServerTrackingData } from './internal'
9
9
  import { log } from './logger'
10
10
  import { handleReportMissing, resolveMissingCapabilityToolName } from './tools'
11
- import { instrumentInitializeHandler, instrumentToolsListHandler, captureToolCall } from './instrumentation'
11
+ import {
12
+ handleInitializeRequest,
13
+ handleListToolsRequest,
14
+ patchRequestHandlers,
15
+ captureToolCall,
16
+ } from './instrumentation'
12
17
  import { getContextArgument } from './tracing-helpers'
13
18
 
14
19
  type MCPRequestHandler = NonNullable<
@@ -24,8 +29,12 @@ type MCPRequestExtra = Parameters<MCPRequestHandler>[1]
24
29
  */
25
30
  export function instrumentLowLevelServer(server: MCPServerLike): void {
26
31
  try {
27
- instrumentInitializeHandler(server)
28
- instrumentToolsListHandler(server)
32
+ // Patch already existing handlers, and patch setRequestHandler to capture dynamically created handlers.
33
+ const handlers = {
34
+ initialize: handleInitializeRequest,
35
+ 'tools/list': handleListToolsRequest,
36
+ }
37
+ patchRequestHandlers(server, handlers)
29
38
 
30
39
  const originalCallToolHandler = server._requestHandlers.get('tools/call')
31
40
  server.setRequestHandler(
@@ -2,11 +2,7 @@
2
2
  // Copyright (c) 2025 MCPcat
3
3
  // Licensed under the MIT License: https://github.com/MCPCat/mcpcat-typescript-sdk/blob/main/LICENSE
4
4
 
5
- import {
6
- InitializeRequestSchema,
7
- ListToolsRequestSchema,
8
- type ListToolsResult,
9
- } from '@modelcontextprotocol/sdk/types.js'
5
+ import type { ListToolsResult } from '@modelcontextprotocol/sdk/types.js'
10
6
  import type { CompatibleRequestHandlerExtra, MCPAnalyticsData, MCPRequestLike, MCPServerLike, McpEvent } from '../types'
11
7
  import { addContextParameterToTools, getContextDescription, isContextEnabled } from './context-parameters'
12
8
  import {
@@ -24,6 +20,7 @@ import { resolveToolCallIntent, setEventIntent, setExplicitContextIntent } from
24
20
  import { getServerTrackingData, handleIdentify } from './internal'
25
21
  import { log } from './logger'
26
22
  import { buildCapturedMcpParameters } from './mcp-payloads'
23
+ import { getLiteralValue, getObjectShape } from './mcp-sdk-compat'
27
24
  import { getServerSessionId } from './session'
28
25
  import { getReportMissingToolDescriptor, resolveMissingCapabilityToolName } from './tools'
29
26
  import { applyResolvedMetadata, isToolResultError } from './tracing-helpers'
@@ -231,45 +228,53 @@ function publishFailedToolEvent(
231
228
 
232
229
  // --- tools/list -----------------------------------------------------------
233
230
 
234
- type MCPServerWithCapabilities = MCPServerLike & {
235
- _capabilities?: {
236
- tools?: unknown
237
- }
238
- }
239
-
240
- const listToolsTracingSetup = new WeakMap<MCPServerLike, boolean>()
231
+ /**
232
+ * A method's patch: runs the original handler and captures analytics. `server`
233
+ * and `originalHandler` are bound by {@link patchRequestHandlers}; the SDK
234
+ * supplies `request` and `extra` per call.
235
+ */
236
+ export type HandlerPatch = (
237
+ server: MCPServerLike,
238
+ originalHandler: MCPRequestHandler,
239
+ request: MCPRequestLike,
240
+ extra: CompatibleRequestHandlerExtra | undefined
241
+ ) => Promise<unknown>
241
242
 
242
243
  /**
243
- * Wraps the server's `tools/list` handler so each listing is captured and the
244
- * SDK-managed tools (context parameter, conversation id, `get_more_tools`) are
245
- * injected. Idempotent per server. Works for both low-level and high-level
246
- * servers — the high-level wrapper passes its underlying `server`.
244
+ * Applies the `patches` (keyed by method, e.g. `initialize`, `tools/list`) to the
245
+ * handlers already registered, and patches `setRequestHandler` so matching
246
+ * handlers registered later are patched too. The latter is what makes adapters
247
+ * that register handlers post-construction work — e.g. `@rekog/mcp-nest` hands a
248
+ * bare server to instrument() and only then registers its handlers.
247
249
  */
248
- export function instrumentToolsListHandler(server: MCPServerLike): void {
249
- if (!(server as MCPServerWithCapabilities)._capabilities?.tools) {
250
- return
251
- }
252
- if (listToolsTracingSetup.get(server)) {
253
- return
250
+ export function patchRequestHandlers(server: MCPServerLike, patches: Record<string, HandlerPatch>): void {
251
+ // Monkey patch existing handlers.
252
+ for (const [handlerName, patch] of Object.entries(patches)) {
253
+ const originalHandler = server._requestHandlers.get(handlerName)
254
+ if (originalHandler) {
255
+ server._requestHandlers.set(handlerName, (request, extra) => patch(server, originalHandler, request, extra))
256
+ }
254
257
  }
255
258
 
256
- const originalListToolsHandler = server._requestHandlers.get('tools/list')
257
- if (!originalListToolsHandler) {
258
- return
259
- }
259
+ // Monkey patch dynamically added handlers (registered after instrument()).
260
+ const originalSetRequestHandler = server.setRequestHandler.bind(server)
261
+ server.setRequestHandler = ((requestSchema: unknown, originalHandler: MCPRequestHandler) => {
262
+ const shape = getObjectShape(requestSchema)
263
+ const handlerName = shape?.method ? getLiteralValue(shape.method) : undefined
264
+ const patch = typeof handlerName === 'string' ? patches[handlerName] : undefined
265
+ if (!patch) {
266
+ return originalSetRequestHandler(requestSchema, originalHandler)
267
+ }
260
268
 
261
- try {
262
- server.setRequestHandler(
263
- ListToolsRequestSchema,
264
- async (request, extra) => await handleListToolsRequest(server, originalListToolsHandler, request, extra)
265
- )
266
- listToolsTracingSetup.set(server, true)
267
- } catch (error) {
268
- log(`Warning: Failed to override list tools handler - ${error}`)
269
- }
269
+ return originalSetRequestHandler(requestSchema, (request, extra) => patch(server, originalHandler, request, extra))
270
+ }) as MCPServerLike['setRequestHandler']
270
271
  }
271
272
 
272
- async function handleListToolsRequest(
273
+ /**
274
+ * Captures each `tools/list` and injects the SDK-managed tools (context
275
+ * parameter, conversation id, `get_more_tools`) into the returned list.
276
+ */
277
+ export async function handleListToolsRequest(
273
278
  server: MCPServerLike,
274
279
  originalListToolsHandler: MCPRequestHandler,
275
280
  request: MCPRequestLike,
@@ -406,40 +411,38 @@ export function cacheToolCategories(cache: Map<string, string>, tools: ListTools
406
411
  // --- initialize -----------------------------------------------------------
407
412
 
408
413
  /**
409
- * Wraps the server's `initialize` handler so the connection handshake is
410
- * captured (and identity resolved) before the original handler runs.
414
+ * Captures the connection handshake (and resolves identity) on `initialize`
415
+ * before the original handler runs.
411
416
  */
412
- export function instrumentInitializeHandler(server: MCPServerLike): void {
413
- const originalInitializeHandler = server._requestHandlers.get('initialize')
414
- if (!originalInitializeHandler) {
415
- return
417
+ export async function handleInitializeRequest(
418
+ server: MCPServerLike,
419
+ originalInitializeHandler: MCPRequestHandler,
420
+ request: MCPRequestLike,
421
+ extra?: CompatibleRequestHandlerExtra
422
+ ): Promise<unknown> {
423
+ const data = getServerTrackingData(server)
424
+ if (!data) {
425
+ log(
426
+ 'Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called instrument(server, options) before using tool calls.'
427
+ )
428
+ return await originalInitializeHandler(request, extra)
416
429
  }
417
430
 
418
- server.setRequestHandler(InitializeRequestSchema, async (request, extra) => {
419
- const data = getServerTrackingData(server)
420
- if (!data) {
421
- log(
422
- 'Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called instrument(server, options) before using tool calls.'
423
- )
424
- return await originalInitializeHandler(request, extra)
425
- }
426
-
427
- const sessionId = getServerSessionId(server, extra)
428
- await handleIdentify(server, data, sessionId, request, extra)
431
+ const sessionId = getServerSessionId(server, extra)
432
+ await handleIdentify(server, data, sessionId, request, extra)
429
433
 
430
- const event: McpEvent = {
431
- sessionId,
432
- resourceName: request.params?.name || 'Unknown Tool Name',
433
- eventType: MCPAnalyticsEventType.mcpInitialize,
434
- parameters: buildCapturedMcpParameters(request),
435
- timestamp: new Date(),
436
- }
434
+ const event: McpEvent = {
435
+ sessionId,
436
+ resourceName: request.params?.name || 'Unknown Tool Name',
437
+ eventType: MCPAnalyticsEventType.mcpInitialize,
438
+ parameters: buildCapturedMcpParameters(request),
439
+ timestamp: new Date(),
440
+ }
437
441
 
438
- await applyResolvedMetadata(event, data, request, extra)
442
+ await applyResolvedMetadata(event, data, request, extra)
439
443
 
440
- const result = await originalInitializeHandler(request, extra)
441
- event.response = result
442
- captureEvent(server, event)
443
- return result
444
- })
444
+ const result = await originalInitializeHandler(request, extra)
445
+ event.response = result
446
+ captureEvent(server, event)
447
+ return result
445
448
  }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '0.4.1'
1
+ export const version = '0.4.3'