@posthog/mcp 0.10.5 → 0.10.6
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.
- package/dist/extensions/context-parameters.d.ts.map +1 -1
- package/dist/extensions/context-parameters.js +1 -4
- package/dist/extensions/context-parameters.mjs +1 -4
- package/dist/extensions/conversation-id.d.ts +1 -1
- package/dist/extensions/conversation-id.d.ts.map +1 -1
- package/dist/extensions/conversation-id.js +2 -2
- package/dist/extensions/conversation-id.mjs +2 -2
- package/dist/extensions/instrument-highlevel.d.ts.map +1 -1
- package/dist/extensions/instrument-highlevel.js +5 -5
- package/dist/extensions/instrument-highlevel.mjs +6 -6
- package/dist/extensions/instrument-lowlevel.d.ts.map +1 -1
- package/dist/extensions/instrument-lowlevel.js +9 -4
- package/dist/extensions/instrument-lowlevel.mjs +10 -5
- package/dist/extensions/instrumentation.d.ts +6 -6
- package/dist/extensions/instrumentation.d.ts.map +1 -1
- package/dist/extensions/instrumentation.js +60 -15
- package/dist/extensions/instrumentation.mjs +56 -14
- package/dist/extensions/intent.d.ts.map +1 -1
- package/dist/extensions/intent.js +1 -1
- package/dist/extensions/intent.mjs +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.mjs +1 -1
- package/package.json +3 -3
- package/src/extensions/context-parameters.ts +1 -7
- package/src/extensions/conversation-id.ts +2 -1
- package/src/extensions/instrument-highlevel.ts +8 -13
- package/src/extensions/instrument-lowlevel.ts +17 -6
- package/src/extensions/instrumentation.ts +90 -18
- package/src/extensions/intent.ts +1 -6
- package/src/version.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-parameters.d.ts","sourceRoot":"","sources":["../../src/extensions/context-parameters.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,EAGL,KAAK,6BAA6B,EACnC,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAO,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAA;AAE7C,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,6BAA6B,CAAA;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,mBAAmB,CAAC,SAAS,CAAC,GAAG,OAAO,CAEjF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,mBAAmB,CAAC,SAAS,CAAC,GAAG,MAAM,GAAG,SAAS,CAEjG;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,SAAS,qBAAqB,EAC3E,IAAI,EAAE,KAAK,EACX,0BAA0B,CAAC,EAAE,MAAM,EACnC,MAAM,GAAE,QAAc,GACrB,KAAK,CA6DP;AAED,wBAAgB,0BAA0B,CAAC,KAAK,SAAS,qBAAqB,EAC5E,KAAK,EAAE,KAAK,EAAE,EACd,0BAA0B,CAAC,EAAE,MAAM,EACnC,MAAM,GAAE,QAAc,GACrB,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"context-parameters.d.ts","sourceRoot":"","sources":["../../src/extensions/context-parameters.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,EAGL,KAAK,6BAA6B,EACnC,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAO,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAA;AAE7C,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,6BAA6B,CAAA;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,mBAAmB,CAAC,SAAS,CAAC,GAAG,OAAO,CAEjF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,mBAAmB,CAAC,SAAS,CAAC,GAAG,MAAM,GAAG,SAAS,CAEjG;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,SAAS,qBAAqB,EAC3E,IAAI,EAAE,KAAK,EACX,0BAA0B,CAAC,EAAE,MAAM,EACnC,MAAM,GAAE,QAAc,GACrB,KAAK,CA6DP;AAED,wBAAgB,0BAA0B,CAAC,KAAK,SAAS,qBAAqB,EAC5E,KAAK,EAAE,KAAK,EAAE,EACd,0BAA0B,CAAC,EAAE,MAAM,EACnC,MAAM,GAAE,QAAc,GACrB,KAAK,EAAE,CAET"}
|
|
@@ -70,10 +70,7 @@ function addContextParameterToTool(tool, contextDescriptionOverride, logger = ex
|
|
|
70
70
|
return modifiedTool;
|
|
71
71
|
}
|
|
72
72
|
function addContextParameterToTools(tools, contextDescriptionOverride, logger = external_logger_js_namespaceObject.log) {
|
|
73
|
-
return tools.map((tool)=>
|
|
74
|
-
if ('get_more_tools' === tool.name) return tool;
|
|
75
|
-
return addContextParameterToTool(tool, contextDescriptionOverride, logger);
|
|
76
|
-
});
|
|
73
|
+
return tools.map((tool)=>addContextParameterToTool(tool, contextDescriptionOverride, logger));
|
|
77
74
|
}
|
|
78
75
|
exports.addContextParameterToTool = __webpack_exports__.addContextParameterToTool;
|
|
79
76
|
exports.addContextParameterToTools = __webpack_exports__.addContextParameterToTools;
|
|
@@ -39,9 +39,6 @@ function addContextParameterToTool(tool, contextDescriptionOverride, logger = lo
|
|
|
39
39
|
return modifiedTool;
|
|
40
40
|
}
|
|
41
41
|
function addContextParameterToTools(tools, contextDescriptionOverride, logger = log) {
|
|
42
|
-
return tools.map((tool)=>
|
|
43
|
-
if ('get_more_tools' === tool.name) return tool;
|
|
44
|
-
return addContextParameterToTool(tool, contextDescriptionOverride, logger);
|
|
45
|
-
});
|
|
42
|
+
return tools.map((tool)=>addContextParameterToTool(tool, contextDescriptionOverride, logger));
|
|
46
43
|
}
|
|
47
44
|
export { addContextParameterToTool, addContextParameterToTools, getContextDescription, isContextEnabled };
|
|
@@ -7,7 +7,7 @@ export interface ConversationIdInjectableTool {
|
|
|
7
7
|
[key: string]: unknown;
|
|
8
8
|
}
|
|
9
9
|
export declare function addConversationIdToTool<TTool extends ConversationIdInjectableTool>(tool: TTool, logger?: LoggerFn): TTool;
|
|
10
|
-
export declare function addConversationIdToTools<TTool extends ConversationIdInjectableTool>(tools: TTool[], missingCapabilityToolName?: string, logger?: LoggerFn): TTool[];
|
|
10
|
+
export declare function addConversationIdToTools<TTool extends ConversationIdInjectableTool>(tools: TTool[], missingCapabilityToolName?: string, skipMissingCapabilityTool?: boolean, logger?: LoggerFn): TTool[];
|
|
11
11
|
export type ConversationIdResolution = {
|
|
12
12
|
minted: false;
|
|
13
13
|
conversationId: string | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-id.d.ts","sourceRoot":"","sources":["../../src/extensions/conversation-id.ts"],"names":[],"mappings":"AAMA,OAAO,EAGL,KAAK,6BAA6B,EACnC,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAO,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAA;AAG7C,eAAO,MAAM,0BAA0B,oBAAoB,CAAA;AAE3D,MAAM,WAAW,4BAA4B;IAC3C,WAAW,CAAC,EAAE,6BAA6B,CAAA;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,wBAAgB,uBAAuB,CAAC,KAAK,SAAS,4BAA4B,EAChF,IAAI,EAAE,KAAK,EACX,MAAM,GAAE,QAAc,GACrB,KAAK,CA4CP;AAED,wBAAgB,wBAAwB,CAAC,KAAK,SAAS,4BAA4B,EACjF,KAAK,EAAE,KAAK,EAAE,EACd,yBAAyB,GAAE,MAA4B,EACvD,MAAM,GAAE,QAAc,GACrB,KAAK,EAAE,CAOT;AAED,MAAM,MAAM,wBAAwB,GAChC;IAAE,MAAM,EAAE,KAAK,CAAC;IAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACrD;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAA;AAE5C;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,yBAAyB,GAAE,MAA4B,GACtD,wBAAwB,CAS1B;AAED;;;;GAIG;AACH,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAS1E;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAUvE;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAM1D;AAED,wBAAgB,6BAA6B,CAAC,cAAc,EAAE,MAAM,GAAG;IACrE,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb,CAKA;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAS/F"}
|
|
1
|
+
{"version":3,"file":"conversation-id.d.ts","sourceRoot":"","sources":["../../src/extensions/conversation-id.ts"],"names":[],"mappings":"AAMA,OAAO,EAGL,KAAK,6BAA6B,EACnC,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAO,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAA;AAG7C,eAAO,MAAM,0BAA0B,oBAAoB,CAAA;AAE3D,MAAM,WAAW,4BAA4B;IAC3C,WAAW,CAAC,EAAE,6BAA6B,CAAA;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,wBAAgB,uBAAuB,CAAC,KAAK,SAAS,4BAA4B,EAChF,IAAI,EAAE,KAAK,EACX,MAAM,GAAE,QAAc,GACrB,KAAK,CA4CP;AAED,wBAAgB,wBAAwB,CAAC,KAAK,SAAS,4BAA4B,EACjF,KAAK,EAAE,KAAK,EAAE,EACd,yBAAyB,GAAE,MAA4B,EACvD,yBAAyB,UAAO,EAChC,MAAM,GAAE,QAAc,GACrB,KAAK,EAAE,CAOT;AAED,MAAM,MAAM,wBAAwB,GAChC;IAAE,MAAM,EAAE,KAAK,CAAC;IAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACrD;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,CAAA;AAE5C;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,yBAAyB,GAAE,MAA4B,GACtD,wBAAwB,CAS1B;AAED;;;;GAIG;AACH,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAS1E;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAUvE;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAM1D;AAED,wBAAgB,6BAA6B,CAAC,cAAc,EAAE,MAAM,GAAG;IACrE,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb,CAKA;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAS/F"}
|
|
@@ -65,9 +65,9 @@ function addConversationIdToTool(tool, logger = external_logger_js_namespaceObje
|
|
|
65
65
|
};
|
|
66
66
|
return modifiedTool;
|
|
67
67
|
}
|
|
68
|
-
function addConversationIdToTools(tools, missingCapabilityToolName = external_tools_js_namespaceObject.GET_MORE_TOOLS_NAME, logger = external_logger_js_namespaceObject.log) {
|
|
68
|
+
function addConversationIdToTools(tools, missingCapabilityToolName = external_tools_js_namespaceObject.GET_MORE_TOOLS_NAME, skipMissingCapabilityTool = true, logger = external_logger_js_namespaceObject.log) {
|
|
69
69
|
return tools.map((tool)=>{
|
|
70
|
-
if (tool.name === missingCapabilityToolName) return tool;
|
|
70
|
+
if (skipMissingCapabilityTool && tool.name === missingCapabilityToolName) return tool;
|
|
71
71
|
return addConversationIdToTool(tool, logger);
|
|
72
72
|
});
|
|
73
73
|
}
|
|
@@ -29,9 +29,9 @@ function addConversationIdToTool(tool, logger = log) {
|
|
|
29
29
|
};
|
|
30
30
|
return modifiedTool;
|
|
31
31
|
}
|
|
32
|
-
function addConversationIdToTools(tools, missingCapabilityToolName = GET_MORE_TOOLS_NAME, logger = log) {
|
|
32
|
+
function addConversationIdToTools(tools, missingCapabilityToolName = GET_MORE_TOOLS_NAME, skipMissingCapabilityTool = true, logger = log) {
|
|
33
33
|
return tools.map((tool)=>{
|
|
34
|
-
if (tool.name === missingCapabilityToolName) return tool;
|
|
34
|
+
if (skipMissingCapabilityTool && tool.name === missingCapabilityToolName) return tool;
|
|
35
35
|
return addConversationIdToTool(tool, logger);
|
|
36
36
|
});
|
|
37
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrument-highlevel.d.ts","sourceRoot":"","sources":["../../src/extensions/instrument-highlevel.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAEV,sBAAsB,EAIvB,MAAM,UAAU,CAAA;AASjB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"instrument-highlevel.d.ts","sourceRoot":"","sources":["../../src/extensions/instrument-highlevel.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAEV,sBAAsB,EAIvB,MAAM,UAAU,CAAA;AASjB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AA8PxC,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,sBAAsB,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,CA2BhG"}
|
|
@@ -107,7 +107,6 @@ function setupListenerToRegisteredTools(server, logger) {
|
|
|
107
107
|
function addTracingToToolCallbackInternal(tool, toolName, server, getCurrentInputSchema, logger) {
|
|
108
108
|
const originalCallback = (0, external_mcp_sdk_compat_js_namespaceObject.getToolFunction)(tool);
|
|
109
109
|
const options = (0, external_internal_js_namespaceObject.getServerTrackingData)(server.server)?.options;
|
|
110
|
-
const missingToolName = (0, external_tools_js_namespaceObject.resolveMissingCapabilityToolName)(options);
|
|
111
110
|
if (wrappedCallbacks.has(originalCallback)) {
|
|
112
111
|
logger(`Tool ${toolName} callback already wrapped, skipping re-wrap`);
|
|
113
112
|
return tool;
|
|
@@ -128,8 +127,8 @@ function addTracingToToolCallbackInternal(tool, toolName, server, getCurrentInpu
|
|
|
128
127
|
}
|
|
129
128
|
const inputSchema = getCurrentInputSchema();
|
|
130
129
|
const cleanedArgs = (0, external_analytics_parameters_js_namespaceObject.stripOwnedAnalyticsArguments)(args, {
|
|
131
|
-
context:
|
|
132
|
-
conversationId:
|
|
130
|
+
context: (0, external_context_parameters_js_namespaceObject.isContextEnabled)(options?.context) && (0, external_analytics_parameters_js_namespaceObject.analyticsOwnsParameter)(inputSchema, 'context'),
|
|
131
|
+
conversationId: options?.enableConversationId === true && (0, external_analytics_parameters_js_namespaceObject.analyticsOwnsParameter)(inputSchema, 'conversation_id')
|
|
133
132
|
});
|
|
134
133
|
try {
|
|
135
134
|
if (void 0 === cleanedArgs) {
|
|
@@ -155,7 +154,9 @@ async function handleToolCallRequest(highLevelServer, server, originalCallToolHa
|
|
|
155
154
|
logger('Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called instrument(server, options) before using tool calls.');
|
|
156
155
|
return await originalCallToolHandler(request, extra);
|
|
157
156
|
}
|
|
158
|
-
|
|
157
|
+
const toolName = request.params?.name;
|
|
158
|
+
const isMissingCapabilityCandidate = data.options.reportMissing && toolName === (0, external_tools_js_namespaceObject.resolveMissingCapabilityToolName)(data.options);
|
|
159
|
+
if (isMissingCapabilityCandidate && await (0, external_instrumentation_js_namespaceObject.isToolAdvertised)(server, toolName, extra, data.logger) === false) {
|
|
159
160
|
const context = (0, external_tracing_helpers_js_namespaceObject.getContextArgument)(request) || '';
|
|
160
161
|
return await (0, external_instrumentation_js_namespaceObject.captureToolCall)({
|
|
161
162
|
server,
|
|
@@ -169,7 +170,6 @@ async function handleToolCallRequest(highLevelServer, server, originalCallToolHa
|
|
|
169
170
|
}, data.logger)
|
|
170
171
|
});
|
|
171
172
|
}
|
|
172
|
-
const toolName = request.params?.name;
|
|
173
173
|
const registeredTool = toolName ? highLevelServer._registeredTools[toolName] : void 0;
|
|
174
174
|
return await (0, external_instrumentation_js_namespaceObject.captureToolCall)({
|
|
175
175
|
server,
|
|
@@ -4,7 +4,7 @@ import { MCPAnalyticsEventType } from "./event-types.mjs";
|
|
|
4
4
|
import { getServerTrackingData } from "./internal.mjs";
|
|
5
5
|
import { createWrappedTool, getToolFunction, hasToolFunction } from "./mcp-sdk-compat.mjs";
|
|
6
6
|
import { handleReportMissing, resolveMissingCapabilityToolName } from "./tools.mjs";
|
|
7
|
-
import { captureToolCall, handleInitializeRequest, handleListToolsRequest, patchRequestHandlers, readToolMetaCategory } from "./instrumentation.mjs";
|
|
7
|
+
import { captureToolCall, handleInitializeRequest, handleListToolsRequest, isToolAdvertised, 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__');
|
|
@@ -79,7 +79,6 @@ function setupListenerToRegisteredTools(server, logger) {
|
|
|
79
79
|
function addTracingToToolCallbackInternal(tool, toolName, server, getCurrentInputSchema, logger) {
|
|
80
80
|
const originalCallback = getToolFunction(tool);
|
|
81
81
|
const options = getServerTrackingData(server.server)?.options;
|
|
82
|
-
const missingToolName = resolveMissingCapabilityToolName(options);
|
|
83
82
|
if (wrappedCallbacks.has(originalCallback)) {
|
|
84
83
|
logger(`Tool ${toolName} callback already wrapped, skipping re-wrap`);
|
|
85
84
|
return tool;
|
|
@@ -100,8 +99,8 @@ function addTracingToToolCallbackInternal(tool, toolName, server, getCurrentInpu
|
|
|
100
99
|
}
|
|
101
100
|
const inputSchema = getCurrentInputSchema();
|
|
102
101
|
const cleanedArgs = stripOwnedAnalyticsArguments(args, {
|
|
103
|
-
context:
|
|
104
|
-
conversationId:
|
|
102
|
+
context: isContextEnabled(options?.context) && analyticsOwnsParameter(inputSchema, 'context'),
|
|
103
|
+
conversationId: options?.enableConversationId === true && analyticsOwnsParameter(inputSchema, 'conversation_id')
|
|
105
104
|
});
|
|
106
105
|
try {
|
|
107
106
|
if (void 0 === cleanedArgs) {
|
|
@@ -127,7 +126,9 @@ async function handleToolCallRequest(highLevelServer, server, originalCallToolHa
|
|
|
127
126
|
logger('Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called instrument(server, options) before using tool calls.');
|
|
128
127
|
return await originalCallToolHandler(request, extra);
|
|
129
128
|
}
|
|
130
|
-
|
|
129
|
+
const toolName = request.params?.name;
|
|
130
|
+
const isMissingCapabilityCandidate = data.options.reportMissing && toolName === resolveMissingCapabilityToolName(data.options);
|
|
131
|
+
if (isMissingCapabilityCandidate && await isToolAdvertised(server, toolName, extra, data.logger) === false) {
|
|
131
132
|
const context = getContextArgument(request) || '';
|
|
132
133
|
return await captureToolCall({
|
|
133
134
|
server,
|
|
@@ -141,7 +142,6 @@ async function handleToolCallRequest(highLevelServer, server, originalCallToolHa
|
|
|
141
142
|
}, data.logger)
|
|
142
143
|
});
|
|
143
144
|
}
|
|
144
|
-
const toolName = request.params?.name;
|
|
145
145
|
const registeredTool = toolName ? highLevelServer._registeredTools[toolName] : void 0;
|
|
146
146
|
return await captureToolCall({
|
|
147
147
|
server,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrument-lowlevel.d.ts","sourceRoot":"","sources":["../../src/extensions/instrument-lowlevel.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAiD,aAAa,EAAE,MAAM,UAAU,CAAA;AAG5F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"instrument-lowlevel.d.ts","sourceRoot":"","sources":["../../src/extensions/instrument-lowlevel.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAiD,aAAa,EAAE,MAAM,UAAU,CAAA;AAG5F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAkBxC;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,CA0BtF"}
|
|
@@ -34,13 +34,16 @@ const external_instrumentation_js_namespaceObject = require("./instrumentation.j
|
|
|
34
34
|
const external_tracing_helpers_js_namespaceObject = require("./tracing-helpers.js");
|
|
35
35
|
function instrumentLowLevelServer(server, logger) {
|
|
36
36
|
try {
|
|
37
|
+
const hadCallToolHandler = server._requestHandlers.has('tools/call');
|
|
37
38
|
const handlers = {
|
|
38
39
|
initialize: (server, originalHandler, request, extra)=>(0, external_instrumentation_js_namespaceObject.handleInitializeRequest)(server, originalHandler, request, extra, logger),
|
|
39
|
-
'tools/list': (server, originalHandler, request, extra)=>(0, external_instrumentation_js_namespaceObject.handleListToolsRequest)(server, originalHandler, request, extra, logger)
|
|
40
|
+
'tools/list': (server, originalHandler, request, extra)=>(0, external_instrumentation_js_namespaceObject.handleListToolsRequest)(server, originalHandler, request, extra, logger),
|
|
41
|
+
'tools/call': (server, originalHandler, request, extra)=>handleToolCallRequest(server, originalHandler, request, extra, logger)
|
|
40
42
|
};
|
|
41
43
|
(0, external_instrumentation_js_namespaceObject.patchRequestHandlers)(server, handlers);
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
if (!hadCallToolHandler) server.setRequestHandler(types_js_namespaceObject.CallToolRequestSchema, async (request)=>{
|
|
45
|
+
throw new Error(`Unknown tool: ${request.params?.name || 'unknown'}`);
|
|
46
|
+
});
|
|
44
47
|
} catch (error) {
|
|
45
48
|
logger(`Warning: Failed to setup tool call instrumentation - ${error}`);
|
|
46
49
|
throw error;
|
|
@@ -52,7 +55,9 @@ async function handleToolCallRequest(server, originalCallToolHandler, request, e
|
|
|
52
55
|
logger('Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called instrument(server, options) before using tool calls.');
|
|
53
56
|
return await originalCallToolHandler?.(request, extra);
|
|
54
57
|
}
|
|
55
|
-
|
|
58
|
+
const toolName = request.params?.name;
|
|
59
|
+
const isMissingCapabilityCandidate = data.options.reportMissing && toolName === (0, external_tools_js_namespaceObject.resolveMissingCapabilityToolName)(data.options);
|
|
60
|
+
if (isMissingCapabilityCandidate && await (0, external_instrumentation_js_namespaceObject.isToolAdvertised)(server, toolName, extra, data.logger) === false) {
|
|
56
61
|
const context = (0, external_tracing_helpers_js_namespaceObject.getContextArgument)(request) || '';
|
|
57
62
|
return await (0, external_instrumentation_js_namespaceObject.captureToolCall)({
|
|
58
63
|
server,
|
|
@@ -2,17 +2,20 @@ import { CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
|
2
2
|
import { MCPAnalyticsEventType } from "./event-types.mjs";
|
|
3
3
|
import { getServerTrackingData } from "./internal.mjs";
|
|
4
4
|
import { handleReportMissing, resolveMissingCapabilityToolName } from "./tools.mjs";
|
|
5
|
-
import { captureToolCall, handleInitializeRequest, handleListToolsRequest, patchRequestHandlers } from "./instrumentation.mjs";
|
|
5
|
+
import { captureToolCall, handleInitializeRequest, handleListToolsRequest, isToolAdvertised, patchRequestHandlers } from "./instrumentation.mjs";
|
|
6
6
|
import { getContextArgument } from "./tracing-helpers.mjs";
|
|
7
7
|
function instrumentLowLevelServer(server, logger) {
|
|
8
8
|
try {
|
|
9
|
+
const hadCallToolHandler = server._requestHandlers.has('tools/call');
|
|
9
10
|
const handlers = {
|
|
10
11
|
initialize: (server, originalHandler, request, extra)=>handleInitializeRequest(server, originalHandler, request, extra, logger),
|
|
11
|
-
'tools/list': (server, originalHandler, request, extra)=>handleListToolsRequest(server, originalHandler, request, extra, logger)
|
|
12
|
+
'tools/list': (server, originalHandler, request, extra)=>handleListToolsRequest(server, originalHandler, request, extra, logger),
|
|
13
|
+
'tools/call': (server, originalHandler, request, extra)=>handleToolCallRequest(server, originalHandler, request, extra, logger)
|
|
12
14
|
};
|
|
13
15
|
patchRequestHandlers(server, handlers);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
if (!hadCallToolHandler) server.setRequestHandler(CallToolRequestSchema, async (request)=>{
|
|
17
|
+
throw new Error(`Unknown tool: ${request.params?.name || 'unknown'}`);
|
|
18
|
+
});
|
|
16
19
|
} catch (error) {
|
|
17
20
|
logger(`Warning: Failed to setup tool call instrumentation - ${error}`);
|
|
18
21
|
throw error;
|
|
@@ -24,7 +27,9 @@ async function handleToolCallRequest(server, originalCallToolHandler, request, e
|
|
|
24
27
|
logger('Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called instrument(server, options) before using tool calls.');
|
|
25
28
|
return await originalCallToolHandler?.(request, extra);
|
|
26
29
|
}
|
|
27
|
-
|
|
30
|
+
const toolName = request.params?.name;
|
|
31
|
+
const isMissingCapabilityCandidate = data.options.reportMissing && toolName === resolveMissingCapabilityToolName(data.options);
|
|
32
|
+
if (isMissingCapabilityCandidate && await isToolAdvertised(server, toolName, extra, data.logger) === false) {
|
|
28
33
|
const context = getContextArgument(request) || '';
|
|
29
34
|
return await captureToolCall({
|
|
30
35
|
server,
|
|
@@ -55,14 +55,14 @@ export declare function captureToolCall(params: TraceToolCallParams): Promise<un
|
|
|
55
55
|
* supplies `request` and `extra` per call.
|
|
56
56
|
*/
|
|
57
57
|
export type HandlerPatch = (server: MCPServerLike, originalHandler: MCPRequestHandler, request: MCPRequestLike, extra: CompatibleRequestHandlerExtra | undefined) => Promise<unknown>;
|
|
58
|
+
export declare function patchRequestHandlers(server: MCPServerLike, patches: Record<string, HandlerPatch>): void;
|
|
58
59
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* bare server to instrument() and only then registers its handlers.
|
|
60
|
+
* Checks the server's raw listing for a real owner of a candidate virtual tool.
|
|
61
|
+
* This does not depend on a previous client request and does not call the
|
|
62
|
+
* instrumented list wrapper, so it neither injects PostHog tools nor captures a
|
|
63
|
+
* synthetic tools/list event. `undefined` fails open to the real dispatcher.
|
|
64
64
|
*/
|
|
65
|
-
export declare function
|
|
65
|
+
export declare function isToolAdvertised(server: MCPServerLike, toolName: string, extra: CompatibleRequestHandlerExtra | undefined, logger: LoggerFn): Promise<boolean | undefined>;
|
|
66
66
|
/**
|
|
67
67
|
* Captures each `tools/list` and injects the SDK-managed tools (context
|
|
68
68
|
* parameter, conversation id, `get_more_tools`) into the returned list.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../src/extensions/instrumentation.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,KAAK,EACV,2BAA2B,EAC3B,6BAA6B,EAC7B,gBAAgB,EAChB,cAAc,EACd,aAAa,EAGd,MAAM,UAAU,CAAA;AAYjB,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAIrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAQxC;;;;;;GAMG;AAEH,KAAK,iBAAiB,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE,6BAA6B,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AAE7G,2EAA2E;AAC3E,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,2FAA2F;IAC3F,kBAAkB,CAAC,EAAE,2BAA2B,CAAA;IAChD;;;;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,
|
|
1
|
+
{"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../src/extensions/instrumentation.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,KAAK,EACV,2BAA2B,EAC3B,6BAA6B,EAC7B,gBAAgB,EAChB,cAAc,EACd,aAAa,EAGd,MAAM,UAAU,CAAA;AAYjB,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAIrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAQxC;;;;;;GAMG;AAEH,KAAK,iBAAiB,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE,6BAA6B,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AAE7G,2EAA2E;AAC3E,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,2FAA2F;IAC3F,kBAAkB,CAAC,EAAE,2BAA2B,CAAA;IAChD;;;;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,CA0DnF;AAuJD;;;;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;AAwBrB,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,IAAI,CA8BvG;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,6BAA6B,GAAG,SAAS,EAChD,MAAM,EAAE,QAAQ,GACf,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAoB9B;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,aAAa,EACrB,wBAAwB,EAAE,iBAAiB,EAC3C,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,6BAA6B,GAAG,SAAS,EAChD,MAAM,EAAE,QAAQ,GACf,OAAO,CAAC;IAAE,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC,CAyC9C;AAiFD,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;AA8GD;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,aAAa,EACrB,yBAAyB,EAAE,iBAAiB,EAC5C,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,6BAA6B,GAAG,SAAS,EAChD,MAAM,EAAE,QAAQ,GACf,OAAO,CAAC,OAAO,CAAC,CA0ClB"}
|
|
@@ -24,11 +24,12 @@ var __webpack_require__ = {};
|
|
|
24
24
|
var __webpack_exports__ = {};
|
|
25
25
|
__webpack_require__.r(__webpack_exports__);
|
|
26
26
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
-
|
|
27
|
+
isToolAdvertised: ()=>isToolAdvertised,
|
|
28
28
|
cacheToolDescriptions: ()=>cacheToolDescriptions,
|
|
29
|
-
cacheToolCategories: ()=>cacheToolCategories,
|
|
30
29
|
handleInitializeRequest: ()=>handleInitializeRequest,
|
|
31
30
|
captureToolCall: ()=>captureToolCall,
|
|
31
|
+
readToolMetaCategory: ()=>readToolMetaCategory,
|
|
32
|
+
cacheToolCategories: ()=>cacheToolCategories,
|
|
32
33
|
handleListToolsRequest: ()=>handleListToolsRequest,
|
|
33
34
|
patchRequestHandlers: ()=>patchRequestHandlers
|
|
34
35
|
});
|
|
@@ -49,11 +50,12 @@ const external_tools_js_namespaceObject = require("./tools.js");
|
|
|
49
50
|
const external_tracing_helpers_js_namespaceObject = require("./tracing-helpers.js");
|
|
50
51
|
async function captureToolCall(params) {
|
|
51
52
|
const { server, data, request, extra, execute, parameterOwnership, eventType, explicitContextIntent, takeCapturedError } = params;
|
|
52
|
-
const
|
|
53
|
-
const
|
|
53
|
+
const resolvedEventType = eventType ?? external_event_types_js_namespaceObject.MCPAnalyticsEventType.mcpToolsCall;
|
|
54
|
+
const ownership = getActiveAnalyticsParameterOwnership(data, request.params?.name, parameterOwnership, resolvedEventType === external_event_types_js_namespaceObject.MCPAnalyticsEventType.mcpMissingCapability);
|
|
55
|
+
const conversation = (0, external_conversation_id_js_namespaceObject.resolveConversationId)(ownership.conversationId, request.params?.arguments, request.params?.name, resolvedEventType === external_event_types_js_namespaceObject.MCPAnalyticsEventType.mcpMissingCapability ? (0, external_tools_js_namespaceObject.resolveMissingCapabilityToolName)(data.options) : '');
|
|
54
56
|
const downstreamRequest = cloneRequestWithoutOwnedAnalyticsArguments(request, ownership);
|
|
55
57
|
const startTime = new Date();
|
|
56
|
-
const event = await prepareToolCallEvent(server, data, request, downstreamRequest, extra, startTime, conversation, ownership,
|
|
58
|
+
const event = await prepareToolCallEvent(server, data, request, downstreamRequest, extra, startTime, conversation, ownership, resolvedEventType);
|
|
57
59
|
if (event && explicitContextIntent) (0, external_intent_js_namespaceObject.setExplicitContextIntent)(event, explicitContextIntent);
|
|
58
60
|
let result;
|
|
59
61
|
try {
|
|
@@ -66,9 +68,8 @@ async function captureToolCall(params) {
|
|
|
66
68
|
publishSuccessfulToolEvent(server, event, finalResult, startTime, data.logger, takeCapturedError);
|
|
67
69
|
return finalResult;
|
|
68
70
|
}
|
|
69
|
-
function getActiveAnalyticsParameterOwnership(data, toolName, override) {
|
|
71
|
+
function getActiveAnalyticsParameterOwnership(data, toolName, override, isMissingCapabilityTool) {
|
|
70
72
|
const ownership = override ?? (toolName ? data.toolAnalyticsParameterOwnership.get(toolName) : void 0);
|
|
71
|
-
const isMissingCapabilityTool = toolName === (0, external_tools_js_namespaceObject.resolveMissingCapabilityToolName)(data.options);
|
|
72
73
|
return {
|
|
73
74
|
context: !isMissingCapabilityTool && (0, external_context_parameters_js_namespaceObject.isContextEnabled)(data.options.context) && ownership?.context === true,
|
|
74
75
|
conversationId: !isMissingCapabilityTool && true === data.options.enableConversationId && ownership?.conversationId === true
|
|
@@ -143,20 +144,53 @@ function publishFailedToolEvent(server, event, error, startTime, conversation, l
|
|
|
143
144
|
logger(`Warning: PostHog MCP analytics failed to publish failed tool event - ${publishError}`);
|
|
144
145
|
}
|
|
145
146
|
}
|
|
147
|
+
const originalRequestHandlers = new WeakMap();
|
|
148
|
+
function rememberOriginalRequestHandler(server, handlerName, originalHandler) {
|
|
149
|
+
let handlers = originalRequestHandlers.get(server);
|
|
150
|
+
if (!handlers) {
|
|
151
|
+
handlers = new Map();
|
|
152
|
+
originalRequestHandlers.set(server, handlers);
|
|
153
|
+
}
|
|
154
|
+
handlers.set(handlerName, originalHandler);
|
|
155
|
+
}
|
|
146
156
|
function patchRequestHandlers(server, patches) {
|
|
147
157
|
for (const [handlerName, patch] of Object.entries(patches)){
|
|
148
158
|
const originalHandler = server._requestHandlers.get(handlerName);
|
|
149
|
-
if (originalHandler)
|
|
159
|
+
if (originalHandler) {
|
|
160
|
+
rememberOriginalRequestHandler(server, handlerName, originalHandler);
|
|
161
|
+
server._requestHandlers.set(handlerName, (request, extra)=>patch(server, originalHandler, request, extra));
|
|
162
|
+
}
|
|
150
163
|
}
|
|
151
164
|
const originalSetRequestHandler = server.setRequestHandler.bind(server);
|
|
152
165
|
server.setRequestHandler = (requestSchema, originalHandler)=>{
|
|
153
166
|
const shape = (0, external_mcp_sdk_compat_js_namespaceObject.getObjectShape)(requestSchema);
|
|
154
167
|
const handlerName = shape?.method ? (0, external_mcp_sdk_compat_js_namespaceObject.getLiteralValue)(shape.method) : void 0;
|
|
155
168
|
const patch = 'string' == typeof handlerName ? patches[handlerName] : void 0;
|
|
156
|
-
if (!patch) return originalSetRequestHandler(requestSchema, originalHandler);
|
|
157
|
-
|
|
169
|
+
if (!patch || 'string' != typeof handlerName) return originalSetRequestHandler(requestSchema, originalHandler);
|
|
170
|
+
const result = originalSetRequestHandler(requestSchema, originalHandler);
|
|
171
|
+
const registeredHandler = server._requestHandlers.get(handlerName);
|
|
172
|
+
if (registeredHandler) {
|
|
173
|
+
rememberOriginalRequestHandler(server, handlerName, registeredHandler);
|
|
174
|
+
server._requestHandlers.set(handlerName, (request, extra)=>patch(server, registeredHandler, request, extra));
|
|
175
|
+
}
|
|
176
|
+
return result;
|
|
158
177
|
};
|
|
159
178
|
}
|
|
179
|
+
async function isToolAdvertised(server, toolName, extra, logger) {
|
|
180
|
+
const listHandler = originalRequestHandlers.get(server)?.get('tools/list');
|
|
181
|
+
if (!listHandler || !server._requestHandlers.has('tools/list')) return;
|
|
182
|
+
try {
|
|
183
|
+
const response = await listHandler({
|
|
184
|
+
method: 'tools/list',
|
|
185
|
+
params: {}
|
|
186
|
+
}, extra);
|
|
187
|
+
if (!response || !Array.isArray(response.tools)) return;
|
|
188
|
+
return response.tools.some((tool)=>tool?.name === toolName);
|
|
189
|
+
} catch (error) {
|
|
190
|
+
logger(`Warning: PostHog MCP analytics could not determine whether "${toolName}" is advertised; delegating to the server - ${error}`);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
160
194
|
async function handleListToolsRequest(server, originalListToolsHandler, request, extra, logger) {
|
|
161
195
|
const data = (0, external_internal_js_namespaceObject.getServerTrackingData)(server);
|
|
162
196
|
const startTime = new Date();
|
|
@@ -208,14 +242,23 @@ async function getTracedToolsList(server, originalListToolsHandler, request, ext
|
|
|
208
242
|
try {
|
|
209
243
|
const data = (0, external_internal_js_namespaceObject.getServerTrackingData)(server);
|
|
210
244
|
const originalResponse = await originalListToolsHandler(request, extra);
|
|
211
|
-
let tools =
|
|
245
|
+
let tools = [
|
|
246
|
+
...originalResponse.tools || []
|
|
247
|
+
];
|
|
212
248
|
if (data) cacheToolAnalyticsParameterOwnership(data.toolAnalyticsParameterOwnership, tools);
|
|
213
249
|
if (data && (0, external_context_parameters_js_namespaceObject.isContextEnabled)(data.options.context)) tools = (0, external_context_parameters_js_namespaceObject.addContextParameterToTools)(tools, (0, external_context_parameters_js_namespaceObject.getContextDescription)(data.options.context), data.logger);
|
|
214
|
-
if (data
|
|
215
|
-
if (data?.options.reportMissing) {
|
|
250
|
+
if (data) {
|
|
216
251
|
const missingToolName = (0, external_tools_js_namespaceObject.resolveMissingCapabilityToolName)(data.options);
|
|
217
|
-
|
|
218
|
-
if (
|
|
252
|
+
let injectedMissingCapabilityTool = false;
|
|
253
|
+
if (data.options.reportMissing) {
|
|
254
|
+
const alreadyPresent = tools.some((tool)=>tool?.name === missingToolName);
|
|
255
|
+
if (alreadyPresent) data.logger(`Warning: Cannot inject missing-capability tool "${missingToolName}" because a real tool already uses that name. The real tool will not be intercepted.`);
|
|
256
|
+
else {
|
|
257
|
+
tools.push((0, external_tools_js_namespaceObject.getReportMissingToolDescriptor)(missingToolName));
|
|
258
|
+
injectedMissingCapabilityTool = true;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (data.options.enableConversationId) tools = (0, external_conversation_id_js_namespaceObject.addConversationIdToTools)(tools, missingToolName, injectedMissingCapabilityTool, data.logger);
|
|
219
262
|
}
|
|
220
263
|
if (data) {
|
|
221
264
|
cacheToolDescriptions(data.toolDescriptions, tools);
|
|
@@ -339,6 +382,7 @@ exports.cacheToolDescriptions = __webpack_exports__.cacheToolDescriptions;
|
|
|
339
382
|
exports.captureToolCall = __webpack_exports__.captureToolCall;
|
|
340
383
|
exports.handleInitializeRequest = __webpack_exports__.handleInitializeRequest;
|
|
341
384
|
exports.handleListToolsRequest = __webpack_exports__.handleListToolsRequest;
|
|
385
|
+
exports.isToolAdvertised = __webpack_exports__.isToolAdvertised;
|
|
342
386
|
exports.patchRequestHandlers = __webpack_exports__.patchRequestHandlers;
|
|
343
387
|
exports.readToolMetaCategory = __webpack_exports__.readToolMetaCategory;
|
|
344
388
|
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
@@ -347,6 +391,7 @@ for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
|
347
391
|
"captureToolCall",
|
|
348
392
|
"handleInitializeRequest",
|
|
349
393
|
"handleListToolsRequest",
|
|
394
|
+
"isToolAdvertised",
|
|
350
395
|
"patchRequestHandlers",
|
|
351
396
|
"readToolMetaCategory"
|
|
352
397
|
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
@@ -15,11 +15,12 @@ import { getReportMissingToolDescriptor, resolveMissingCapabilityToolName } from
|
|
|
15
15
|
import { applyResolvedMetadata, isToolResultError } from "./tracing-helpers.mjs";
|
|
16
16
|
async function captureToolCall(params) {
|
|
17
17
|
const { server, data, request, extra, execute, parameterOwnership, eventType, explicitContextIntent, takeCapturedError } = params;
|
|
18
|
-
const
|
|
19
|
-
const
|
|
18
|
+
const resolvedEventType = eventType ?? MCPAnalyticsEventType.mcpToolsCall;
|
|
19
|
+
const ownership = getActiveAnalyticsParameterOwnership(data, request.params?.name, parameterOwnership, resolvedEventType === MCPAnalyticsEventType.mcpMissingCapability);
|
|
20
|
+
const conversation = resolveConversationId(ownership.conversationId, request.params?.arguments, request.params?.name, resolvedEventType === MCPAnalyticsEventType.mcpMissingCapability ? resolveMissingCapabilityToolName(data.options) : '');
|
|
20
21
|
const downstreamRequest = cloneRequestWithoutOwnedAnalyticsArguments(request, ownership);
|
|
21
22
|
const startTime = new Date();
|
|
22
|
-
const event = await prepareToolCallEvent(server, data, request, downstreamRequest, extra, startTime, conversation, ownership,
|
|
23
|
+
const event = await prepareToolCallEvent(server, data, request, downstreamRequest, extra, startTime, conversation, ownership, resolvedEventType);
|
|
23
24
|
if (event && explicitContextIntent) setExplicitContextIntent(event, explicitContextIntent);
|
|
24
25
|
let result;
|
|
25
26
|
try {
|
|
@@ -32,9 +33,8 @@ async function captureToolCall(params) {
|
|
|
32
33
|
publishSuccessfulToolEvent(server, event, finalResult, startTime, data.logger, takeCapturedError);
|
|
33
34
|
return finalResult;
|
|
34
35
|
}
|
|
35
|
-
function getActiveAnalyticsParameterOwnership(data, toolName, override) {
|
|
36
|
+
function getActiveAnalyticsParameterOwnership(data, toolName, override, isMissingCapabilityTool) {
|
|
36
37
|
const ownership = override ?? (toolName ? data.toolAnalyticsParameterOwnership.get(toolName) : void 0);
|
|
37
|
-
const isMissingCapabilityTool = toolName === resolveMissingCapabilityToolName(data.options);
|
|
38
38
|
return {
|
|
39
39
|
context: !isMissingCapabilityTool && isContextEnabled(data.options.context) && ownership?.context === true,
|
|
40
40
|
conversationId: !isMissingCapabilityTool && true === data.options.enableConversationId && ownership?.conversationId === true
|
|
@@ -109,20 +109,53 @@ function publishFailedToolEvent(server, event, error, startTime, conversation, l
|
|
|
109
109
|
logger(`Warning: PostHog MCP analytics failed to publish failed tool event - ${publishError}`);
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
|
+
const originalRequestHandlers = new WeakMap();
|
|
113
|
+
function rememberOriginalRequestHandler(server, handlerName, originalHandler) {
|
|
114
|
+
let handlers = originalRequestHandlers.get(server);
|
|
115
|
+
if (!handlers) {
|
|
116
|
+
handlers = new Map();
|
|
117
|
+
originalRequestHandlers.set(server, handlers);
|
|
118
|
+
}
|
|
119
|
+
handlers.set(handlerName, originalHandler);
|
|
120
|
+
}
|
|
112
121
|
function patchRequestHandlers(server, patches) {
|
|
113
122
|
for (const [handlerName, patch] of Object.entries(patches)){
|
|
114
123
|
const originalHandler = server._requestHandlers.get(handlerName);
|
|
115
|
-
if (originalHandler)
|
|
124
|
+
if (originalHandler) {
|
|
125
|
+
rememberOriginalRequestHandler(server, handlerName, originalHandler);
|
|
126
|
+
server._requestHandlers.set(handlerName, (request, extra)=>patch(server, originalHandler, request, extra));
|
|
127
|
+
}
|
|
116
128
|
}
|
|
117
129
|
const originalSetRequestHandler = server.setRequestHandler.bind(server);
|
|
118
130
|
server.setRequestHandler = (requestSchema, originalHandler)=>{
|
|
119
131
|
const shape = getObjectShape(requestSchema);
|
|
120
132
|
const handlerName = shape?.method ? getLiteralValue(shape.method) : void 0;
|
|
121
133
|
const patch = 'string' == typeof handlerName ? patches[handlerName] : void 0;
|
|
122
|
-
if (!patch) return originalSetRequestHandler(requestSchema, originalHandler);
|
|
123
|
-
|
|
134
|
+
if (!patch || 'string' != typeof handlerName) return originalSetRequestHandler(requestSchema, originalHandler);
|
|
135
|
+
const result = originalSetRequestHandler(requestSchema, originalHandler);
|
|
136
|
+
const registeredHandler = server._requestHandlers.get(handlerName);
|
|
137
|
+
if (registeredHandler) {
|
|
138
|
+
rememberOriginalRequestHandler(server, handlerName, registeredHandler);
|
|
139
|
+
server._requestHandlers.set(handlerName, (request, extra)=>patch(server, registeredHandler, request, extra));
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
124
142
|
};
|
|
125
143
|
}
|
|
144
|
+
async function isToolAdvertised(server, toolName, extra, logger) {
|
|
145
|
+
const listHandler = originalRequestHandlers.get(server)?.get('tools/list');
|
|
146
|
+
if (!listHandler || !server._requestHandlers.has('tools/list')) return;
|
|
147
|
+
try {
|
|
148
|
+
const response = await listHandler({
|
|
149
|
+
method: 'tools/list',
|
|
150
|
+
params: {}
|
|
151
|
+
}, extra);
|
|
152
|
+
if (!response || !Array.isArray(response.tools)) return;
|
|
153
|
+
return response.tools.some((tool)=>tool?.name === toolName);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
logger(`Warning: PostHog MCP analytics could not determine whether "${toolName}" is advertised; delegating to the server - ${error}`);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
126
159
|
async function handleListToolsRequest(server, originalListToolsHandler, request, extra, logger) {
|
|
127
160
|
const data = getServerTrackingData(server);
|
|
128
161
|
const startTime = new Date();
|
|
@@ -174,14 +207,23 @@ async function getTracedToolsList(server, originalListToolsHandler, request, ext
|
|
|
174
207
|
try {
|
|
175
208
|
const data = getServerTrackingData(server);
|
|
176
209
|
const originalResponse = await originalListToolsHandler(request, extra);
|
|
177
|
-
let tools =
|
|
210
|
+
let tools = [
|
|
211
|
+
...originalResponse.tools || []
|
|
212
|
+
];
|
|
178
213
|
if (data) cacheToolAnalyticsParameterOwnership(data.toolAnalyticsParameterOwnership, tools);
|
|
179
214
|
if (data && isContextEnabled(data.options.context)) tools = addContextParameterToTools(tools, getContextDescription(data.options.context), data.logger);
|
|
180
|
-
if (data
|
|
181
|
-
if (data?.options.reportMissing) {
|
|
215
|
+
if (data) {
|
|
182
216
|
const missingToolName = resolveMissingCapabilityToolName(data.options);
|
|
183
|
-
|
|
184
|
-
if (
|
|
217
|
+
let injectedMissingCapabilityTool = false;
|
|
218
|
+
if (data.options.reportMissing) {
|
|
219
|
+
const alreadyPresent = tools.some((tool)=>tool?.name === missingToolName);
|
|
220
|
+
if (alreadyPresent) data.logger(`Warning: Cannot inject missing-capability tool "${missingToolName}" because a real tool already uses that name. The real tool will not be intercepted.`);
|
|
221
|
+
else {
|
|
222
|
+
tools.push(getReportMissingToolDescriptor(missingToolName));
|
|
223
|
+
injectedMissingCapabilityTool = true;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (data.options.enableConversationId) tools = addConversationIdToTools(tools, missingToolName, injectedMissingCapabilityTool, data.logger);
|
|
185
227
|
}
|
|
186
228
|
if (data) {
|
|
187
229
|
cacheToolDescriptions(data.toolDescriptions, tools);
|
|
@@ -300,4 +342,4 @@ function readProtocolVersion(result, request) {
|
|
|
300
342
|
const requested = request.params?.protocolVersion;
|
|
301
343
|
return 'string' == typeof requested && requested.length > 0 ? requested : void 0;
|
|
302
344
|
}
|
|
303
|
-
export { cacheToolCategories, cacheToolDescriptions, captureToolCall, handleInitializeRequest, handleListToolsRequest, patchRequestHandlers, readToolMetaCategory };
|
|
345
|
+
export { cacheToolCategories, cacheToolDescriptions, captureToolCall, handleInitializeRequest, handleListToolsRequest, isToolAdvertised, patchRequestHandlers, readToolMetaCategory };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent.d.ts","sourceRoot":"","sources":["../../src/extensions/intent.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,6BAA6B,EAC7B,gBAAgB,EAChB,wBAAwB,EACxB,cAAc,EACd,QAAQ,EACT,MAAM,UAAU,CAAA;AAGjB,UAAU,cAAc;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,wBAAwB,CAAA;CACjC;AAkCD,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,cAAc,EACvB,oBAAoB,EAAE,OAAO,EAC7B,KAAK,CAAC,EAAE,6BAA6B,GACpC,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"intent.d.ts","sourceRoot":"","sources":["../../src/extensions/intent.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,6BAA6B,EAC7B,gBAAgB,EAChB,wBAAwB,EACxB,cAAc,EACd,QAAQ,EACT,MAAM,UAAU,CAAA;AAGjB,UAAU,cAAc;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,wBAAwB,CAAA;CACjC;AAkCD,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,cAAc,EACvB,oBAAoB,EAAE,OAAO,EAC7B,KAAK,CAAC,EAAE,6BAA6B,GACpC,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAOhC;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,cAAc,GAAG,IAAI,GAAG,IAAI,CAO3F;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAQ/E"}
|
|
@@ -53,7 +53,7 @@ async function runIntentFallback(data, request, extra) {
|
|
|
53
53
|
}
|
|
54
54
|
async function resolveToolCallIntent(data, request, analyticsOwnsContext, extra) {
|
|
55
55
|
const contextArgument = getContextArgument(request);
|
|
56
|
-
if (analyticsOwnsContext && (0, external_context_parameters_js_namespaceObject.isContextEnabled)(data.options.context) &&
|
|
56
|
+
if (analyticsOwnsContext && (0, external_context_parameters_js_namespaceObject.isContextEnabled)(data.options.context) && contextArgument) return {
|
|
57
57
|
intent: contextArgument,
|
|
58
58
|
source: 'context_parameter'
|
|
59
59
|
};
|
|
@@ -23,7 +23,7 @@ async function runIntentFallback(data, request, extra) {
|
|
|
23
23
|
}
|
|
24
24
|
async function resolveToolCallIntent(data, request, analyticsOwnsContext, extra) {
|
|
25
25
|
const contextArgument = getContextArgument(request);
|
|
26
|
-
if (analyticsOwnsContext && isContextEnabled(data.options.context) &&
|
|
26
|
+
if (analyticsOwnsContext && isContextEnabled(data.options.context) && contextArgument) return {
|
|
27
27
|
intent: contextArgument,
|
|
28
28
|
source: 'context_parameter'
|
|
29
29
|
};
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version = "0.10.
|
|
1
|
+
export declare const version = "0.10.6";
|
|
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.10.
|
|
29
|
+
const version = '0.10.6';
|
|
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.10.
|
|
1
|
+
const version = '0.10.6';
|
|
2
2
|
export { version };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@posthog/mcp",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.6",
|
|
4
4
|
"bugs": {
|
|
5
5
|
"url": "https://github.com/PostHog/posthog-js/issues"
|
|
6
6
|
},
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"@types/node": "^20.0.0",
|
|
40
40
|
"jest": "29.7.0",
|
|
41
41
|
"zod": "^3.25.0",
|
|
42
|
-
"
|
|
43
|
-
"posthog-
|
|
42
|
+
"posthog-node": "^5.47.8",
|
|
43
|
+
"@posthog-tooling/tsconfig-base": "1.1.1"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"@modelcontextprotocol/sdk": ">=1.26.0",
|
|
@@ -108,11 +108,5 @@ export function addContextParameterToTools<TTool extends ContextInjectableTool>(
|
|
|
108
108
|
contextDescriptionOverride?: string,
|
|
109
109
|
logger: LoggerFn = log
|
|
110
110
|
): TTool[] {
|
|
111
|
-
return tools.map((tool) =>
|
|
112
|
-
// Skip get_more_tools - it has its own special context parameter
|
|
113
|
-
if (tool.name === 'get_more_tools') {
|
|
114
|
-
return tool
|
|
115
|
-
}
|
|
116
|
-
return addContextParameterToTool(tool, contextDescriptionOverride, logger)
|
|
117
|
-
})
|
|
111
|
+
return tools.map((tool) => addContextParameterToTool(tool, contextDescriptionOverride, logger))
|
|
118
112
|
}
|
|
@@ -73,10 +73,11 @@ export function addConversationIdToTool<TTool extends ConversationIdInjectableTo
|
|
|
73
73
|
export function addConversationIdToTools<TTool extends ConversationIdInjectableTool>(
|
|
74
74
|
tools: TTool[],
|
|
75
75
|
missingCapabilityToolName: string = GET_MORE_TOOLS_NAME,
|
|
76
|
+
skipMissingCapabilityTool = true,
|
|
76
77
|
logger: LoggerFn = log
|
|
77
78
|
): TTool[] {
|
|
78
79
|
return tools.map((tool) => {
|
|
79
|
-
if (tool.name === missingCapabilityToolName) {
|
|
80
|
+
if (skipMissingCapabilityTool && tool.name === missingCapabilityToolName) {
|
|
80
81
|
return tool
|
|
81
82
|
}
|
|
82
83
|
return addConversationIdToTool(tool, logger)
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
handleListToolsRequest,
|
|
28
28
|
patchRequestHandlers,
|
|
29
29
|
captureToolCall,
|
|
30
|
+
isToolAdvertised,
|
|
30
31
|
readToolMetaCategory,
|
|
31
32
|
type HandlerPatch,
|
|
32
33
|
} from './instrumentation'
|
|
@@ -162,8 +163,6 @@ function addTracingToToolCallbackInternal(
|
|
|
162
163
|
): RegisteredTool {
|
|
163
164
|
const originalCallback = getToolFunction(tool)
|
|
164
165
|
const options = getServerTrackingData(server.server as MCPServerLike)?.options
|
|
165
|
-
const missingToolName = resolveMissingCapabilityToolName(options)
|
|
166
|
-
|
|
167
166
|
if (wrappedCallbacks.has(originalCallback)) {
|
|
168
167
|
logger(`Tool ${toolName} callback already wrapped, skipping re-wrap`)
|
|
169
168
|
return tool
|
|
@@ -188,16 +187,9 @@ function addTracingToToolCallbackInternal(
|
|
|
188
187
|
|
|
189
188
|
const inputSchema = getCurrentInputSchema()
|
|
190
189
|
const cleanedArgs = stripOwnedAnalyticsArguments(args, {
|
|
191
|
-
context:
|
|
192
|
-
|
|
193
|
-
isContextEnabled(options?.context) &&
|
|
194
|
-
analyticsOwnsParameter(inputSchema, 'context'),
|
|
195
|
-
conversationId:
|
|
196
|
-
toolName !== missingToolName &&
|
|
197
|
-
options?.enableConversationId === true &&
|
|
198
|
-
analyticsOwnsParameter(inputSchema, 'conversation_id'),
|
|
190
|
+
context: isContextEnabled(options?.context) && analyticsOwnsParameter(inputSchema, 'context'),
|
|
191
|
+
conversationId: options?.enableConversationId === true && analyticsOwnsParameter(inputSchema, 'conversation_id'),
|
|
199
192
|
})
|
|
200
|
-
|
|
201
193
|
try {
|
|
202
194
|
if (cleanedArgs === undefined) {
|
|
203
195
|
const handler = originalCallback as (extra: CompatibleRequestHandlerExtra) => Promise<CallToolResult>
|
|
@@ -242,7 +234,11 @@ async function handleToolCallRequest(
|
|
|
242
234
|
return await originalCallToolHandler(request, extra)
|
|
243
235
|
}
|
|
244
236
|
|
|
245
|
-
|
|
237
|
+
const toolName = request.params?.name
|
|
238
|
+
const isMissingCapabilityCandidate =
|
|
239
|
+
data.options.reportMissing && toolName === resolveMissingCapabilityToolName(data.options)
|
|
240
|
+
|
|
241
|
+
if (isMissingCapabilityCandidate && (await isToolAdvertised(server, toolName, extra, data.logger)) === false) {
|
|
246
242
|
const context = getContextArgument(request) || ''
|
|
247
243
|
return await captureToolCall({
|
|
248
244
|
server,
|
|
@@ -255,7 +251,6 @@ async function handleToolCallRequest(
|
|
|
255
251
|
})
|
|
256
252
|
}
|
|
257
253
|
|
|
258
|
-
const toolName = request.params?.name
|
|
259
254
|
const registeredTool = toolName ? highLevelServer._registeredTools[toolName] : undefined
|
|
260
255
|
|
|
261
256
|
// Strip SDK-owned arguments before the MCP SDK validates the registered Zod
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
handleListToolsRequest,
|
|
15
15
|
patchRequestHandlers,
|
|
16
16
|
captureToolCall,
|
|
17
|
+
isToolAdvertised,
|
|
17
18
|
type HandlerPatch,
|
|
18
19
|
} from './instrumentation'
|
|
19
20
|
import { getContextArgument } from './tracing-helpers'
|
|
@@ -32,19 +33,25 @@ type MCPRequestExtra = Parameters<MCPRequestHandler>[1]
|
|
|
32
33
|
export function instrumentLowLevelServer(server: MCPServerLike, logger: LoggerFn): void {
|
|
33
34
|
try {
|
|
34
35
|
// Patch already existing handlers, and patch setRequestHandler to capture dynamically created handlers.
|
|
36
|
+
const hadCallToolHandler = server._requestHandlers.has('tools/call')
|
|
35
37
|
const handlers: Record<string, HandlerPatch> = {
|
|
36
38
|
initialize: (server, originalHandler, request, extra) =>
|
|
37
39
|
handleInitializeRequest(server, originalHandler, request, extra, logger),
|
|
38
40
|
'tools/list': (server, originalHandler, request, extra) =>
|
|
39
41
|
handleListToolsRequest(server, originalHandler, request, extra, logger),
|
|
42
|
+
'tools/call': (server, originalHandler, request, extra) =>
|
|
43
|
+
handleToolCallRequest(server, originalHandler, request, extra, logger),
|
|
40
44
|
}
|
|
41
45
|
patchRequestHandlers(server, handlers)
|
|
42
46
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
if (!hadCallToolHandler) {
|
|
48
|
+
// Register a raw fallback through the patched setter so reportMissing works
|
|
49
|
+
// even before an application dispatcher is attached. A later registration
|
|
50
|
+
// replaces it and is wrapped by patchRequestHandlers.
|
|
51
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
52
|
+
throw new Error(`Unknown tool: ${request.params?.name || 'unknown'}`)
|
|
53
|
+
})
|
|
54
|
+
}
|
|
48
55
|
} catch (error) {
|
|
49
56
|
logger(`Warning: Failed to setup tool call instrumentation - ${error}`)
|
|
50
57
|
throw error
|
|
@@ -66,7 +73,11 @@ async function handleToolCallRequest(
|
|
|
66
73
|
return await originalCallToolHandler?.(request, extra)
|
|
67
74
|
}
|
|
68
75
|
|
|
69
|
-
|
|
76
|
+
const toolName = request.params?.name
|
|
77
|
+
const isMissingCapabilityCandidate =
|
|
78
|
+
data.options.reportMissing && toolName === resolveMissingCapabilityToolName(data.options)
|
|
79
|
+
|
|
80
|
+
if (isMissingCapabilityCandidate && (await isToolAdvertised(server, toolName, extra, data.logger)) === false) {
|
|
70
81
|
const context = getContextArgument(request) || ''
|
|
71
82
|
return await captureToolCall({
|
|
72
83
|
server,
|
|
@@ -98,13 +98,20 @@ export async function captureToolCall(params: TraceToolCallParams): Promise<unkn
|
|
|
98
98
|
explicitContextIntent,
|
|
99
99
|
takeCapturedError,
|
|
100
100
|
} = params
|
|
101
|
-
|
|
102
|
-
const ownership = getActiveAnalyticsParameterOwnership(
|
|
101
|
+
const resolvedEventType = eventType ?? MCPAnalyticsEventType.mcpToolsCall
|
|
102
|
+
const ownership = getActiveAnalyticsParameterOwnership(
|
|
103
|
+
data,
|
|
104
|
+
request.params?.name,
|
|
105
|
+
parameterOwnership,
|
|
106
|
+
resolvedEventType === MCPAnalyticsEventType.mcpMissingCapability
|
|
107
|
+
)
|
|
103
108
|
const conversation = resolveConversationId(
|
|
104
109
|
ownership.conversationId,
|
|
105
110
|
request.params?.arguments,
|
|
106
111
|
request.params?.name,
|
|
107
|
-
|
|
112
|
+
resolvedEventType === MCPAnalyticsEventType.mcpMissingCapability
|
|
113
|
+
? resolveMissingCapabilityToolName(data.options)
|
|
114
|
+
: ''
|
|
108
115
|
)
|
|
109
116
|
const downstreamRequest = cloneRequestWithoutOwnedAnalyticsArguments(request, ownership)
|
|
110
117
|
|
|
@@ -120,7 +127,7 @@ export async function captureToolCall(params: TraceToolCallParams): Promise<unkn
|
|
|
120
127
|
startTime,
|
|
121
128
|
conversation,
|
|
122
129
|
ownership,
|
|
123
|
-
|
|
130
|
+
resolvedEventType
|
|
124
131
|
)
|
|
125
132
|
if (event && explicitContextIntent) {
|
|
126
133
|
setExplicitContextIntent(event, explicitContextIntent)
|
|
@@ -142,10 +149,10 @@ export async function captureToolCall(params: TraceToolCallParams): Promise<unkn
|
|
|
142
149
|
function getActiveAnalyticsParameterOwnership(
|
|
143
150
|
data: MCPAnalyticsData,
|
|
144
151
|
toolName: string | undefined,
|
|
145
|
-
override
|
|
152
|
+
override: AnalyticsParameterOwnership | undefined,
|
|
153
|
+
isMissingCapabilityTool: boolean
|
|
146
154
|
): AnalyticsParameterOwnership {
|
|
147
155
|
const ownership = override ?? (toolName ? data.toolAnalyticsParameterOwnership.get(toolName) : undefined)
|
|
148
|
-
const isMissingCapabilityTool = toolName === resolveMissingCapabilityToolName(data.options)
|
|
149
156
|
return {
|
|
150
157
|
context: !isMissingCapabilityTool && isContextEnabled(data.options.context) && ownership?.context === true,
|
|
151
158
|
conversationId:
|
|
@@ -307,11 +314,27 @@ export type HandlerPatch = (
|
|
|
307
314
|
* that register handlers post-construction work — e.g. `@rekog/mcp-nest` hands a
|
|
308
315
|
* bare server to instrument() and only then registers its handlers.
|
|
309
316
|
*/
|
|
317
|
+
const originalRequestHandlers = new WeakMap<MCPServerLike, Map<string, MCPRequestHandler>>()
|
|
318
|
+
|
|
319
|
+
function rememberOriginalRequestHandler(
|
|
320
|
+
server: MCPServerLike,
|
|
321
|
+
handlerName: string,
|
|
322
|
+
originalHandler: MCPRequestHandler
|
|
323
|
+
): void {
|
|
324
|
+
let handlers = originalRequestHandlers.get(server)
|
|
325
|
+
if (!handlers) {
|
|
326
|
+
handlers = new Map()
|
|
327
|
+
originalRequestHandlers.set(server, handlers)
|
|
328
|
+
}
|
|
329
|
+
handlers.set(handlerName, originalHandler)
|
|
330
|
+
}
|
|
331
|
+
|
|
310
332
|
export function patchRequestHandlers(server: MCPServerLike, patches: Record<string, HandlerPatch>): void {
|
|
311
333
|
// Monkey patch existing handlers.
|
|
312
334
|
for (const [handlerName, patch] of Object.entries(patches)) {
|
|
313
335
|
const originalHandler = server._requestHandlers.get(handlerName)
|
|
314
336
|
if (originalHandler) {
|
|
337
|
+
rememberOriginalRequestHandler(server, handlerName, originalHandler)
|
|
315
338
|
server._requestHandlers.set(handlerName, (request, extra) => patch(server, originalHandler, request, extra))
|
|
316
339
|
}
|
|
317
340
|
}
|
|
@@ -322,14 +345,55 @@ export function patchRequestHandlers(server: MCPServerLike, patches: Record<stri
|
|
|
322
345
|
const shape = getObjectShape(requestSchema)
|
|
323
346
|
const handlerName = shape?.method ? getLiteralValue(shape.method) : undefined
|
|
324
347
|
const patch = typeof handlerName === 'string' ? patches[handlerName] : undefined
|
|
325
|
-
if (!patch) {
|
|
348
|
+
if (!patch || typeof handlerName !== 'string') {
|
|
326
349
|
return originalSetRequestHandler(requestSchema, originalHandler)
|
|
327
350
|
}
|
|
328
351
|
|
|
329
|
-
|
|
352
|
+
// Register first so the MCP SDK's request/result validation stays inside
|
|
353
|
+
// our analytics wrapper, matching handlers that existed before instrument().
|
|
354
|
+
const result = originalSetRequestHandler(requestSchema, originalHandler)
|
|
355
|
+
const registeredHandler = server._requestHandlers.get(handlerName)
|
|
356
|
+
if (registeredHandler) {
|
|
357
|
+
rememberOriginalRequestHandler(server, handlerName, registeredHandler)
|
|
358
|
+
server._requestHandlers.set(handlerName, (request, extra) => patch(server, registeredHandler, request, extra))
|
|
359
|
+
}
|
|
360
|
+
return result
|
|
330
361
|
}) as MCPServerLike['setRequestHandler']
|
|
331
362
|
}
|
|
332
363
|
|
|
364
|
+
/**
|
|
365
|
+
* Checks the server's raw listing for a real owner of a candidate virtual tool.
|
|
366
|
+
* This does not depend on a previous client request and does not call the
|
|
367
|
+
* instrumented list wrapper, so it neither injects PostHog tools nor captures a
|
|
368
|
+
* synthetic tools/list event. `undefined` fails open to the real dispatcher.
|
|
369
|
+
*/
|
|
370
|
+
export async function isToolAdvertised(
|
|
371
|
+
server: MCPServerLike,
|
|
372
|
+
toolName: string,
|
|
373
|
+
extra: CompatibleRequestHandlerExtra | undefined,
|
|
374
|
+
logger: LoggerFn
|
|
375
|
+
): Promise<boolean | undefined> {
|
|
376
|
+
const listHandler = originalRequestHandlers.get(server)?.get('tools/list')
|
|
377
|
+
if (!listHandler || !server._requestHandlers.has('tools/list')) {
|
|
378
|
+
return undefined
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
try {
|
|
382
|
+
const response = (await listHandler({ method: 'tools/list', params: {} }, extra)) as ListToolsResult
|
|
383
|
+
if (!response || !Array.isArray(response.tools)) {
|
|
384
|
+
return undefined
|
|
385
|
+
}
|
|
386
|
+
// Match the page the current list instrumentation can expose. Pagination
|
|
387
|
+
// passthrough is handled separately from missing-capability ownership.
|
|
388
|
+
return response.tools.some((tool) => tool?.name === toolName)
|
|
389
|
+
} catch (error) {
|
|
390
|
+
logger(
|
|
391
|
+
`Warning: PostHog MCP analytics could not determine whether "${toolName}" is advertised; delegating to the server - ${error}`
|
|
392
|
+
)
|
|
393
|
+
return undefined
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
333
397
|
/**
|
|
334
398
|
* Captures each `tools/list` and injects the SDK-managed tools (context
|
|
335
399
|
* parameter, conversation id, `get_more_tools`) into the returned list.
|
|
@@ -414,25 +478,33 @@ async function getTracedToolsList(
|
|
|
414
478
|
try {
|
|
415
479
|
const data = getServerTrackingData(server)
|
|
416
480
|
const originalResponse = (await originalListToolsHandler(request, extra)) as ListToolsResult
|
|
417
|
-
|
|
481
|
+
// Injection must not mutate arrays reused or frozen by the server.
|
|
482
|
+
let tools = [...(originalResponse.tools || [])]
|
|
418
483
|
|
|
419
484
|
if (data) {
|
|
420
485
|
cacheToolAnalyticsParameterOwnership(data.toolAnalyticsParameterOwnership, tools)
|
|
421
486
|
}
|
|
422
|
-
|
|
423
487
|
if (data && isContextEnabled(data.options.context)) {
|
|
424
488
|
tools = addContextParameterToTools(tools, getContextDescription(data.options.context), data.logger)
|
|
425
489
|
}
|
|
426
490
|
|
|
427
|
-
if (data
|
|
428
|
-
tools = addConversationIdToTools(tools, resolveMissingCapabilityToolName(data.options), data.logger)
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
if (data?.options.reportMissing) {
|
|
491
|
+
if (data) {
|
|
432
492
|
const missingToolName = resolveMissingCapabilityToolName(data.options)
|
|
433
|
-
|
|
434
|
-
if (
|
|
435
|
-
tools.
|
|
493
|
+
let injectedMissingCapabilityTool = false
|
|
494
|
+
if (data.options.reportMissing) {
|
|
495
|
+
const alreadyPresent = tools.some((tool) => tool?.name === missingToolName)
|
|
496
|
+
if (alreadyPresent) {
|
|
497
|
+
data.logger(
|
|
498
|
+
`Warning: Cannot inject missing-capability tool "${missingToolName}" because a real tool already uses that name. The real tool will not be intercepted.`
|
|
499
|
+
)
|
|
500
|
+
} else {
|
|
501
|
+
tools.push(getReportMissingToolDescriptor(missingToolName))
|
|
502
|
+
injectedMissingCapabilityTool = true
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (data.options.enableConversationId) {
|
|
507
|
+
tools = addConversationIdToTools(tools, missingToolName, injectedMissingCapabilityTool, data.logger)
|
|
436
508
|
}
|
|
437
509
|
}
|
|
438
510
|
|
package/src/extensions/intent.ts
CHANGED
|
@@ -56,12 +56,7 @@ export async function resolveToolCallIntent(
|
|
|
56
56
|
extra?: CompatibleRequestHandlerExtra
|
|
57
57
|
): Promise<ResolvedIntent | null> {
|
|
58
58
|
const contextArgument = getContextArgument(request)
|
|
59
|
-
if (
|
|
60
|
-
analyticsOwnsContext &&
|
|
61
|
-
isContextEnabled(data.options.context) &&
|
|
62
|
-
request.params?.name !== 'get_more_tools' &&
|
|
63
|
-
contextArgument
|
|
64
|
-
) {
|
|
59
|
+
if (analyticsOwnsContext && isContextEnabled(data.options.context) && contextArgument) {
|
|
65
60
|
return { intent: contextArgument, source: 'context_parameter' }
|
|
66
61
|
}
|
|
67
62
|
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '0.10.
|
|
1
|
+
export const version = '0.10.6'
|