@posthog/mcp 0.0.3 → 0.0.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 +17 -0
- package/dist/index.cjs +177 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +177 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -52,6 +52,22 @@ track(server, {
|
|
|
52
52
|
With `enableAITracing: true`, tool calls also emit `$ai_span` events with `$ai_trace_id` and `$ai_span_id`.
|
|
53
53
|
The regular `mcp_tool_call` event includes the same trace/span IDs so it can be joined back to the LLM analytics span.
|
|
54
54
|
|
|
55
|
+
### Conversation ID (opt-in)
|
|
56
|
+
|
|
57
|
+
When `enableConversationId: true` is passed to `track()`, the SDK injects an optional `conversation_id` argument into every tool's input schema and tells the agent to reuse the value the server returns. When the agent omits it on a call, the SDK mints a UUID and appends a short text block to the tool's response asking the agent to echo the same `conversation_id` on subsequent calls. The value the agent supplies (or the SDK mints) is captured on PostHog events as `$mcp_conversation_id` — separate from `$session_id`, which is unchanged. Off by default.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
track(server, {
|
|
61
|
+
apiKey: process.env.POSTHOG_API_KEY,
|
|
62
|
+
enableConversationId: true,
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Caveats:**
|
|
67
|
+
|
|
68
|
+
- The prompt-back is appended as a regular `text` content block (the only channel MCP currently offers for server→agent communication). Consumers that surface raw tool-call content to end users will see `[SERVER]: Reuse conversation_id=…` in their UI. If/when the MCP spec grows a server-directives channel (e.g. on `_meta`), the SDK will move there.
|
|
69
|
+
- The captured `$mcp_conversation_id` value is agent-controlled when the agent supplies one — the SDK accepts any non-empty string. Customers wanting to bind it to their own session scheme (chat ID, JWT `jti`, request ID) can do so freely; the trade-off is that nothing prevents a misbehaving agent from sending arbitrary values.
|
|
70
|
+
|
|
55
71
|
The SDK sends events through `posthog-node`, so it uses the same PostHog ingestion client, batching, retry, flush, and shutdown behavior as the existing Node SDK.
|
|
56
72
|
|
|
57
73
|
If your application already owns a PostHog client, pass it in instead:
|
|
@@ -86,6 +102,7 @@ Important properties:
|
|
|
86
102
|
- `$mcp_intent` from the `context` argument, or from `intentFallback` when no context was provided
|
|
87
103
|
- `$mcp_intent_source` as `context_parameter` or `inferred`
|
|
88
104
|
- `$mcp_tool_name` and `$mcp_resource_name`
|
|
105
|
+
- `$mcp_tool_description`, the tool's current `description` at the moment of the call — populated on `mcp_tool_call` and on the paired `$exception` event so the description is visible inline while triaging
|
|
89
106
|
- `$mcp_parameters` and `$mcp_response`, after redaction and truncation
|
|
90
107
|
- `$mcp_duration_ms`
|
|
91
108
|
- `$mcp_is_error`
|
package/dist/index.cjs
CHANGED
|
@@ -350,6 +350,7 @@ function getRequestResourceName(request) {
|
|
|
350
350
|
//#endregion
|
|
351
351
|
//#region src/modules/constants.ts
|
|
352
352
|
const DEFAULT_CONTEXT_PARAMETER_DESCRIPTION = `Explain why you are calling this tool and how it fits into the user's overall goal. This parameter is used for analytics and user intent tracking. YOU MUST provide 15-25 words (count carefully). NEVER use first person ('I', 'we', 'you') - maintain third-person perspective. NEVER include sensitive information such as credentials, passwords, or personal data. Example (20 words): "Searching across the organization's repositories to find all open issues related to performance complaints and latency issues for team prioritization."`;
|
|
353
|
+
const DEFAULT_CONVERSATION_ID_DESCRIPTION = "Echo the conversation_id from the server's previous response. The server provides it on the first call — never invent one, and do not issue parallel tool calls until you have it.";
|
|
353
354
|
const POSTHOG_MCP_ANALYTICS_SOURCE = "posthog_mcp_analytics";
|
|
354
355
|
const PostHogMCPAnalyticsEvent = {
|
|
355
356
|
AiSpan: "$ai_span",
|
|
@@ -376,6 +377,7 @@ const PostHogMCPAnalyticsProperty = {
|
|
|
376
377
|
AiTraceId: "$ai_trace_id",
|
|
377
378
|
ClientName: "$mcp_client_name",
|
|
378
379
|
ClientVersion: "$mcp_client_version",
|
|
380
|
+
ConversationId: "$mcp_conversation_id",
|
|
379
381
|
DurationMs: "$mcp_duration_ms",
|
|
380
382
|
IsError: "$mcp_is_error",
|
|
381
383
|
Intent: "$mcp_intent",
|
|
@@ -387,6 +389,7 @@ const PostHogMCPAnalyticsProperty = {
|
|
|
387
389
|
ServerVersion: "$mcp_server_version",
|
|
388
390
|
SessionId: "$session_id",
|
|
389
391
|
Source: "$mcp_source",
|
|
392
|
+
ToolDescription: "$mcp_tool_description",
|
|
390
393
|
ToolName: "$mcp_tool_name"
|
|
391
394
|
};
|
|
392
395
|
//#endregion
|
|
@@ -421,6 +424,7 @@ function buildCaptureEvent(event, options) {
|
|
|
421
424
|
[PostHogMCPAnalyticsProperty.SessionId]: event.sessionId,
|
|
422
425
|
[PostHogMCPAnalyticsProperty.Source]: POSTHOG_MCP_ANALYTICS_SOURCE
|
|
423
426
|
};
|
|
427
|
+
addConversationIdProperty(event, properties);
|
|
424
428
|
addCommonEventProperties(event, properties);
|
|
425
429
|
addTraceReferenceProperties(event, properties, options);
|
|
426
430
|
addCustomEventProperties(event, properties);
|
|
@@ -446,11 +450,15 @@ function addTraceReferenceProperties(event, properties, options) {
|
|
|
446
450
|
properties[PostHogMCPAnalyticsProperty.AiTraceId] = getAITraceId(event);
|
|
447
451
|
properties[PostHogMCPAnalyticsProperty.AiSpanId] = getAISpanId(event);
|
|
448
452
|
}
|
|
453
|
+
function addConversationIdProperty(event, properties) {
|
|
454
|
+
if (event.conversationId !== void 0 && event.conversationId !== "") properties[PostHogMCPAnalyticsProperty.ConversationId] = event.conversationId;
|
|
455
|
+
}
|
|
449
456
|
function addCommonEventProperties(event, properties) {
|
|
450
457
|
if (event.resourceName) {
|
|
451
458
|
properties[PostHogMCPAnalyticsProperty.ResourceName] = event.resourceName;
|
|
452
459
|
if (event.eventType === MCPAnalyticsEventType.mcpToolsCall) properties[PostHogMCPAnalyticsProperty.ToolName] = event.resourceName;
|
|
453
460
|
}
|
|
461
|
+
if (event.toolDescription && event.eventType === MCPAnalyticsEventType.mcpToolsCall) properties[PostHogMCPAnalyticsProperty.ToolDescription] = event.toolDescription;
|
|
454
462
|
if (event.duration !== void 0) properties[PostHogMCPAnalyticsProperty.DurationMs] = event.duration;
|
|
455
463
|
if (event.serverName) properties[PostHogMCPAnalyticsProperty.ServerName] = event.serverName;
|
|
456
464
|
if (event.serverVersion) properties[PostHogMCPAnalyticsProperty.ServerVersion] = event.serverVersion;
|
|
@@ -477,6 +485,7 @@ function buildExceptionEvent(event) {
|
|
|
477
485
|
$exception_source: "backend",
|
|
478
486
|
[PostHogMCPAnalyticsProperty.SessionId]: event.sessionId
|
|
479
487
|
};
|
|
488
|
+
addConversationIdProperty(event, properties);
|
|
480
489
|
if (event.error) {
|
|
481
490
|
if (event.error.message) properties.$exception_message = event.error.message;
|
|
482
491
|
if (event.error.type) properties.$exception_type = event.error.type;
|
|
@@ -486,6 +495,7 @@ function buildExceptionEvent(event) {
|
|
|
486
495
|
properties[PostHogMCPAnalyticsProperty.ResourceName] = event.resourceName;
|
|
487
496
|
if (event.eventType === MCPAnalyticsEventType.mcpToolsCall) properties[PostHogMCPAnalyticsProperty.ToolName] = event.resourceName;
|
|
488
497
|
}
|
|
498
|
+
if (event.toolDescription && event.eventType === MCPAnalyticsEventType.mcpToolsCall) properties[PostHogMCPAnalyticsProperty.ToolDescription] = event.toolDescription;
|
|
489
499
|
if (event.serverName) properties[PostHogMCPAnalyticsProperty.ServerName] = event.serverName;
|
|
490
500
|
if (event.serverVersion) properties[PostHogMCPAnalyticsProperty.ServerVersion] = event.serverVersion;
|
|
491
501
|
if (event.clientName) properties[PostHogMCPAnalyticsProperty.ClientName] = event.clientName;
|
|
@@ -510,6 +520,7 @@ function buildAISpanEvent(event) {
|
|
|
510
520
|
[PostHogMCPAnalyticsProperty.SessionId]: event.sessionId,
|
|
511
521
|
[PostHogMCPAnalyticsProperty.Source]: POSTHOG_MCP_ANALYTICS_SOURCE
|
|
512
522
|
};
|
|
523
|
+
addConversationIdProperty(event, properties);
|
|
513
524
|
if (event.duration !== void 0) properties[PostHogMCPAnalyticsProperty.AiLatency] = event.duration / 1e3;
|
|
514
525
|
if (event.isError && event.error) properties.$ai_error = event.error;
|
|
515
526
|
if (event.parameters !== void 0) properties[PostHogMCPAnalyticsProperty.AiInputState] = event.parameters;
|
|
@@ -544,6 +555,7 @@ const PROTECTED_FIELDS = new Set([
|
|
|
544
555
|
"identifyActorName",
|
|
545
556
|
"identifyData",
|
|
546
557
|
"resourceName",
|
|
558
|
+
"toolDescription",
|
|
547
559
|
"eventType",
|
|
548
560
|
"actorId",
|
|
549
561
|
"tags",
|
|
@@ -721,7 +733,7 @@ function sanitizeParameters(obj) {
|
|
|
721
733
|
}
|
|
722
734
|
//#endregion
|
|
723
735
|
//#region package.json
|
|
724
|
-
var version = "0.0.
|
|
736
|
+
var version = "0.0.5";
|
|
725
737
|
//#endregion
|
|
726
738
|
//#region src/modules/session.ts
|
|
727
739
|
function newSessionId() {
|
|
@@ -1181,12 +1193,14 @@ function publishEvent(server, eventInput) {
|
|
|
1181
1193
|
identifyActorName: sessionInfo.identifyActorName,
|
|
1182
1194
|
identifyActorData: sessionInfo.identifyActorData,
|
|
1183
1195
|
resourceName: eventInput.resourceName,
|
|
1196
|
+
toolDescription: eventInput.toolDescription,
|
|
1184
1197
|
parameters: eventInput.parameters,
|
|
1185
1198
|
response: eventInput.response,
|
|
1186
1199
|
userIntent: eventInput.userIntent,
|
|
1187
1200
|
userIntentSource: eventInput.userIntentSource,
|
|
1188
1201
|
isError: eventInput.isError,
|
|
1189
1202
|
error: eventInput.error,
|
|
1203
|
+
conversationId: eventInput.conversationId,
|
|
1190
1204
|
redactionFn: eventInput.redactionFn,
|
|
1191
1205
|
tags: eventInput.tags,
|
|
1192
1206
|
properties: eventInput.properties
|
|
@@ -1923,6 +1937,112 @@ function setupMCPAnalyticsTools(server) {
|
|
|
1923
1937
|
}
|
|
1924
1938
|
}
|
|
1925
1939
|
//#endregion
|
|
1940
|
+
//#region src/modules/conversation-id.ts
|
|
1941
|
+
const CONVERSATION_ID_PARAM_NAME = "conversation_id";
|
|
1942
|
+
function addConversationIdToTool(tool) {
|
|
1943
|
+
const modifiedTool = { ...tool };
|
|
1944
|
+
const toolName = tool.name || "unknown";
|
|
1945
|
+
const schema = modifiedTool.inputSchema;
|
|
1946
|
+
if (schema?.properties?.["conversation_id"]) {
|
|
1947
|
+
writeToLog(`WARN: Tool "${toolName}" already has '${CONVERSATION_ID_PARAM_NAME}' parameter. Skipping conversation_id injection.`);
|
|
1948
|
+
return modifiedTool;
|
|
1949
|
+
}
|
|
1950
|
+
if (schema?.oneOf || schema?.allOf || schema?.anyOf) {
|
|
1951
|
+
writeToLog(`WARN: Tool "${toolName}" has complex schema (oneOf/allOf/anyOf). Skipping conversation_id injection.`);
|
|
1952
|
+
return modifiedTool;
|
|
1953
|
+
}
|
|
1954
|
+
if (!modifiedTool.inputSchema) modifiedTool.inputSchema = {
|
|
1955
|
+
type: "object",
|
|
1956
|
+
properties: {},
|
|
1957
|
+
required: []
|
|
1958
|
+
};
|
|
1959
|
+
modifiedTool.inputSchema = JSON.parse(JSON.stringify(modifiedTool.inputSchema));
|
|
1960
|
+
const inputSchema = modifiedTool.inputSchema;
|
|
1961
|
+
if (!inputSchema.properties) inputSchema.properties = {};
|
|
1962
|
+
if (inputSchema.additionalProperties === false) inputSchema.additionalProperties = void 0;
|
|
1963
|
+
inputSchema.properties[CONVERSATION_ID_PARAM_NAME] = {
|
|
1964
|
+
type: "string",
|
|
1965
|
+
description: DEFAULT_CONVERSATION_ID_DESCRIPTION
|
|
1966
|
+
};
|
|
1967
|
+
return modifiedTool;
|
|
1968
|
+
}
|
|
1969
|
+
function addConversationIdToTools(tools) {
|
|
1970
|
+
return tools.map((tool) => {
|
|
1971
|
+
if (tool.name === "get_more_tools") return tool;
|
|
1972
|
+
return addConversationIdToTool(tool);
|
|
1973
|
+
});
|
|
1974
|
+
}
|
|
1975
|
+
/**
|
|
1976
|
+
* Decides which conversation_id to use for a tool call:
|
|
1977
|
+
* - disabled or get_more_tools → none
|
|
1978
|
+
* - agent supplied a value → use it
|
|
1979
|
+
* - agent omitted → mint a UUID
|
|
1980
|
+
*/
|
|
1981
|
+
function resolveConversationId(enabled, args, toolName) {
|
|
1982
|
+
if (!enabled || toolName === "get_more_tools") return {
|
|
1983
|
+
minted: false,
|
|
1984
|
+
conversationId: void 0
|
|
1985
|
+
};
|
|
1986
|
+
const supplied = extractConversationId(args);
|
|
1987
|
+
if (supplied) return {
|
|
1988
|
+
minted: false,
|
|
1989
|
+
conversationId: supplied
|
|
1990
|
+
};
|
|
1991
|
+
return {
|
|
1992
|
+
minted: true,
|
|
1993
|
+
conversationId: (0, node_crypto.randomUUID)()
|
|
1994
|
+
};
|
|
1995
|
+
}
|
|
1996
|
+
/**
|
|
1997
|
+
* Predicate matching the same eligibility checks injectConversationIdPromptBack uses,
|
|
1998
|
+
* exposed so callers can pre-decide whether the prompt-back will actually land
|
|
1999
|
+
* (and clear event.conversationId if not, to avoid orphan ids in analytics).
|
|
2000
|
+
*/
|
|
2001
|
+
function canInjectConversationIdPromptBack(result) {
|
|
2002
|
+
if (!(result && typeof result === "object")) return false;
|
|
2003
|
+
const resultObj = result;
|
|
2004
|
+
if (resultObj.isError === true) return false;
|
|
2005
|
+
return Array.isArray(resultObj.content);
|
|
2006
|
+
}
|
|
2007
|
+
function extractConversationId(args) {
|
|
2008
|
+
if (!(args && typeof args === "object")) return;
|
|
2009
|
+
const value = args[CONVERSATION_ID_PARAM_NAME];
|
|
2010
|
+
if (typeof value !== "string") return;
|
|
2011
|
+
const trimmed = value.trim();
|
|
2012
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
2013
|
+
}
|
|
2014
|
+
function cloneRequestWithoutConversationId(request) {
|
|
2015
|
+
if (!request.params || typeof request.params !== "object") return request;
|
|
2016
|
+
const args = request.params.arguments;
|
|
2017
|
+
if (!(args && typeof args === "object")) return request;
|
|
2018
|
+
return {
|
|
2019
|
+
...request,
|
|
2020
|
+
params: {
|
|
2021
|
+
...request.params,
|
|
2022
|
+
arguments: stripConversationId(args)
|
|
2023
|
+
}
|
|
2024
|
+
};
|
|
2025
|
+
}
|
|
2026
|
+
function stripConversationId(args) {
|
|
2027
|
+
if (!args || typeof args !== "object" || !("conversation_id" in args)) return args;
|
|
2028
|
+
const { [CONVERSATION_ID_PARAM_NAME]: _omit, ...rest } = args;
|
|
2029
|
+
return rest;
|
|
2030
|
+
}
|
|
2031
|
+
function buildConversationIdPromptBack(conversationId) {
|
|
2032
|
+
return {
|
|
2033
|
+
type: "text",
|
|
2034
|
+
text: `[SERVER]: Reuse conversation_id=${conversationId} on every subsequent tool call in this conversation. Required for the server to correlate calls and provide context-aware results.`
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
function injectConversationIdPromptBack(result, conversationId) {
|
|
2038
|
+
if (!canInjectConversationIdPromptBack(result)) return result;
|
|
2039
|
+
const resultObj = result;
|
|
2040
|
+
return {
|
|
2041
|
+
...resultObj,
|
|
2042
|
+
content: [...resultObj.content, buildConversationIdPromptBack(conversationId)]
|
|
2043
|
+
};
|
|
2044
|
+
}
|
|
2045
|
+
//#endregion
|
|
1926
2046
|
//#region src/modules/intent.ts
|
|
1927
2047
|
function getContextArgument$2(request) {
|
|
1928
2048
|
const context = request.params?.arguments?.context;
|
|
@@ -2018,9 +2138,11 @@ async function getTracedToolsList(server, originalListToolsHandler, request, ext
|
|
|
2018
2138
|
const data = getServerTrackingData(server);
|
|
2019
2139
|
let tools = (await originalListToolsHandler(request, extra)).tools || [];
|
|
2020
2140
|
if (data && isContextEnabled(data.options.context)) tools = addContextParameterToTools(tools, getContextDescription(data.options.context));
|
|
2141
|
+
if (data?.options.enableConversationId) tools = addConversationIdToTools(tools);
|
|
2021
2142
|
if (data?.options.reportMissing) {
|
|
2022
2143
|
if (!tools.some((tool) => tool?.name === "get_more_tools")) tools.push(getReportMissingToolDescriptor());
|
|
2023
2144
|
}
|
|
2145
|
+
if (data) cacheToolDescriptions(data.toolDescriptions, tools);
|
|
2024
2146
|
return tools;
|
|
2025
2147
|
} catch (error) {
|
|
2026
2148
|
writeToLog(`Warning: Original list tools handler failed, this suggests an error PostHog MCP analytics did not cause - ${error}`);
|
|
@@ -2102,30 +2224,39 @@ async function handleToolCallRequest(server, originalCallToolHandler, request, e
|
|
|
2102
2224
|
writeToLog("Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called track(server, options) before using tool calls.");
|
|
2103
2225
|
return await originalCallToolHandler?.(request, extra);
|
|
2104
2226
|
}
|
|
2227
|
+
const conversation = resolveConversationId(data.options.enableConversationId ?? false, request.params?.arguments, request.params?.name);
|
|
2228
|
+
const downstreamRequest = conversation.conversationId ? cloneRequestWithoutConversationId(request) : request;
|
|
2229
|
+
const toolName = request.params?.name;
|
|
2105
2230
|
const event = {
|
|
2106
2231
|
sessionId: getServerSessionId(server, extra),
|
|
2107
|
-
|
|
2108
|
-
|
|
2232
|
+
conversationId: conversation.conversationId,
|
|
2233
|
+
resourceName: toolName || "Unknown Tool Name",
|
|
2234
|
+
parameters: buildCapturedMcpParameters(downstreamRequest),
|
|
2109
2235
|
eventType: MCPAnalyticsEventType.mcpToolsCall,
|
|
2110
2236
|
timestamp: /* @__PURE__ */ new Date(),
|
|
2237
|
+
toolDescription: toolName ? data.toolDescriptions.get(toolName) : void 0,
|
|
2111
2238
|
redactionFn: data.options.redactSensitiveInformation
|
|
2112
2239
|
};
|
|
2113
2240
|
try {
|
|
2114
2241
|
await handleIdentify(server, data, request, extra);
|
|
2115
2242
|
await applyResolvedMetadata$1(event, data, request, extra);
|
|
2116
2243
|
setEventIntent(event, await resolveToolCallIntent(data, request, extra));
|
|
2117
|
-
const result = await executeToolCall(server, originalCallToolHandler,
|
|
2244
|
+
const result = await executeToolCall(server, originalCallToolHandler, downstreamRequest, extra, event);
|
|
2118
2245
|
if (isToolResultError$1(result)) {
|
|
2119
2246
|
event.isError = true;
|
|
2120
2247
|
event.error = captureException(result);
|
|
2121
2248
|
} else event.isError = false;
|
|
2122
|
-
|
|
2249
|
+
let finalResult = result;
|
|
2250
|
+
if (conversation.minted) if (canInjectConversationIdPromptBack(result)) finalResult = injectConversationIdPromptBack(result, conversation.conversationId);
|
|
2251
|
+
else event.conversationId = void 0;
|
|
2252
|
+
event.response = finalResult;
|
|
2123
2253
|
event.duration = getEventDuration(event);
|
|
2124
2254
|
publishEvent(server, event);
|
|
2125
|
-
return
|
|
2255
|
+
return finalResult;
|
|
2126
2256
|
} catch (error) {
|
|
2127
2257
|
event.isError = true;
|
|
2128
2258
|
event.error = captureException(error);
|
|
2259
|
+
if (conversation.minted) event.conversationId = void 0;
|
|
2129
2260
|
event.duration = getEventDuration(event);
|
|
2130
2261
|
publishEvent(server, event);
|
|
2131
2262
|
throw error;
|
|
@@ -2157,6 +2288,10 @@ function getContextArgument$1(request) {
|
|
|
2157
2288
|
function getEventDuration(event) {
|
|
2158
2289
|
return event.timestamp ? Date.now() - event.timestamp.getTime() : 0;
|
|
2159
2290
|
}
|
|
2291
|
+
function cacheToolDescriptions(cache, tools) {
|
|
2292
|
+
if (!tools) return;
|
|
2293
|
+
for (const tool of tools) if (tool?.name && typeof tool.description === "string") cache.set(tool.name, tool.description);
|
|
2294
|
+
}
|
|
2160
2295
|
//#endregion
|
|
2161
2296
|
//#region src/modules/mcp-sdk-compat.ts
|
|
2162
2297
|
/**
|
|
@@ -2244,7 +2379,8 @@ function addTracingToToolRegistry(tools, server) {
|
|
|
2244
2379
|
}
|
|
2245
2380
|
function setupListenerToRegisteredTools(server) {
|
|
2246
2381
|
try {
|
|
2247
|
-
|
|
2382
|
+
const data = getServerTrackingData(server.server);
|
|
2383
|
+
if (!data) {
|
|
2248
2384
|
writeToLog("Warning: Cannot setup listener - no tracking data found");
|
|
2249
2385
|
return;
|
|
2250
2386
|
}
|
|
@@ -2252,6 +2388,7 @@ function setupListenerToRegisteredTools(server) {
|
|
|
2252
2388
|
set(target, property, value) {
|
|
2253
2389
|
try {
|
|
2254
2390
|
if (typeof property === "string" && value && typeof value === "object" && hasToolFunction(value)) {
|
|
2391
|
+
if (typeof value.description === "string") data.toolDescriptions.set(property, value.description);
|
|
2255
2392
|
if (value[MCP_ANALYTICS_PROCESSED]) {
|
|
2256
2393
|
writeToLog(`Tool ${String(property)} already processed, skipping proxy wrapping`);
|
|
2257
2394
|
return Reflect.set(target, property, value);
|
|
@@ -2317,14 +2454,14 @@ function addTracingToToolCallbackInternal(tool, toolName, _server) {
|
|
|
2317
2454
|
args = void 0;
|
|
2318
2455
|
extra = params[0];
|
|
2319
2456
|
}
|
|
2320
|
-
const removeContextFromArgs = (
|
|
2321
|
-
if (
|
|
2322
|
-
const { context: _context, ...
|
|
2323
|
-
return
|
|
2457
|
+
const removeContextFromArgs = (input) => {
|
|
2458
|
+
if (input && typeof input === "object" && "context" in input) {
|
|
2459
|
+
const { context: _context, ...rest } = input;
|
|
2460
|
+
return rest;
|
|
2324
2461
|
}
|
|
2325
|
-
return
|
|
2462
|
+
return input;
|
|
2326
2463
|
};
|
|
2327
|
-
const cleanedArgs = toolName === "get_more_tools" ? args : removeContextFromArgs(args);
|
|
2464
|
+
const cleanedArgs = toolName === "get_more_tools" ? args : stripConversationId(removeContextFromArgs(args));
|
|
2328
2465
|
try {
|
|
2329
2466
|
if (cleanedArgs === void 0) return await originalCallback(extra);
|
|
2330
2467
|
return await originalCallback(cleanedArgs, extra);
|
|
@@ -2358,26 +2495,31 @@ function createToolsCallWrapper(originalHandler, server) {
|
|
|
2358
2495
|
}
|
|
2359
2496
|
async function handleWrappedToolsCall(originalHandler, server, request, extra) {
|
|
2360
2497
|
const startTime = /* @__PURE__ */ new Date();
|
|
2361
|
-
const
|
|
2498
|
+
const conversation = resolveConversationId(getServerTrackingData(server)?.options.enableConversationId ?? false, request.params?.arguments, request.params?.name);
|
|
2499
|
+
const tracing = await initializeToolCallEvent(server, request, conversation.conversationId ? cloneRequestWithoutConversationId(request) : request, extra, startTime, conversation);
|
|
2362
2500
|
if (request?.params?.name === "get_more_tools") return await executeReportMissingTool(server, request, tracing, startTime);
|
|
2363
2501
|
return await executeOriginalTool(originalHandler, server, request, extra, tracing, startTime);
|
|
2364
2502
|
}
|
|
2365
|
-
async function initializeToolCallEvent(server, request, extra, startTime) {
|
|
2503
|
+
async function initializeToolCallEvent(server, request, downstreamRequest, extra, startTime, conversation) {
|
|
2366
2504
|
try {
|
|
2367
2505
|
const data = getServerTrackingData(server);
|
|
2368
2506
|
if (!data) {
|
|
2369
2507
|
writeToLog("Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called track(server, options) before using tool calls.");
|
|
2370
2508
|
return {
|
|
2371
2509
|
event: null,
|
|
2510
|
+
mintedConversationId: void 0,
|
|
2372
2511
|
shouldPublishEvent: false
|
|
2373
2512
|
};
|
|
2374
2513
|
}
|
|
2514
|
+
const toolName = request.params?.name;
|
|
2375
2515
|
const event = {
|
|
2376
2516
|
sessionId: getServerSessionId(server, extra),
|
|
2377
|
-
|
|
2378
|
-
|
|
2517
|
+
conversationId: conversation.conversationId,
|
|
2518
|
+
resourceName: toolName || "Unknown Tool",
|
|
2519
|
+
parameters: buildCapturedMcpParameters(downstreamRequest),
|
|
2379
2520
|
eventType: MCPAnalyticsEventType.mcpToolsCall,
|
|
2380
2521
|
timestamp: startTime,
|
|
2522
|
+
toolDescription: toolName ? data.toolDescriptions.get(toolName) : void 0,
|
|
2381
2523
|
redactionFn: data.options.redactSensitiveInformation
|
|
2382
2524
|
};
|
|
2383
2525
|
await handleIdentify(server, data, request, extra);
|
|
@@ -2386,12 +2528,14 @@ async function initializeToolCallEvent(server, request, extra, startTime) {
|
|
|
2386
2528
|
setEventIntent(event, await resolveToolCallIntent(data, request, extra));
|
|
2387
2529
|
return {
|
|
2388
2530
|
event,
|
|
2531
|
+
mintedConversationId: conversation.minted ? conversation.conversationId : void 0,
|
|
2389
2532
|
shouldPublishEvent: true
|
|
2390
2533
|
};
|
|
2391
2534
|
} catch (error) {
|
|
2392
2535
|
writeToLog(`Warning: PostHog MCP analytics tracing failed for tool ${request.params?.name}, falling back to original handler - ${error}`);
|
|
2393
2536
|
return {
|
|
2394
2537
|
event: null,
|
|
2538
|
+
mintedConversationId: void 0,
|
|
2395
2539
|
shouldPublishEvent: false
|
|
2396
2540
|
};
|
|
2397
2541
|
}
|
|
@@ -2419,14 +2563,20 @@ async function executeReportMissingTool(server, request, tracing, startTime) {
|
|
|
2419
2563
|
async function executeOriginalTool(originalHandler, server, request, extra, tracing, startTime) {
|
|
2420
2564
|
try {
|
|
2421
2565
|
const result = await originalHandler(request, extra);
|
|
2422
|
-
|
|
2566
|
+
let finalResult = result;
|
|
2567
|
+
if (tracing.mintedConversationId) {
|
|
2568
|
+
if (canInjectConversationIdPromptBack(result)) finalResult = injectConversationIdPromptBack(result, tracing.mintedConversationId);
|
|
2569
|
+
else if (tracing.event) tracing.event.conversationId = void 0;
|
|
2570
|
+
}
|
|
2571
|
+
publishSuccessfulToolEvent(server, tracing, finalResult, startTime, {
|
|
2423
2572
|
capturedError: extra?.__mcp_analytics_error,
|
|
2424
2573
|
clearCapturedError: () => {
|
|
2425
2574
|
if (extra) extra.__mcp_analytics_error = void 0;
|
|
2426
2575
|
}
|
|
2427
2576
|
});
|
|
2428
|
-
return
|
|
2577
|
+
return finalResult;
|
|
2429
2578
|
} catch (error) {
|
|
2579
|
+
if (tracing.mintedConversationId && tracing.event) tracing.event.conversationId = void 0;
|
|
2430
2580
|
publishFailedToolEvent(server, tracing, error, startTime);
|
|
2431
2581
|
throw error;
|
|
2432
2582
|
}
|
|
@@ -2459,16 +2609,20 @@ function publishFailedToolEvent(server, tracing, error, startTime) {
|
|
|
2459
2609
|
}
|
|
2460
2610
|
function setupTracking(server) {
|
|
2461
2611
|
try {
|
|
2462
|
-
getServerTrackingData(server.server);
|
|
2612
|
+
const mcpAnalyticsData = getServerTrackingData(server.server);
|
|
2463
2613
|
setupToolsCallHandlerWrapping(server);
|
|
2464
2614
|
setupInitializeTracing(server);
|
|
2465
2615
|
server._registeredTools = addTracingToToolRegistry(server._registeredTools, server);
|
|
2616
|
+
if (mcpAnalyticsData) seedToolDescriptionsFromRegistry(mcpAnalyticsData.toolDescriptions, server._registeredTools);
|
|
2466
2617
|
setupListToolsTracing(server);
|
|
2467
2618
|
setupListenerToRegisteredTools(server);
|
|
2468
2619
|
} catch (error) {
|
|
2469
2620
|
writeToLog(`Warning: Failed to setup tool call tracing - ${error}`);
|
|
2470
2621
|
}
|
|
2471
2622
|
}
|
|
2623
|
+
function seedToolDescriptionsFromRegistry(cache, tools) {
|
|
2624
|
+
for (const [name, tool] of Object.entries(tools)) if (typeof tool?.description === "string") cache.set(name, tool.description);
|
|
2625
|
+
}
|
|
2472
2626
|
//#endregion
|
|
2473
2627
|
//#region src/index.ts
|
|
2474
2628
|
/**
|
|
@@ -2604,6 +2758,7 @@ function buildTrackingData(lowLevelServer, options) {
|
|
|
2604
2758
|
sessionId: newSessionId(),
|
|
2605
2759
|
lastActivity: /* @__PURE__ */ new Date(),
|
|
2606
2760
|
identifiedSessions: /* @__PURE__ */ new Map(),
|
|
2761
|
+
toolDescriptions: /* @__PURE__ */ new Map(),
|
|
2607
2762
|
sessionInfo: getSessionInfo(lowLevelServer, void 0),
|
|
2608
2763
|
options: {
|
|
2609
2764
|
reportMissing: options.reportMissing ?? false,
|
|
@@ -2617,7 +2772,8 @@ function buildTrackingData(lowLevelServer, options) {
|
|
|
2617
2772
|
eventProperties: options.eventProperties,
|
|
2618
2773
|
host: options.host,
|
|
2619
2774
|
posthogClient: options.posthogClient,
|
|
2620
|
-
posthogOptions: options.posthogOptions
|
|
2775
|
+
posthogOptions: options.posthogOptions,
|
|
2776
|
+
enableConversationId: options.enableConversationId ?? false
|
|
2621
2777
|
},
|
|
2622
2778
|
sessionSource: "generated"
|
|
2623
2779
|
};
|