@posthog/mcp 0.11.4 → 0.11.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/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 +24 -22
- package/dist/extensions/instrumentation.mjs +24 -22
- 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 +86 -32
- 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,12 +95,10 @@ 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);
|
|
100
|
-
const identity = await (0, external_internal_js_namespaceObject.handleIdentify)(server, data, sessionId, request, sessionInfo, extra, !!conversation.conversationId);
|
|
101
|
-
const requestAttribution = (0, external_internal_js_namespaceObject.withIdentity)(sessionInfo, identity);
|
|
102
102
|
const toolName = request.params?.name;
|
|
103
103
|
const event = {
|
|
104
104
|
sessionId,
|
|
@@ -112,8 +112,10 @@ async function prepareToolCallEvent(server, data, request, downstreamRequest, ex
|
|
|
112
112
|
};
|
|
113
113
|
(0, external_client_identity_js_namespaceObject.stampClientIdentity)(event, request, extra, server);
|
|
114
114
|
(0, external_transport_identity_js_namespaceObject.stampTransportIdentity)(event, extra);
|
|
115
|
+
const identity = await (0, external_internal_js_namespaceObject.handleIdentify)(server, data, sessionId, request, sessionInfo, extra, !!conversation.conversationId);
|
|
116
|
+
const requestAttribution = (0, external_internal_js_namespaceObject.withIdentity)(sessionInfo, identity);
|
|
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);
|
|
@@ -395,8 +395,6 @@ async function handleInitializeRequest(server, originalInitializeHandler, reques
|
|
|
395
395
|
clientName: initializeClientInfo?.name ?? sessionInfo.clientName,
|
|
396
396
|
clientVersion: initializeClientInfo?.version ?? sessionInfo.clientVersion
|
|
397
397
|
};
|
|
398
|
-
const identity = await (0, external_internal_js_namespaceObject.handleIdentify)(server, data, sessionId, request, requestSessionInfo, extra);
|
|
399
|
-
const requestAttribution = (0, external_internal_js_namespaceObject.withIdentity)(requestSessionInfo, identity);
|
|
400
398
|
const event = {
|
|
401
399
|
sessionId,
|
|
402
400
|
resourceName: request.params?.name || 'Unknown Tool Name',
|
|
@@ -405,7 +403,11 @@ async function handleInitializeRequest(server, originalInitializeHandler, reques
|
|
|
405
403
|
timestamp: new Date()
|
|
406
404
|
};
|
|
407
405
|
(0, external_client_identity_js_namespaceObject.stampClientIdentity)(event, request, extra, server);
|
|
406
|
+
if (initializeClientInfo?.name) event.clientName = initializeClientInfo.name;
|
|
407
|
+
if (initializeClientInfo?.version) event.clientVersion = initializeClientInfo.version;
|
|
408
408
|
(0, external_transport_identity_js_namespaceObject.stampTransportIdentity)(event, extra);
|
|
409
|
+
const identity = await (0, external_internal_js_namespaceObject.handleIdentify)(server, data, sessionId, request, requestSessionInfo, extra);
|
|
410
|
+
const requestAttribution = (0, external_internal_js_namespaceObject.withIdentity)(requestSessionInfo, identity);
|
|
409
411
|
await (0, external_tracing_helpers_js_namespaceObject.applyResolvedMetadata)(event, data, request, extra);
|
|
410
412
|
const result = await originalInitializeHandler(request, extra);
|
|
411
413
|
event.response = result;
|
|
@@ -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,12 +59,10 @@ 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);
|
|
64
|
-
const identity = await handleIdentify(server, data, sessionId, request, sessionInfo, extra, !!conversation.conversationId);
|
|
65
|
-
const requestAttribution = withIdentity(sessionInfo, identity);
|
|
66
66
|
const toolName = request.params?.name;
|
|
67
67
|
const event = {
|
|
68
68
|
sessionId,
|
|
@@ -76,8 +76,10 @@ async function prepareToolCallEvent(server, data, request, downstreamRequest, ex
|
|
|
76
76
|
};
|
|
77
77
|
stampClientIdentity(event, request, extra, server);
|
|
78
78
|
stampTransportIdentity(event, extra);
|
|
79
|
+
const identity = await handleIdentify(server, data, sessionId, request, sessionInfo, extra, !!conversation.conversationId);
|
|
80
|
+
const requestAttribution = withIdentity(sessionInfo, identity);
|
|
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);
|
|
@@ -359,8 +359,6 @@ async function handleInitializeRequest(server, originalInitializeHandler, reques
|
|
|
359
359
|
clientName: initializeClientInfo?.name ?? sessionInfo.clientName,
|
|
360
360
|
clientVersion: initializeClientInfo?.version ?? sessionInfo.clientVersion
|
|
361
361
|
};
|
|
362
|
-
const identity = await handleIdentify(server, data, sessionId, request, requestSessionInfo, extra);
|
|
363
|
-
const requestAttribution = withIdentity(requestSessionInfo, identity);
|
|
364
362
|
const event = {
|
|
365
363
|
sessionId,
|
|
366
364
|
resourceName: request.params?.name || 'Unknown Tool Name',
|
|
@@ -369,7 +367,11 @@ async function handleInitializeRequest(server, originalInitializeHandler, reques
|
|
|
369
367
|
timestamp: new Date()
|
|
370
368
|
};
|
|
371
369
|
stampClientIdentity(event, request, extra, server);
|
|
370
|
+
if (initializeClientInfo?.name) event.clientName = initializeClientInfo.name;
|
|
371
|
+
if (initializeClientInfo?.version) event.clientVersion = initializeClientInfo.version;
|
|
372
372
|
stampTransportIdentity(event, extra);
|
|
373
|
+
const identity = await handleIdentify(server, data, sessionId, request, requestSessionInfo, extra);
|
|
374
|
+
const requestAttribution = withIdentity(requestSessionInfo, identity);
|
|
373
375
|
await applyResolvedMetadata(event, data, request, extra);
|
|
374
376
|
const result = await originalInitializeHandler(request, extra);
|
|
375
377
|
event.response = result;
|
|
@@ -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.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.11.
|
|
29
|
+
const version = '0.11.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.11.
|
|
1
|
+
const version = '0.11.6';
|
|
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.6",
|
|
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,23 +217,14 @@ 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)
|
|
206
225
|
// Snapshot token/client/protocol metadata synchronously, before identify or
|
|
207
226
|
// metadata callbacks can yield and let another request replace shared state.
|
|
208
227
|
const sessionInfo = getSessionInfo(server, data, sessionId)
|
|
209
|
-
const identity = await handleIdentify(
|
|
210
|
-
server,
|
|
211
|
-
data,
|
|
212
|
-
sessionId,
|
|
213
|
-
request,
|
|
214
|
-
sessionInfo,
|
|
215
|
-
extra,
|
|
216
|
-
!!conversation.conversationId
|
|
217
|
-
)
|
|
218
|
-
const requestAttribution = withIdentity(sessionInfo, identity)
|
|
219
228
|
|
|
220
229
|
const toolName = request.params?.name
|
|
221
230
|
const event: McpEvent = {
|
|
@@ -230,14 +239,30 @@ async function prepareToolCallEvent(
|
|
|
230
239
|
}
|
|
231
240
|
// Modern (stateless) clients carry client name/version + protocol version in
|
|
232
241
|
// `_meta` on every request rather than at `initialize`; stamp them onto this
|
|
233
|
-
// event now so concurrent requests can't cross-attribute it.
|
|
242
|
+
// event now so concurrent requests can't cross-attribute it. Stamped before
|
|
243
|
+
// the identify await below for the same reason the snapshot is: the chain's
|
|
244
|
+
// last link is the server's own `getClientVersion()`, which a concurrent
|
|
245
|
+
// `initialize` can replace while an identify callback is in flight — and
|
|
246
|
+
// `captureEvent` prefers a stamped value over `sessionInfo`, so a late stamp
|
|
247
|
+
// would win with the wrong client's name.
|
|
234
248
|
stampClientIdentity(event, request, extra, server)
|
|
235
249
|
// Which *surface* of the client made this call lives only in the request
|
|
236
250
|
// headers (HTTP transports); `clientInfo` can't tell a vendor's products apart.
|
|
237
251
|
stampTransportIdentity(event, extra)
|
|
238
252
|
|
|
253
|
+
const identity = await handleIdentify(
|
|
254
|
+
server,
|
|
255
|
+
data,
|
|
256
|
+
sessionId,
|
|
257
|
+
request,
|
|
258
|
+
sessionInfo,
|
|
259
|
+
extra,
|
|
260
|
+
!!conversation.conversationId
|
|
261
|
+
)
|
|
262
|
+
const requestAttribution = withIdentity(sessionInfo, identity)
|
|
263
|
+
|
|
239
264
|
await applyResolvedMetadata(event, data, request, extra)
|
|
240
|
-
setEventIntent(event, await resolveToolCallIntent(data, request,
|
|
265
|
+
setEventIntent(event, await resolveToolCallIntent(data, request, canCaptureContextIntent, extra))
|
|
241
266
|
return { event, requestAttribution }
|
|
242
267
|
} catch (error) {
|
|
243
268
|
data.logger(
|
|
@@ -511,27 +536,39 @@ export async function handleListToolsRequest(
|
|
|
511
536
|
request: MCPRequestLike,
|
|
512
537
|
extra: CompatibleRequestHandlerExtra | undefined,
|
|
513
538
|
logger: LoggerFn
|
|
514
|
-
): Promise<
|
|
539
|
+
): Promise<CompatibleToolsListLike> {
|
|
515
540
|
const data = getServerTrackingData(server)
|
|
516
541
|
const startTime = new Date()
|
|
517
542
|
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
|
|
543
|
+
// Snapshot before identify, metadata resolution, or the list handler can yield
|
|
544
|
+
// to a concurrent request using the same instrumented server.
|
|
545
|
+
const sessionInfo = getSessionInfo(server, data, sessionId)
|
|
521
546
|
const event: McpEvent = {
|
|
522
547
|
sessionId,
|
|
523
548
|
parameters: buildCapturedMcpParameters(request),
|
|
524
549
|
eventType: MCPAnalyticsEventType.mcpToolsList,
|
|
525
550
|
timestamp: startTime,
|
|
526
551
|
}
|
|
552
|
+
// Stamp before the identify await below. The last link of the identity chain is
|
|
553
|
+
// the server's own `getClientVersion()`, which a concurrent `initialize` can
|
|
554
|
+
// replace while a slow identify callback is in flight — and `captureEvent`
|
|
555
|
+
// prefers a stamped value over the `sessionInfo` snapshot, so a late stamp
|
|
556
|
+
// would win with the wrong client's name.
|
|
527
557
|
stampClientIdentity(event, request, extra, server)
|
|
528
558
|
stampTransportIdentity(event, extra)
|
|
529
559
|
|
|
560
|
+
// `getSessionInfo` only surfaces an identity some earlier request already
|
|
561
|
+
// cached on this instance. On a per-request instance that cache is always
|
|
562
|
+
// empty, so without resolving `identify` here too, `tools/list` would be the
|
|
563
|
+
// one request path that never attributes to a person.
|
|
564
|
+
const identity = data ? await handleIdentify(server, data, sessionId, request, sessionInfo, extra) : undefined
|
|
565
|
+
const requestAttribution = withIdentity(sessionInfo, identity)
|
|
566
|
+
|
|
530
567
|
if (data) {
|
|
531
568
|
await applyResolvedMetadata(event, data, request, extra)
|
|
532
569
|
}
|
|
533
570
|
|
|
534
|
-
const
|
|
571
|
+
const response = await getTracedToolsList(
|
|
535
572
|
server,
|
|
536
573
|
originalListToolsHandler,
|
|
537
574
|
request,
|
|
@@ -540,12 +577,13 @@ export async function handleListToolsRequest(
|
|
|
540
577
|
logger,
|
|
541
578
|
requestAttribution
|
|
542
579
|
)
|
|
580
|
+
const tools = response.tools
|
|
543
581
|
|
|
544
582
|
if (!data) {
|
|
545
583
|
logger(
|
|
546
584
|
'Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called instrument(server, options) before using tool calls.'
|
|
547
585
|
)
|
|
548
|
-
return
|
|
586
|
+
return response
|
|
549
587
|
}
|
|
550
588
|
|
|
551
589
|
if (tools.length === 0) {
|
|
@@ -556,15 +594,15 @@ export async function handleListToolsRequest(
|
|
|
556
594
|
event.isError = true
|
|
557
595
|
event.duration = Date.now() - startTime.getTime()
|
|
558
596
|
captureEvent(server, event, data.logger, requestAttribution)
|
|
559
|
-
return
|
|
597
|
+
return response
|
|
560
598
|
}
|
|
561
599
|
|
|
562
|
-
event.response =
|
|
600
|
+
event.response = response
|
|
563
601
|
event.listedToolNames = collectListedToolNames(tools)
|
|
564
602
|
event.isError = false
|
|
565
603
|
event.duration = Date.now() - startTime.getTime()
|
|
566
604
|
captureEvent(server, event, data.logger, requestAttribution)
|
|
567
|
-
return
|
|
605
|
+
return response
|
|
568
606
|
}
|
|
569
607
|
|
|
570
608
|
function cacheToolAnalyticsParameterOwnership(
|
|
@@ -595,7 +633,7 @@ async function getTracedToolsList(
|
|
|
595
633
|
event: McpEvent,
|
|
596
634
|
logger: LoggerFn,
|
|
597
635
|
requestAttribution: SessionInfo
|
|
598
|
-
): Promise<CompatibleToolsListLike
|
|
636
|
+
): Promise<CompatibleToolsListLike> {
|
|
599
637
|
try {
|
|
600
638
|
const data = getServerTrackingData(server)
|
|
601
639
|
const originalResponse = (await originalListToolsHandler(request, extra)) as CompatibleToolsListLike
|
|
@@ -637,7 +675,8 @@ async function getTracedToolsList(
|
|
|
637
675
|
cacheToolCategories(data.toolCategories, tools)
|
|
638
676
|
}
|
|
639
677
|
|
|
640
|
-
|
|
678
|
+
// Spread, never enumerate — fields later revisions add survive by default.
|
|
679
|
+
return { ...originalResponse, tools }
|
|
641
680
|
} catch (error) {
|
|
642
681
|
logger(
|
|
643
682
|
`Warning: Original list tools handler failed, this suggests an error PostHog MCP analytics did not cause - ${error}`
|
|
@@ -837,9 +876,6 @@ export async function handleInitializeRequest(
|
|
|
837
876
|
clientName: initializeClientInfo?.name ?? sessionInfo.clientName,
|
|
838
877
|
clientVersion: initializeClientInfo?.version ?? sessionInfo.clientVersion,
|
|
839
878
|
}
|
|
840
|
-
const identity = await handleIdentify(server, data, sessionId, request, requestSessionInfo, extra)
|
|
841
|
-
const requestAttribution = withIdentity(requestSessionInfo, identity)
|
|
842
|
-
|
|
843
879
|
const event: McpEvent = {
|
|
844
880
|
sessionId,
|
|
845
881
|
resourceName: request.params?.name || 'Unknown Tool Name',
|
|
@@ -847,12 +883,30 @@ export async function handleInitializeRequest(
|
|
|
847
883
|
parameters: buildCapturedMcpParameters(request),
|
|
848
884
|
timestamp: new Date(),
|
|
849
885
|
}
|
|
850
|
-
//
|
|
851
|
-
//
|
|
852
|
-
//
|
|
886
|
+
// Picks up client info if a client also sends it in `_meta` (the negotiated
|
|
887
|
+
// protocol version below overrides any `_meta` one). Stamped before the
|
|
888
|
+
// identify await for the same reason the snapshot is taken there: the chain's
|
|
889
|
+
// last link is the server's own `getClientVersion()`, which a concurrent
|
|
890
|
+
// `initialize` can replace while an identify callback is in flight.
|
|
853
891
|
stampClientIdentity(event, request, extra, server)
|
|
892
|
+
// A legacy `initialize` carries its client in the request body, which is the
|
|
893
|
+
// one place the chain does not read. That body is this request's own answer,
|
|
894
|
+
// so it outranks the accessor — which on a server that already handshaked
|
|
895
|
+
// still names the *previous* client, because the SDK only records this one
|
|
896
|
+
// when its handler runs further below. Without this, the second and every
|
|
897
|
+
// later handshake on a shared instance reports the first client's name, since
|
|
898
|
+
// `captureEvent` prefers a stamped value over `requestSessionInfo`.
|
|
899
|
+
if (initializeClientInfo?.name) {
|
|
900
|
+
event.clientName = initializeClientInfo.name
|
|
901
|
+
}
|
|
902
|
+
if (initializeClientInfo?.version) {
|
|
903
|
+
event.clientVersion = initializeClientInfo.version
|
|
904
|
+
}
|
|
854
905
|
stampTransportIdentity(event, extra)
|
|
855
906
|
|
|
907
|
+
const identity = await handleIdentify(server, data, sessionId, request, requestSessionInfo, extra)
|
|
908
|
+
const requestAttribution = withIdentity(requestSessionInfo, identity)
|
|
909
|
+
|
|
856
910
|
await applyResolvedMetadata(event, data, request, extra)
|
|
857
911
|
|
|
858
912
|
const result = await originalInitializeHandler(request, extra)
|
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.6'
|