@posthog/mcp 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -23,6 +23,7 @@ interface MCPAnalyticsOptions {
23
23
  apiKey?: string | null;
24
24
  context?: boolean | MCPAnalyticsContextOptions;
25
25
  enableAITracing?: boolean;
26
+ enableConversationId?: boolean;
26
27
  enableTracing?: boolean;
27
28
  eventProperties?: (request: MCPRequestLike, extra?: CompatibleRequestHandlerExtra) => JsonRecord | null | Promise<JsonRecord | null>;
28
29
  eventTags?: (request: MCPRequestLike, extra?: CompatibleRequestHandlerExtra) => Record<string, string> | null | Promise<Record<string, string> | null>;
@@ -92,6 +93,7 @@ declare const PostHogMCPAnalyticsProperty: {
92
93
  readonly AiTraceId: "$ai_trace_id";
93
94
  readonly ClientName: "$mcp_client_name";
94
95
  readonly ClientVersion: "$mcp_client_version";
96
+ readonly ConversationId: "$mcp_conversation_id";
95
97
  readonly DurationMs: "$mcp_duration_ms";
96
98
  readonly IsError: "$mcp_is_error";
97
99
  readonly Intent: "$mcp_intent";
package/dist/index.d.mts CHANGED
@@ -22,6 +22,7 @@ interface MCPAnalyticsOptions {
22
22
  apiKey?: string | null;
23
23
  context?: boolean | MCPAnalyticsContextOptions;
24
24
  enableAITracing?: boolean;
25
+ enableConversationId?: boolean;
25
26
  enableTracing?: boolean;
26
27
  eventProperties?: (request: MCPRequestLike, extra?: CompatibleRequestHandlerExtra) => JsonRecord | null | Promise<JsonRecord | null>;
27
28
  eventTags?: (request: MCPRequestLike, extra?: CompatibleRequestHandlerExtra) => Record<string, string> | null | Promise<Record<string, string> | null>;
@@ -91,6 +92,7 @@ declare const PostHogMCPAnalyticsProperty: {
91
92
  readonly AiTraceId: "$ai_trace_id";
92
93
  readonly ClientName: "$mcp_client_name";
93
94
  readonly ClientVersion: "$mcp_client_version";
95
+ readonly ConversationId: "$mcp_conversation_id";
94
96
  readonly DurationMs: "$mcp_duration_ms";
95
97
  readonly IsError: "$mcp_is_error";
96
98
  readonly Intent: "$mcp_intent";
package/dist/index.mjs CHANGED
@@ -349,6 +349,7 @@ function getRequestResourceName(request) {
349
349
  //#endregion
350
350
  //#region src/modules/constants.ts
351
351
  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."`;
352
+ 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.";
352
353
  const POSTHOG_MCP_ANALYTICS_SOURCE = "posthog_mcp_analytics";
353
354
  const PostHogMCPAnalyticsEvent = {
354
355
  AiSpan: "$ai_span",
@@ -375,6 +376,7 @@ const PostHogMCPAnalyticsProperty = {
375
376
  AiTraceId: "$ai_trace_id",
376
377
  ClientName: "$mcp_client_name",
377
378
  ClientVersion: "$mcp_client_version",
379
+ ConversationId: "$mcp_conversation_id",
378
380
  DurationMs: "$mcp_duration_ms",
379
381
  IsError: "$mcp_is_error",
380
382
  Intent: "$mcp_intent",
@@ -420,6 +422,7 @@ function buildCaptureEvent(event, options) {
420
422
  [PostHogMCPAnalyticsProperty.SessionId]: event.sessionId,
421
423
  [PostHogMCPAnalyticsProperty.Source]: POSTHOG_MCP_ANALYTICS_SOURCE
422
424
  };
425
+ addConversationIdProperty(event, properties);
423
426
  addCommonEventProperties(event, properties);
424
427
  addTraceReferenceProperties(event, properties, options);
425
428
  addCustomEventProperties(event, properties);
@@ -445,6 +448,9 @@ function addTraceReferenceProperties(event, properties, options) {
445
448
  properties[PostHogMCPAnalyticsProperty.AiTraceId] = getAITraceId(event);
446
449
  properties[PostHogMCPAnalyticsProperty.AiSpanId] = getAISpanId(event);
447
450
  }
451
+ function addConversationIdProperty(event, properties) {
452
+ if (event.conversationId !== void 0 && event.conversationId !== "") properties[PostHogMCPAnalyticsProperty.ConversationId] = event.conversationId;
453
+ }
448
454
  function addCommonEventProperties(event, properties) {
449
455
  if (event.resourceName) {
450
456
  properties[PostHogMCPAnalyticsProperty.ResourceName] = event.resourceName;
@@ -476,6 +482,7 @@ function buildExceptionEvent(event) {
476
482
  $exception_source: "backend",
477
483
  [PostHogMCPAnalyticsProperty.SessionId]: event.sessionId
478
484
  };
485
+ addConversationIdProperty(event, properties);
479
486
  if (event.error) {
480
487
  if (event.error.message) properties.$exception_message = event.error.message;
481
488
  if (event.error.type) properties.$exception_type = event.error.type;
@@ -509,6 +516,7 @@ function buildAISpanEvent(event) {
509
516
  [PostHogMCPAnalyticsProperty.SessionId]: event.sessionId,
510
517
  [PostHogMCPAnalyticsProperty.Source]: POSTHOG_MCP_ANALYTICS_SOURCE
511
518
  };
519
+ addConversationIdProperty(event, properties);
512
520
  if (event.duration !== void 0) properties[PostHogMCPAnalyticsProperty.AiLatency] = event.duration / 1e3;
513
521
  if (event.isError && event.error) properties.$ai_error = event.error;
514
522
  if (event.parameters !== void 0) properties[PostHogMCPAnalyticsProperty.AiInputState] = event.parameters;
@@ -720,7 +728,7 @@ function sanitizeParameters(obj) {
720
728
  }
721
729
  //#endregion
722
730
  //#region package.json
723
- var version = "0.0.3";
731
+ var version = "0.0.4";
724
732
  //#endregion
725
733
  //#region src/modules/session.ts
726
734
  function newSessionId() {
@@ -1186,6 +1194,7 @@ function publishEvent(server, eventInput) {
1186
1194
  userIntentSource: eventInput.userIntentSource,
1187
1195
  isError: eventInput.isError,
1188
1196
  error: eventInput.error,
1197
+ conversationId: eventInput.conversationId,
1189
1198
  redactionFn: eventInput.redactionFn,
1190
1199
  tags: eventInput.tags,
1191
1200
  properties: eventInput.properties
@@ -1922,6 +1931,112 @@ function setupMCPAnalyticsTools(server) {
1922
1931
  }
1923
1932
  }
1924
1933
  //#endregion
1934
+ //#region src/modules/conversation-id.ts
1935
+ const CONVERSATION_ID_PARAM_NAME = "conversation_id";
1936
+ function addConversationIdToTool(tool) {
1937
+ const modifiedTool = { ...tool };
1938
+ const toolName = tool.name || "unknown";
1939
+ const schema = modifiedTool.inputSchema;
1940
+ if (schema?.properties?.["conversation_id"]) {
1941
+ writeToLog(`WARN: Tool "${toolName}" already has '${CONVERSATION_ID_PARAM_NAME}' parameter. Skipping conversation_id injection.`);
1942
+ return modifiedTool;
1943
+ }
1944
+ if (schema?.oneOf || schema?.allOf || schema?.anyOf) {
1945
+ writeToLog(`WARN: Tool "${toolName}" has complex schema (oneOf/allOf/anyOf). Skipping conversation_id injection.`);
1946
+ return modifiedTool;
1947
+ }
1948
+ if (!modifiedTool.inputSchema) modifiedTool.inputSchema = {
1949
+ type: "object",
1950
+ properties: {},
1951
+ required: []
1952
+ };
1953
+ modifiedTool.inputSchema = JSON.parse(JSON.stringify(modifiedTool.inputSchema));
1954
+ const inputSchema = modifiedTool.inputSchema;
1955
+ if (!inputSchema.properties) inputSchema.properties = {};
1956
+ if (inputSchema.additionalProperties === false) inputSchema.additionalProperties = void 0;
1957
+ inputSchema.properties[CONVERSATION_ID_PARAM_NAME] = {
1958
+ type: "string",
1959
+ description: DEFAULT_CONVERSATION_ID_DESCRIPTION
1960
+ };
1961
+ return modifiedTool;
1962
+ }
1963
+ function addConversationIdToTools(tools) {
1964
+ return tools.map((tool) => {
1965
+ if (tool.name === "get_more_tools") return tool;
1966
+ return addConversationIdToTool(tool);
1967
+ });
1968
+ }
1969
+ /**
1970
+ * Decides which conversation_id to use for a tool call:
1971
+ * - disabled or get_more_tools → none
1972
+ * - agent supplied a value → use it
1973
+ * - agent omitted → mint a UUID
1974
+ */
1975
+ function resolveConversationId(enabled, args, toolName) {
1976
+ if (!enabled || toolName === "get_more_tools") return {
1977
+ minted: false,
1978
+ conversationId: void 0
1979
+ };
1980
+ const supplied = extractConversationId(args);
1981
+ if (supplied) return {
1982
+ minted: false,
1983
+ conversationId: supplied
1984
+ };
1985
+ return {
1986
+ minted: true,
1987
+ conversationId: randomUUID()
1988
+ };
1989
+ }
1990
+ /**
1991
+ * Predicate matching the same eligibility checks injectConversationIdPromptBack uses,
1992
+ * exposed so callers can pre-decide whether the prompt-back will actually land
1993
+ * (and clear event.conversationId if not, to avoid orphan ids in analytics).
1994
+ */
1995
+ function canInjectConversationIdPromptBack(result) {
1996
+ if (!(result && typeof result === "object")) return false;
1997
+ const resultObj = result;
1998
+ if (resultObj.isError === true) return false;
1999
+ return Array.isArray(resultObj.content);
2000
+ }
2001
+ function extractConversationId(args) {
2002
+ if (!(args && typeof args === "object")) return;
2003
+ const value = args[CONVERSATION_ID_PARAM_NAME];
2004
+ if (typeof value !== "string") return;
2005
+ const trimmed = value.trim();
2006
+ return trimmed.length > 0 ? trimmed : void 0;
2007
+ }
2008
+ function cloneRequestWithoutConversationId(request) {
2009
+ if (!request.params || typeof request.params !== "object") return request;
2010
+ const args = request.params.arguments;
2011
+ if (!(args && typeof args === "object")) return request;
2012
+ return {
2013
+ ...request,
2014
+ params: {
2015
+ ...request.params,
2016
+ arguments: stripConversationId(args)
2017
+ }
2018
+ };
2019
+ }
2020
+ function stripConversationId(args) {
2021
+ if (!args || typeof args !== "object" || !("conversation_id" in args)) return args;
2022
+ const { [CONVERSATION_ID_PARAM_NAME]: _omit, ...rest } = args;
2023
+ return rest;
2024
+ }
2025
+ function buildConversationIdPromptBack(conversationId) {
2026
+ return {
2027
+ type: "text",
2028
+ 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.`
2029
+ };
2030
+ }
2031
+ function injectConversationIdPromptBack(result, conversationId) {
2032
+ if (!canInjectConversationIdPromptBack(result)) return result;
2033
+ const resultObj = result;
2034
+ return {
2035
+ ...resultObj,
2036
+ content: [...resultObj.content, buildConversationIdPromptBack(conversationId)]
2037
+ };
2038
+ }
2039
+ //#endregion
1925
2040
  //#region src/modules/intent.ts
1926
2041
  function getContextArgument$2(request) {
1927
2042
  const context = request.params?.arguments?.context;
@@ -2017,6 +2132,7 @@ async function getTracedToolsList(server, originalListToolsHandler, request, ext
2017
2132
  const data = getServerTrackingData(server);
2018
2133
  let tools = (await originalListToolsHandler(request, extra)).tools || [];
2019
2134
  if (data && isContextEnabled(data.options.context)) tools = addContextParameterToTools(tools, getContextDescription(data.options.context));
2135
+ if (data?.options.enableConversationId) tools = addConversationIdToTools(tools);
2020
2136
  if (data?.options.reportMissing) {
2021
2137
  if (!tools.some((tool) => tool?.name === "get_more_tools")) tools.push(getReportMissingToolDescriptor());
2022
2138
  }
@@ -2101,10 +2217,13 @@ async function handleToolCallRequest(server, originalCallToolHandler, request, e
2101
2217
  writeToLog("Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called track(server, options) before using tool calls.");
2102
2218
  return await originalCallToolHandler?.(request, extra);
2103
2219
  }
2220
+ const conversation = resolveConversationId(data.options.enableConversationId ?? false, request.params?.arguments, request.params?.name);
2221
+ const downstreamRequest = conversation.conversationId ? cloneRequestWithoutConversationId(request) : request;
2104
2222
  const event = {
2105
2223
  sessionId: getServerSessionId(server, extra),
2224
+ conversationId: conversation.conversationId,
2106
2225
  resourceName: request.params?.name || "Unknown Tool Name",
2107
- parameters: buildCapturedMcpParameters(request),
2226
+ parameters: buildCapturedMcpParameters(downstreamRequest),
2108
2227
  eventType: MCPAnalyticsEventType.mcpToolsCall,
2109
2228
  timestamp: /* @__PURE__ */ new Date(),
2110
2229
  redactionFn: data.options.redactSensitiveInformation
@@ -2113,18 +2232,22 @@ async function handleToolCallRequest(server, originalCallToolHandler, request, e
2113
2232
  await handleIdentify(server, data, request, extra);
2114
2233
  await applyResolvedMetadata$1(event, data, request, extra);
2115
2234
  setEventIntent(event, await resolveToolCallIntent(data, request, extra));
2116
- const result = await executeToolCall(server, originalCallToolHandler, request, extra, event);
2235
+ const result = await executeToolCall(server, originalCallToolHandler, downstreamRequest, extra, event);
2117
2236
  if (isToolResultError$1(result)) {
2118
2237
  event.isError = true;
2119
2238
  event.error = captureException(result);
2120
2239
  } else event.isError = false;
2121
- event.response = result;
2240
+ let finalResult = result;
2241
+ if (conversation.minted) if (canInjectConversationIdPromptBack(result)) finalResult = injectConversationIdPromptBack(result, conversation.conversationId);
2242
+ else event.conversationId = void 0;
2243
+ event.response = finalResult;
2122
2244
  event.duration = getEventDuration(event);
2123
2245
  publishEvent(server, event);
2124
- return result;
2246
+ return finalResult;
2125
2247
  } catch (error) {
2126
2248
  event.isError = true;
2127
2249
  event.error = captureException(error);
2250
+ if (conversation.minted) event.conversationId = void 0;
2128
2251
  event.duration = getEventDuration(event);
2129
2252
  publishEvent(server, event);
2130
2253
  throw error;
@@ -2316,14 +2439,14 @@ function addTracingToToolCallbackInternal(tool, toolName, _server) {
2316
2439
  args = void 0;
2317
2440
  extra = params[0];
2318
2441
  }
2319
- const removeContextFromArgs = (args) => {
2320
- if (args && typeof args === "object" && "context" in args) {
2321
- const { context: _context, ...argsWithoutContext } = args;
2322
- return argsWithoutContext;
2442
+ const removeContextFromArgs = (input) => {
2443
+ if (input && typeof input === "object" && "context" in input) {
2444
+ const { context: _context, ...rest } = input;
2445
+ return rest;
2323
2446
  }
2324
- return args;
2447
+ return input;
2325
2448
  };
2326
- const cleanedArgs = toolName === "get_more_tools" ? args : removeContextFromArgs(args);
2449
+ const cleanedArgs = toolName === "get_more_tools" ? args : stripConversationId(removeContextFromArgs(args));
2327
2450
  try {
2328
2451
  if (cleanedArgs === void 0) return await originalCallback(extra);
2329
2452
  return await originalCallback(cleanedArgs, extra);
@@ -2357,24 +2480,27 @@ function createToolsCallWrapper(originalHandler, server) {
2357
2480
  }
2358
2481
  async function handleWrappedToolsCall(originalHandler, server, request, extra) {
2359
2482
  const startTime = /* @__PURE__ */ new Date();
2360
- const tracing = await initializeToolCallEvent(server, request, extra, startTime);
2483
+ const conversation = resolveConversationId(getServerTrackingData(server)?.options.enableConversationId ?? false, request.params?.arguments, request.params?.name);
2484
+ const tracing = await initializeToolCallEvent(server, request, conversation.conversationId ? cloneRequestWithoutConversationId(request) : request, extra, startTime, conversation);
2361
2485
  if (request?.params?.name === "get_more_tools") return await executeReportMissingTool(server, request, tracing, startTime);
2362
2486
  return await executeOriginalTool(originalHandler, server, request, extra, tracing, startTime);
2363
2487
  }
2364
- async function initializeToolCallEvent(server, request, extra, startTime) {
2488
+ async function initializeToolCallEvent(server, request, downstreamRequest, extra, startTime, conversation) {
2365
2489
  try {
2366
2490
  const data = getServerTrackingData(server);
2367
2491
  if (!data) {
2368
2492
  writeToLog("Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called track(server, options) before using tool calls.");
2369
2493
  return {
2370
2494
  event: null,
2495
+ mintedConversationId: void 0,
2371
2496
  shouldPublishEvent: false
2372
2497
  };
2373
2498
  }
2374
2499
  const event = {
2375
2500
  sessionId: getServerSessionId(server, extra),
2501
+ conversationId: conversation.conversationId,
2376
2502
  resourceName: request.params?.name || "Unknown Tool",
2377
- parameters: buildCapturedMcpParameters(request),
2503
+ parameters: buildCapturedMcpParameters(downstreamRequest),
2378
2504
  eventType: MCPAnalyticsEventType.mcpToolsCall,
2379
2505
  timestamp: startTime,
2380
2506
  redactionFn: data.options.redactSensitiveInformation
@@ -2385,12 +2511,14 @@ async function initializeToolCallEvent(server, request, extra, startTime) {
2385
2511
  setEventIntent(event, await resolveToolCallIntent(data, request, extra));
2386
2512
  return {
2387
2513
  event,
2514
+ mintedConversationId: conversation.minted ? conversation.conversationId : void 0,
2388
2515
  shouldPublishEvent: true
2389
2516
  };
2390
2517
  } catch (error) {
2391
2518
  writeToLog(`Warning: PostHog MCP analytics tracing failed for tool ${request.params?.name}, falling back to original handler - ${error}`);
2392
2519
  return {
2393
2520
  event: null,
2521
+ mintedConversationId: void 0,
2394
2522
  shouldPublishEvent: false
2395
2523
  };
2396
2524
  }
@@ -2418,14 +2546,20 @@ async function executeReportMissingTool(server, request, tracing, startTime) {
2418
2546
  async function executeOriginalTool(originalHandler, server, request, extra, tracing, startTime) {
2419
2547
  try {
2420
2548
  const result = await originalHandler(request, extra);
2421
- publishSuccessfulToolEvent(server, tracing, result, startTime, {
2549
+ let finalResult = result;
2550
+ if (tracing.mintedConversationId) {
2551
+ if (canInjectConversationIdPromptBack(result)) finalResult = injectConversationIdPromptBack(result, tracing.mintedConversationId);
2552
+ else if (tracing.event) tracing.event.conversationId = void 0;
2553
+ }
2554
+ publishSuccessfulToolEvent(server, tracing, finalResult, startTime, {
2422
2555
  capturedError: extra?.__mcp_analytics_error,
2423
2556
  clearCapturedError: () => {
2424
2557
  if (extra) extra.__mcp_analytics_error = void 0;
2425
2558
  }
2426
2559
  });
2427
- return result;
2560
+ return finalResult;
2428
2561
  } catch (error) {
2562
+ if (tracing.mintedConversationId && tracing.event) tracing.event.conversationId = void 0;
2429
2563
  publishFailedToolEvent(server, tracing, error, startTime);
2430
2564
  throw error;
2431
2565
  }
@@ -2616,7 +2750,8 @@ function buildTrackingData(lowLevelServer, options) {
2616
2750
  eventProperties: options.eventProperties,
2617
2751
  host: options.host,
2618
2752
  posthogClient: options.posthogClient,
2619
- posthogOptions: options.posthogOptions
2753
+ posthogOptions: options.posthogOptions,
2754
+ enableConversationId: options.enableConversationId ?? false
2620
2755
  },
2621
2756
  sessionSource: "generated"
2622
2757
  };