@posthog/mcp 0.11.4 → 0.11.5
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/README.md +33 -0
- package/dist/extensions/instrumentation.d.ts +1 -3
- package/dist/extensions/instrumentation.d.ts.map +1 -1
- package/dist/extensions/instrumentation.js +18 -18
- package/dist/extensions/instrumentation.mjs +18 -18
- package/dist/extensions/intent.d.ts +1 -1
- package/dist/extensions/intent.d.ts.map +1 -1
- package/dist/extensions/intent.js +2 -2
- package/dist/extensions/intent.mjs +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.mjs +1 -1
- package/package.json +4 -4
- package/src/extensions/instrumentation.ts +48 -15
- package/src/extensions/intent.ts +2 -2
- package/src/version.ts +1 -1
package/README.md
CHANGED
|
@@ -116,6 +116,39 @@ identify: async (request, extra) => {
|
|
|
116
116
|
}
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
+
### What `$mcp_intent` records, and how to turn it off
|
|
120
|
+
|
|
121
|
+
`context` defaults to **on**: the SDK adds a `context` parameter to every tool it advertises, asks
|
|
122
|
+
the agent to say why it is calling, and records the answer as `$mcp_intent`. It is stripped before
|
|
123
|
+
your tool runs wherever the SDK can confirm the parameter is its own.
|
|
124
|
+
|
|
125
|
+
On a server that builds a fresh instance per HTTP request — `createMcpHandler`, or `@rekog/mcp-nest`
|
|
126
|
+
in its stateless mode — that confirmation is not available: ownership is learned while serving
|
|
127
|
+
`tools/list`, and the instance handling a `tools/call` never served one. There the SDK records the
|
|
128
|
+
argument but does **not** strip it, because deleting an argument your tool declared would cost you
|
|
129
|
+
the call, while an extra key usually costs nothing.
|
|
130
|
+
|
|
131
|
+
What matters is instance lifetime, not statelessness. A server that is stateless at the transport
|
|
132
|
+
(`sessionIdGenerator: undefined`) but keeps one long-lived server object learns ownership from the
|
|
133
|
+
first `tools/list` and keeps it, so none of the above applies to it.
|
|
134
|
+
|
|
135
|
+
The consequence worth knowing: if **your own** tool declares a parameter named `context` and the SDK
|
|
136
|
+
cannot tell that it is yours, its value is recorded as `$mcp_intent`. It never leaves your project,
|
|
137
|
+
and it is capped at 2048 characters. Two ways out, both one line:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
instrument(server, posthog, { context: false }) // no injection, no capture
|
|
141
|
+
|
|
142
|
+
instrument(server, posthog, { // keep it, drop the property
|
|
143
|
+
beforeSend: (event) => {
|
|
144
|
+
delete event.properties.$mcp_intent
|
|
145
|
+
return event
|
|
146
|
+
},
|
|
147
|
+
})
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`intentFallback` is the third option: supply the intent yourself when the agent did not send one.
|
|
151
|
+
|
|
119
152
|
### If you switched to `instrument(server.server)`
|
|
120
153
|
|
|
121
154
|
Before v2 support landed, the compatibility gate rejected high-level v2 servers, and the usual
|
|
@@ -95,9 +95,7 @@ export declare function isToolAdvertised(server: MCPServerLike, toolName: string
|
|
|
95
95
|
* Captures each `tools/list` and injects the SDK-managed tools (context
|
|
96
96
|
* parameter, conversation id, `get_more_tools`) into the returned list.
|
|
97
97
|
*/
|
|
98
|
-
export declare function handleListToolsRequest(server: MCPServerLike, originalListToolsHandler: MCPRequestHandler, request: MCPRequestLike, extra: CompatibleRequestHandlerExtra | undefined, logger: LoggerFn): Promise<
|
|
99
|
-
tools: CompatibleToolsListLike['tools'];
|
|
100
|
-
}>;
|
|
98
|
+
export declare function handleListToolsRequest(server: MCPServerLike, originalListToolsHandler: MCPRequestHandler, request: MCPRequestLike, extra: CompatibleRequestHandlerExtra | undefined, logger: LoggerFn): Promise<CompatibleToolsListLike>;
|
|
101
99
|
export declare function cacheToolDescriptions(cache: Map<string, string>, tools: CompatibleToolsListLike['tools'] | undefined): void;
|
|
102
100
|
/**
|
|
103
101
|
* Category declared on a tool's `_meta` block (the MCP spec allows arbitrary
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../src/extensions/instrumentation.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,2BAA2B,EAC3B,6BAA6B,EAC7B,uBAAuB,EACvB,gBAAgB,EAChB,cAAc,EACd,aAAa,EAId,MAAM,UAAU,CAAA;AAcjB,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAIrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AASxC;;;;;;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":"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"}
|
|
@@ -56,10 +56,11 @@ async function captureToolCall(params) {
|
|
|
56
56
|
const { server, data, request, extra, execute, parameterOwnership, eventType, explicitContextIntent, takeCapturedError } = params;
|
|
57
57
|
const resolvedEventType = eventType ?? external_event_types_js_namespaceObject.MCPAnalyticsEventType.mcpToolsCall;
|
|
58
58
|
const ownership = getActiveAnalyticsParameterOwnership(data, request.params?.name, parameterOwnership, resolvedEventType === external_event_types_js_namespaceObject.MCPAnalyticsEventType.mcpMissingCapability);
|
|
59
|
+
const canCaptureContextIntent = resolvedEventType !== external_event_types_js_namespaceObject.MCPAnalyticsEventType.mcpMissingCapability && (0, external_context_parameters_js_namespaceObject.isContextEnabled)(data.options.context) && (ownership.context || !ownership.contextOwnershipKnown);
|
|
59
60
|
const conversation = (0, external_conversation_id_js_namespaceObject.resolveConversationId)(ownership.conversationId, request.params?.arguments);
|
|
60
61
|
const downstreamRequest = cloneRequestWithoutOwnedAnalyticsArguments(request, ownership);
|
|
61
62
|
const startTime = new Date();
|
|
62
|
-
const preparedEvent = await prepareToolCallEvent(server, data, request, downstreamRequest, extra, startTime, conversation, ownership, resolvedEventType);
|
|
63
|
+
const preparedEvent = await prepareToolCallEvent(server, data, request, downstreamRequest, extra, startTime, conversation, ownership, resolvedEventType, canCaptureContextIntent);
|
|
63
64
|
if (preparedEvent && explicitContextIntent) (0, external_intent_js_namespaceObject.setExplicitContextIntent)(preparedEvent.event, explicitContextIntent);
|
|
64
65
|
let result;
|
|
65
66
|
try {
|
|
@@ -76,6 +77,7 @@ function getActiveAnalyticsParameterOwnership(data, toolName, override, isMissin
|
|
|
76
77
|
const listed = toolName ? data.toolAnalyticsParameterOwnership.get(toolName) : void 0;
|
|
77
78
|
const ownership = override ?? listed;
|
|
78
79
|
return {
|
|
80
|
+
contextOwnershipKnown: void 0 !== ownership,
|
|
79
81
|
context: !isMissingCapabilityTool && (0, external_context_parameters_js_namespaceObject.isContextEnabled)(data.options.context) && ownership?.context === true,
|
|
80
82
|
conversationId: true === data.options.enableConversationId && ownership?.conversationId === true,
|
|
81
83
|
outputInstructions: true === data.options.enableConversationId && listed?.outputInstructions === true
|
|
@@ -93,7 +95,7 @@ function cloneRequestWithoutOwnedAnalyticsArguments(request, ownership) {
|
|
|
93
95
|
}
|
|
94
96
|
};
|
|
95
97
|
}
|
|
96
|
-
async function prepareToolCallEvent(server, data, request, downstreamRequest, extra, startTime, conversation, ownership, eventType) {
|
|
98
|
+
async function prepareToolCallEvent(server, data, request, downstreamRequest, extra, startTime, conversation, ownership, eventType, canCaptureContextIntent) {
|
|
97
99
|
try {
|
|
98
100
|
const sessionId = (0, external_session_js_namespaceObject.getSessionId)(server, extra, conversation.conversationId);
|
|
99
101
|
const sessionInfo = (0, external_session_js_namespaceObject.getSessionInfo)(server, data, sessionId);
|
|
@@ -113,7 +115,7 @@ async function prepareToolCallEvent(server, data, request, downstreamRequest, ex
|
|
|
113
115
|
(0, external_client_identity_js_namespaceObject.stampClientIdentity)(event, request, extra, server);
|
|
114
116
|
(0, external_transport_identity_js_namespaceObject.stampTransportIdentity)(event, extra);
|
|
115
117
|
await (0, external_tracing_helpers_js_namespaceObject.applyResolvedMetadata)(event, data, request, extra);
|
|
116
|
-
(0, external_intent_js_namespaceObject.setEventIntent)(event, await (0, external_intent_js_namespaceObject.resolveToolCallIntent)(data, request,
|
|
118
|
+
(0, external_intent_js_namespaceObject.setEventIntent)(event, await (0, external_intent_js_namespaceObject.resolveToolCallIntent)(data, request, canCaptureContextIntent, extra));
|
|
117
119
|
return {
|
|
118
120
|
event,
|
|
119
121
|
requestAttribution
|
|
@@ -223,7 +225,7 @@ async function handleListToolsRequest(server, originalListToolsHandler, request,
|
|
|
223
225
|
const data = (0, external_internal_js_namespaceObject.getServerTrackingData)(server);
|
|
224
226
|
const startTime = new Date();
|
|
225
227
|
const sessionId = (0, external_session_js_namespaceObject.getSessionId)(server, extra);
|
|
226
|
-
const
|
|
228
|
+
const sessionInfo = (0, external_session_js_namespaceObject.getSessionInfo)(server, data, sessionId);
|
|
227
229
|
const event = {
|
|
228
230
|
sessionId,
|
|
229
231
|
parameters: (0, external_mcp_payloads_js_namespaceObject.buildCapturedMcpParameters)(request),
|
|
@@ -232,13 +234,14 @@ async function handleListToolsRequest(server, originalListToolsHandler, request,
|
|
|
232
234
|
};
|
|
233
235
|
(0, external_client_identity_js_namespaceObject.stampClientIdentity)(event, request, extra, server);
|
|
234
236
|
(0, external_transport_identity_js_namespaceObject.stampTransportIdentity)(event, extra);
|
|
237
|
+
const identity = data ? await (0, external_internal_js_namespaceObject.handleIdentify)(server, data, sessionId, request, sessionInfo, extra) : void 0;
|
|
238
|
+
const requestAttribution = (0, external_internal_js_namespaceObject.withIdentity)(sessionInfo, identity);
|
|
235
239
|
if (data) await (0, external_tracing_helpers_js_namespaceObject.applyResolvedMetadata)(event, data, request, extra);
|
|
236
|
-
const
|
|
240
|
+
const response = await getTracedToolsList(server, originalListToolsHandler, request, extra, event, logger, requestAttribution);
|
|
241
|
+
const tools = response.tools;
|
|
237
242
|
if (!data) {
|
|
238
243
|
logger('Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called instrument(server, options) before using tool calls.');
|
|
239
|
-
return
|
|
240
|
-
tools
|
|
241
|
-
};
|
|
244
|
+
return response;
|
|
242
245
|
}
|
|
243
246
|
if (0 === tools.length) {
|
|
244
247
|
data.logger('Warning: No tools found in the original list. This is likely due to the tools not being registered before PostHog MCP analytics.instrument().');
|
|
@@ -246,20 +249,14 @@ async function handleListToolsRequest(server, originalListToolsHandler, request,
|
|
|
246
249
|
event.isError = true;
|
|
247
250
|
event.duration = Date.now() - startTime.getTime();
|
|
248
251
|
(0, external_capture_js_namespaceObject.captureEvent)(server, event, data.logger, requestAttribution);
|
|
249
|
-
return
|
|
250
|
-
tools
|
|
251
|
-
};
|
|
252
|
+
return response;
|
|
252
253
|
}
|
|
253
|
-
event.response =
|
|
254
|
-
tools
|
|
255
|
-
};
|
|
254
|
+
event.response = response;
|
|
256
255
|
event.listedToolNames = collectListedToolNames(tools);
|
|
257
256
|
event.isError = false;
|
|
258
257
|
event.duration = Date.now() - startTime.getTime();
|
|
259
258
|
(0, external_capture_js_namespaceObject.captureEvent)(server, event, data.logger, requestAttribution);
|
|
260
|
-
return
|
|
261
|
-
tools
|
|
262
|
-
};
|
|
259
|
+
return response;
|
|
263
260
|
}
|
|
264
261
|
function cacheToolAnalyticsParameterOwnership(cache, tools) {
|
|
265
262
|
for (const tool of tools)if (tool?.name) cache.set(tool.name, (0, external_analytics_parameters_js_namespaceObject.getAnalyticsParameterOwnership)(tool.inputSchema, tool.outputSchema));
|
|
@@ -300,7 +297,10 @@ async function getTracedToolsList(server, originalListToolsHandler, request, ext
|
|
|
300
297
|
cacheToolDescriptions(data.toolDescriptions, tools);
|
|
301
298
|
cacheToolCategories(data.toolCategories, tools);
|
|
302
299
|
}
|
|
303
|
-
return
|
|
300
|
+
return {
|
|
301
|
+
...originalResponse,
|
|
302
|
+
tools
|
|
303
|
+
};
|
|
304
304
|
} catch (error) {
|
|
305
305
|
logger(`Warning: Original list tools handler failed, this suggests an error PostHog MCP analytics did not cause - ${error}`);
|
|
306
306
|
event.error = (0, external_exceptions_js_namespaceObject.captureException)(error);
|
|
@@ -20,10 +20,11 @@ async function captureToolCall(params) {
|
|
|
20
20
|
const { server, data, request, extra, execute, parameterOwnership, eventType, explicitContextIntent, takeCapturedError } = params;
|
|
21
21
|
const resolvedEventType = eventType ?? MCPAnalyticsEventType.mcpToolsCall;
|
|
22
22
|
const ownership = getActiveAnalyticsParameterOwnership(data, request.params?.name, parameterOwnership, resolvedEventType === MCPAnalyticsEventType.mcpMissingCapability);
|
|
23
|
+
const canCaptureContextIntent = resolvedEventType !== MCPAnalyticsEventType.mcpMissingCapability && isContextEnabled(data.options.context) && (ownership.context || !ownership.contextOwnershipKnown);
|
|
23
24
|
const conversation = resolveConversationId(ownership.conversationId, request.params?.arguments);
|
|
24
25
|
const downstreamRequest = cloneRequestWithoutOwnedAnalyticsArguments(request, ownership);
|
|
25
26
|
const startTime = new Date();
|
|
26
|
-
const preparedEvent = await prepareToolCallEvent(server, data, request, downstreamRequest, extra, startTime, conversation, ownership, resolvedEventType);
|
|
27
|
+
const preparedEvent = await prepareToolCallEvent(server, data, request, downstreamRequest, extra, startTime, conversation, ownership, resolvedEventType, canCaptureContextIntent);
|
|
27
28
|
if (preparedEvent && explicitContextIntent) setExplicitContextIntent(preparedEvent.event, explicitContextIntent);
|
|
28
29
|
let result;
|
|
29
30
|
try {
|
|
@@ -40,6 +41,7 @@ function getActiveAnalyticsParameterOwnership(data, toolName, override, isMissin
|
|
|
40
41
|
const listed = toolName ? data.toolAnalyticsParameterOwnership.get(toolName) : void 0;
|
|
41
42
|
const ownership = override ?? listed;
|
|
42
43
|
return {
|
|
44
|
+
contextOwnershipKnown: void 0 !== ownership,
|
|
43
45
|
context: !isMissingCapabilityTool && isContextEnabled(data.options.context) && ownership?.context === true,
|
|
44
46
|
conversationId: true === data.options.enableConversationId && ownership?.conversationId === true,
|
|
45
47
|
outputInstructions: true === data.options.enableConversationId && listed?.outputInstructions === true
|
|
@@ -57,7 +59,7 @@ function cloneRequestWithoutOwnedAnalyticsArguments(request, ownership) {
|
|
|
57
59
|
}
|
|
58
60
|
};
|
|
59
61
|
}
|
|
60
|
-
async function prepareToolCallEvent(server, data, request, downstreamRequest, extra, startTime, conversation, ownership, eventType) {
|
|
62
|
+
async function prepareToolCallEvent(server, data, request, downstreamRequest, extra, startTime, conversation, ownership, eventType, canCaptureContextIntent) {
|
|
61
63
|
try {
|
|
62
64
|
const sessionId = getSessionId(server, extra, conversation.conversationId);
|
|
63
65
|
const sessionInfo = getSessionInfo(server, data, sessionId);
|
|
@@ -77,7 +79,7 @@ async function prepareToolCallEvent(server, data, request, downstreamRequest, ex
|
|
|
77
79
|
stampClientIdentity(event, request, extra, server);
|
|
78
80
|
stampTransportIdentity(event, extra);
|
|
79
81
|
await applyResolvedMetadata(event, data, request, extra);
|
|
80
|
-
setEventIntent(event, await resolveToolCallIntent(data, request,
|
|
82
|
+
setEventIntent(event, await resolveToolCallIntent(data, request, canCaptureContextIntent, extra));
|
|
81
83
|
return {
|
|
82
84
|
event,
|
|
83
85
|
requestAttribution
|
|
@@ -187,7 +189,7 @@ async function handleListToolsRequest(server, originalListToolsHandler, request,
|
|
|
187
189
|
const data = getServerTrackingData(server);
|
|
188
190
|
const startTime = new Date();
|
|
189
191
|
const sessionId = getSessionId(server, extra);
|
|
190
|
-
const
|
|
192
|
+
const sessionInfo = getSessionInfo(server, data, sessionId);
|
|
191
193
|
const event = {
|
|
192
194
|
sessionId,
|
|
193
195
|
parameters: buildCapturedMcpParameters(request),
|
|
@@ -196,13 +198,14 @@ async function handleListToolsRequest(server, originalListToolsHandler, request,
|
|
|
196
198
|
};
|
|
197
199
|
stampClientIdentity(event, request, extra, server);
|
|
198
200
|
stampTransportIdentity(event, extra);
|
|
201
|
+
const identity = data ? await handleIdentify(server, data, sessionId, request, sessionInfo, extra) : void 0;
|
|
202
|
+
const requestAttribution = withIdentity(sessionInfo, identity);
|
|
199
203
|
if (data) await applyResolvedMetadata(event, data, request, extra);
|
|
200
|
-
const
|
|
204
|
+
const response = await getTracedToolsList(server, originalListToolsHandler, request, extra, event, logger, requestAttribution);
|
|
205
|
+
const tools = response.tools;
|
|
201
206
|
if (!data) {
|
|
202
207
|
logger('Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called instrument(server, options) before using tool calls.');
|
|
203
|
-
return
|
|
204
|
-
tools
|
|
205
|
-
};
|
|
208
|
+
return response;
|
|
206
209
|
}
|
|
207
210
|
if (0 === tools.length) {
|
|
208
211
|
data.logger('Warning: No tools found in the original list. This is likely due to the tools not being registered before PostHog MCP analytics.instrument().');
|
|
@@ -210,20 +213,14 @@ async function handleListToolsRequest(server, originalListToolsHandler, request,
|
|
|
210
213
|
event.isError = true;
|
|
211
214
|
event.duration = Date.now() - startTime.getTime();
|
|
212
215
|
captureEvent(server, event, data.logger, requestAttribution);
|
|
213
|
-
return
|
|
214
|
-
tools
|
|
215
|
-
};
|
|
216
|
+
return response;
|
|
216
217
|
}
|
|
217
|
-
event.response =
|
|
218
|
-
tools
|
|
219
|
-
};
|
|
218
|
+
event.response = response;
|
|
220
219
|
event.listedToolNames = collectListedToolNames(tools);
|
|
221
220
|
event.isError = false;
|
|
222
221
|
event.duration = Date.now() - startTime.getTime();
|
|
223
222
|
captureEvent(server, event, data.logger, requestAttribution);
|
|
224
|
-
return
|
|
225
|
-
tools
|
|
226
|
-
};
|
|
223
|
+
return response;
|
|
227
224
|
}
|
|
228
225
|
function cacheToolAnalyticsParameterOwnership(cache, tools) {
|
|
229
226
|
for (const tool of tools)if (tool?.name) cache.set(tool.name, getAnalyticsParameterOwnership(tool.inputSchema, tool.outputSchema));
|
|
@@ -264,7 +261,10 @@ async function getTracedToolsList(server, originalListToolsHandler, request, ext
|
|
|
264
261
|
cacheToolDescriptions(data.toolDescriptions, tools);
|
|
265
262
|
cacheToolCategories(data.toolCategories, tools);
|
|
266
263
|
}
|
|
267
|
-
return
|
|
264
|
+
return {
|
|
265
|
+
...originalResponse,
|
|
266
|
+
tools
|
|
267
|
+
};
|
|
268
268
|
} catch (error) {
|
|
269
269
|
logger(`Warning: Original list tools handler failed, this suggests an error PostHog MCP analytics did not cause - ${error}`);
|
|
270
270
|
event.error = captureException(error);
|
|
@@ -3,7 +3,7 @@ interface ResolvedIntent {
|
|
|
3
3
|
intent: string;
|
|
4
4
|
source: MCPAnalyticsIntentSource;
|
|
5
5
|
}
|
|
6
|
-
export declare function resolveToolCallIntent(data: MCPAnalyticsData, request: MCPRequestLike,
|
|
6
|
+
export declare function resolveToolCallIntent(data: MCPAnalyticsData, request: MCPRequestLike, canCaptureContext: boolean, extra?: CompatibleRequestHandlerExtra): Promise<ResolvedIntent | null>;
|
|
7
7
|
export declare function setEventIntent(event: McpEvent, resolvedIntent: ResolvedIntent | null): void;
|
|
8
8
|
export declare function setExplicitContextIntent(event: McpEvent, context: string): void;
|
|
9
9
|
export {};
|
|
@@ -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,
|
|
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,iBAAiB,EAAE,OAAO,EAC1B,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"}
|
|
@@ -51,9 +51,9 @@ async function runIntentFallback(data, request, extra) {
|
|
|
51
51
|
return null;
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
-
async function resolveToolCallIntent(data, request,
|
|
54
|
+
async function resolveToolCallIntent(data, request, canCaptureContext, extra) {
|
|
55
55
|
const contextArgument = getContextArgument(request);
|
|
56
|
-
if (
|
|
56
|
+
if (canCaptureContext && (0, external_context_parameters_js_namespaceObject.isContextEnabled)(data.options.context) && contextArgument) return {
|
|
57
57
|
intent: contextArgument,
|
|
58
58
|
source: 'context_parameter'
|
|
59
59
|
};
|
|
@@ -21,9 +21,9 @@ async function runIntentFallback(data, request, extra) {
|
|
|
21
21
|
return null;
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
-
async function resolveToolCallIntent(data, request,
|
|
24
|
+
async function resolveToolCallIntent(data, request, canCaptureContext, extra) {
|
|
25
25
|
const contextArgument = getContextArgument(request);
|
|
26
|
-
if (
|
|
26
|
+
if (canCaptureContext && 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.11.
|
|
1
|
+
export declare const version = "0.11.5";
|
|
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.11.
|
|
29
|
+
const version = '0.11.5';
|
|
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.11.
|
|
1
|
+
const version = '0.11.5';
|
|
2
2
|
export { version };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@posthog/mcp",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.5",
|
|
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.
|
|
31
|
+
"@posthog/core": "^1.48.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@babel/preset-env": "^7.27.1",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"@types/node": "^20.0.0",
|
|
41
41
|
"jest": "29.7.0",
|
|
42
42
|
"zod": "^3.25.0",
|
|
43
|
-
"posthog-
|
|
44
|
-
"
|
|
43
|
+
"@posthog-tooling/tsconfig-base": "1.1.1",
|
|
44
|
+
"posthog-node": "^5.49.0"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@modelcontextprotocol/sdk": ">=1.26.0",
|
|
@@ -109,6 +109,14 @@ export async function captureToolCall(params: TraceToolCallParams): Promise<unkn
|
|
|
109
109
|
parameterOwnership,
|
|
110
110
|
resolvedEventType === MCPAnalyticsEventType.mcpMissingCapability
|
|
111
111
|
)
|
|
112
|
+
// Reading the argument and removing it are separate questions: deleting one the
|
|
113
|
+
// application declared costs the customer their call, so the strip below still
|
|
114
|
+
// requires positive ownership, while reading it when ownership is unresolved
|
|
115
|
+
// costs at worst a mislabelled property. ADR-0011.
|
|
116
|
+
const canCaptureContextIntent =
|
|
117
|
+
resolvedEventType !== MCPAnalyticsEventType.mcpMissingCapability &&
|
|
118
|
+
isContextEnabled(data.options.context) &&
|
|
119
|
+
(ownership.context || !ownership.contextOwnershipKnown)
|
|
112
120
|
const conversation = resolveConversationId(ownership.conversationId, request.params?.arguments)
|
|
113
121
|
const downstreamRequest = cloneRequestWithoutOwnedAnalyticsArguments(request, ownership)
|
|
114
122
|
|
|
@@ -124,7 +132,8 @@ export async function captureToolCall(params: TraceToolCallParams): Promise<unkn
|
|
|
124
132
|
startTime,
|
|
125
133
|
conversation,
|
|
126
134
|
ownership,
|
|
127
|
-
resolvedEventType
|
|
135
|
+
resolvedEventType,
|
|
136
|
+
canCaptureContextIntent
|
|
128
137
|
)
|
|
129
138
|
if (preparedEvent && explicitContextIntent) {
|
|
130
139
|
setExplicitContextIntent(preparedEvent.event, explicitContextIntent)
|
|
@@ -150,15 +159,24 @@ interface PreparedToolEvent {
|
|
|
150
159
|
requestAttribution: SessionInfo
|
|
151
160
|
}
|
|
152
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Ownership for one request, plus whether it could be resolved at all — "no
|
|
164
|
+
* answer" must not read the same as "the application owns it".
|
|
165
|
+
*/
|
|
166
|
+
interface ActiveAnalyticsParameterOwnership extends AnalyticsParameterOwnership {
|
|
167
|
+
contextOwnershipKnown: boolean
|
|
168
|
+
}
|
|
169
|
+
|
|
153
170
|
function getActiveAnalyticsParameterOwnership(
|
|
154
171
|
data: MCPAnalyticsData,
|
|
155
172
|
toolName: string | undefined,
|
|
156
173
|
override: AnalyticsParameterOwnership | undefined,
|
|
157
174
|
isMissingCapabilityTool: boolean
|
|
158
|
-
):
|
|
175
|
+
): ActiveAnalyticsParameterOwnership {
|
|
159
176
|
const listed = toolName ? data.toolAnalyticsParameterOwnership.get(toolName) : undefined
|
|
160
177
|
const ownership = override ?? listed
|
|
161
178
|
return {
|
|
179
|
+
contextOwnershipKnown: ownership !== undefined,
|
|
162
180
|
context: !isMissingCapabilityTool && isContextEnabled(data.options.context) && ownership?.context === true,
|
|
163
181
|
conversationId: data.options.enableConversationId === true && ownership?.conversationId === true,
|
|
164
182
|
// Deliberately read off `listed`, never the override: only the advertised
|
|
@@ -199,7 +217,8 @@ async function prepareToolCallEvent(
|
|
|
199
217
|
startTime: Date,
|
|
200
218
|
conversation: ConversationIdResolution,
|
|
201
219
|
ownership: AnalyticsParameterOwnership,
|
|
202
|
-
eventType: MCPAnalyticsEventType
|
|
220
|
+
eventType: MCPAnalyticsEventType,
|
|
221
|
+
canCaptureContextIntent: boolean
|
|
203
222
|
): Promise<PreparedToolEvent | null> {
|
|
204
223
|
try {
|
|
205
224
|
const sessionId = getSessionId(server, extra, conversation.conversationId)
|
|
@@ -237,7 +256,7 @@ async function prepareToolCallEvent(
|
|
|
237
256
|
stampTransportIdentity(event, extra)
|
|
238
257
|
|
|
239
258
|
await applyResolvedMetadata(event, data, request, extra)
|
|
240
|
-
setEventIntent(event, await resolveToolCallIntent(data, request,
|
|
259
|
+
setEventIntent(event, await resolveToolCallIntent(data, request, canCaptureContextIntent, extra))
|
|
241
260
|
return { event, requestAttribution }
|
|
242
261
|
} catch (error) {
|
|
243
262
|
data.logger(
|
|
@@ -511,27 +530,39 @@ export async function handleListToolsRequest(
|
|
|
511
530
|
request: MCPRequestLike,
|
|
512
531
|
extra: CompatibleRequestHandlerExtra | undefined,
|
|
513
532
|
logger: LoggerFn
|
|
514
|
-
): Promise<
|
|
533
|
+
): Promise<CompatibleToolsListLike> {
|
|
515
534
|
const data = getServerTrackingData(server)
|
|
516
535
|
const startTime = new Date()
|
|
517
536
|
const sessionId = getSessionId(server, extra)
|
|
518
|
-
// Snapshot before metadata resolution or the list handler can yield
|
|
519
|
-
// concurrent request using the same instrumented server.
|
|
520
|
-
const
|
|
537
|
+
// Snapshot before identify, metadata resolution, or the list handler can yield
|
|
538
|
+
// to a concurrent request using the same instrumented server.
|
|
539
|
+
const sessionInfo = getSessionInfo(server, data, sessionId)
|
|
521
540
|
const event: McpEvent = {
|
|
522
541
|
sessionId,
|
|
523
542
|
parameters: buildCapturedMcpParameters(request),
|
|
524
543
|
eventType: MCPAnalyticsEventType.mcpToolsList,
|
|
525
544
|
timestamp: startTime,
|
|
526
545
|
}
|
|
546
|
+
// Stamp before the identify await below. The last link of the identity chain is
|
|
547
|
+
// the server's own `getClientVersion()`, which a concurrent `initialize` can
|
|
548
|
+
// replace while a slow identify callback is in flight — and `captureEvent`
|
|
549
|
+
// prefers a stamped value over the `sessionInfo` snapshot, so a late stamp
|
|
550
|
+
// would win with the wrong client's name.
|
|
527
551
|
stampClientIdentity(event, request, extra, server)
|
|
528
552
|
stampTransportIdentity(event, extra)
|
|
529
553
|
|
|
554
|
+
// `getSessionInfo` only surfaces an identity some earlier request already
|
|
555
|
+
// cached on this instance. On a per-request instance that cache is always
|
|
556
|
+
// empty, so without resolving `identify` here too, `tools/list` would be the
|
|
557
|
+
// one request path that never attributes to a person.
|
|
558
|
+
const identity = data ? await handleIdentify(server, data, sessionId, request, sessionInfo, extra) : undefined
|
|
559
|
+
const requestAttribution = withIdentity(sessionInfo, identity)
|
|
560
|
+
|
|
530
561
|
if (data) {
|
|
531
562
|
await applyResolvedMetadata(event, data, request, extra)
|
|
532
563
|
}
|
|
533
564
|
|
|
534
|
-
const
|
|
565
|
+
const response = await getTracedToolsList(
|
|
535
566
|
server,
|
|
536
567
|
originalListToolsHandler,
|
|
537
568
|
request,
|
|
@@ -540,12 +571,13 @@ export async function handleListToolsRequest(
|
|
|
540
571
|
logger,
|
|
541
572
|
requestAttribution
|
|
542
573
|
)
|
|
574
|
+
const tools = response.tools
|
|
543
575
|
|
|
544
576
|
if (!data) {
|
|
545
577
|
logger(
|
|
546
578
|
'Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called instrument(server, options) before using tool calls.'
|
|
547
579
|
)
|
|
548
|
-
return
|
|
580
|
+
return response
|
|
549
581
|
}
|
|
550
582
|
|
|
551
583
|
if (tools.length === 0) {
|
|
@@ -556,15 +588,15 @@ export async function handleListToolsRequest(
|
|
|
556
588
|
event.isError = true
|
|
557
589
|
event.duration = Date.now() - startTime.getTime()
|
|
558
590
|
captureEvent(server, event, data.logger, requestAttribution)
|
|
559
|
-
return
|
|
591
|
+
return response
|
|
560
592
|
}
|
|
561
593
|
|
|
562
|
-
event.response =
|
|
594
|
+
event.response = response
|
|
563
595
|
event.listedToolNames = collectListedToolNames(tools)
|
|
564
596
|
event.isError = false
|
|
565
597
|
event.duration = Date.now() - startTime.getTime()
|
|
566
598
|
captureEvent(server, event, data.logger, requestAttribution)
|
|
567
|
-
return
|
|
599
|
+
return response
|
|
568
600
|
}
|
|
569
601
|
|
|
570
602
|
function cacheToolAnalyticsParameterOwnership(
|
|
@@ -595,7 +627,7 @@ async function getTracedToolsList(
|
|
|
595
627
|
event: McpEvent,
|
|
596
628
|
logger: LoggerFn,
|
|
597
629
|
requestAttribution: SessionInfo
|
|
598
|
-
): Promise<CompatibleToolsListLike
|
|
630
|
+
): Promise<CompatibleToolsListLike> {
|
|
599
631
|
try {
|
|
600
632
|
const data = getServerTrackingData(server)
|
|
601
633
|
const originalResponse = (await originalListToolsHandler(request, extra)) as CompatibleToolsListLike
|
|
@@ -637,7 +669,8 @@ async function getTracedToolsList(
|
|
|
637
669
|
cacheToolCategories(data.toolCategories, tools)
|
|
638
670
|
}
|
|
639
671
|
|
|
640
|
-
|
|
672
|
+
// Spread, never enumerate — fields later revisions add survive by default.
|
|
673
|
+
return { ...originalResponse, tools }
|
|
641
674
|
} catch (error) {
|
|
642
675
|
logger(
|
|
643
676
|
`Warning: Original list tools handler failed, this suggests an error PostHog MCP analytics did not cause - ${error}`
|
package/src/extensions/intent.ts
CHANGED
|
@@ -52,11 +52,11 @@ async function runIntentFallback(
|
|
|
52
52
|
export async function resolveToolCallIntent(
|
|
53
53
|
data: MCPAnalyticsData,
|
|
54
54
|
request: MCPRequestLike,
|
|
55
|
-
|
|
55
|
+
canCaptureContext: boolean,
|
|
56
56
|
extra?: CompatibleRequestHandlerExtra
|
|
57
57
|
): Promise<ResolvedIntent | null> {
|
|
58
58
|
const contextArgument = getContextArgument(request)
|
|
59
|
-
if (
|
|
59
|
+
if (canCaptureContext && isContextEnabled(data.options.context) && contextArgument) {
|
|
60
60
|
return { intent: contextArgument, source: 'context_parameter' }
|
|
61
61
|
}
|
|
62
62
|
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '0.11.
|
|
1
|
+
export const version = '0.11.5'
|