@posthog/mcp 0.0.1 → 0.0.3

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
@@ -28,6 +28,7 @@ interface MCPAnalyticsOptions {
28
28
  eventTags?: (request: MCPRequestLike, extra?: CompatibleRequestHandlerExtra) => Record<string, string> | null | Promise<Record<string, string> | null>;
29
29
  host?: string;
30
30
  identify?: (request: MCPRequestLike, extra?: CompatibleRequestHandlerExtra) => Promise<UserIdentity | null>;
31
+ intentFallback?: (request: MCPRequestLike, extra?: CompatibleRequestHandlerExtra) => MaybePromise<string | null | undefined>;
31
32
  posthogClient?: PostHogCaptureClient;
32
33
  posthogOptions?: Pick<PostHogOptions, "fetch" | "flushAt" | "flushInterval" | "host" | "requestTimeout" | "waitUntil" | "waitUntilDebounceMs" | "waitUntilMaxWaitMs">;
33
34
  redactSensitiveInformation?: RedactFunction;
@@ -36,6 +37,8 @@ interface MCPAnalyticsOptions {
36
37
  interface MCPAnalyticsContextOptions {
37
38
  description?: string;
38
39
  }
40
+ type MaybePromise<T> = T | Promise<T>;
41
+ type MCPAnalyticsIntentSource = "context_parameter" | "inferred";
39
42
  type RedactFunction = (text: string) => Promise<string>;
40
43
  interface CompatibleRequestHandlerExtra {
41
44
  headers?: Record<string, string | string[]>;
@@ -62,6 +65,21 @@ interface CustomEventData {
62
65
  }
63
66
  //#endregion
64
67
  //#region src/modules/constants.d.ts
68
+ declare const POSTHOG_MCP_ANALYTICS_SOURCE = "posthog_mcp_analytics";
69
+ declare const PostHogMCPAnalyticsEvent: {
70
+ readonly AiSpan: "$ai_span";
71
+ readonly Custom: "mcp_custom";
72
+ readonly Exception: "$exception";
73
+ readonly Identify: "$identify";
74
+ readonly Initialize: "mcp_initialize";
75
+ readonly PromptGet: "mcp_prompt_get";
76
+ readonly PromptsList: "mcp_prompts_list";
77
+ readonly ResourceRead: "mcp_resource_read";
78
+ readonly ResourcesList: "mcp_resources_list";
79
+ readonly ToolCall: "mcp_tool_call";
80
+ readonly ToolsList: "mcp_tools_list";
81
+ };
82
+ type PostHogMCPAnalyticsEvent = (typeof PostHogMCPAnalyticsEvent)[keyof typeof PostHogMCPAnalyticsEvent];
65
83
  declare const PostHogMCPAnalyticsProperty: {
66
84
  readonly AiInputState: "$ai_input_state";
67
85
  readonly AiIsError: "$ai_is_error";
@@ -77,6 +95,7 @@ declare const PostHogMCPAnalyticsProperty: {
77
95
  readonly DurationMs: "$mcp_duration_ms";
78
96
  readonly IsError: "$mcp_is_error";
79
97
  readonly Intent: "$mcp_intent";
98
+ readonly IntentSource: "$mcp_intent_source";
80
99
  readonly Parameters: "$mcp_parameters";
81
100
  readonly ResourceName: "$mcp_resource_name";
82
101
  readonly Response: "$mcp_response";
@@ -100,6 +119,7 @@ type PostHogMCPAnalyticsProperty = (typeof PostHogMCPAnalyticsProperty)[keyof ty
100
119
  * @param options.enableAITracing - Emits `$ai_span` events for tool calls so MCP activity appears in PostHog LLM analytics. Defaults to false.
101
120
  * @param options.enableTracing - Enables tracking of tool calls and usage patterns.
102
121
  * @param options.context - Enables the required "context" parameter on tools to capture user intent. Pass false to disable, or an object with a custom description.
122
+ * @param options.intentFallback - Optional consumer-supplied callback invoked when a tool call has no explicit `context` argument. Return a short user intent string to capture as `$mcp_intent` (with `$mcp_intent_source = "inferred"`). The SDK does not infer anything on its own — this is purely a slot for your own derivation logic (e.g. a switch on `request.params.name`, an LLM call, etc.). Runs on the hot path of every uncontextualized tool call.
103
123
  * @param options.identify - Async function to identify users and attach custom data to their sessions.
104
124
  * @param options.redactSensitiveInformation - Function to redact sensitive data before sending to PostHog.
105
125
  * @param options.eventTags - Callback invoked on every auto-captured event (tool calls, tool lists, initialize) to attach string key-value tags. Tags are intended to be indexed and queryable in PostHog — use them for structured metadata you'll want to filter or group by (e.g., trace IDs, environments, regions). Tags are validated client-side: keys must be ≤32 chars matching `[a-zA-Z0-9$_.:\- ]`, values must be strings ≤200 chars with no newlines, max 50 entries per event. Invalid entries are silently dropped with a warning logged to `~/posthog-mcp-analytics.log`. If the callback throws or returns null, tags are omitted. Receives the same `(request, extra)` arguments as `identify`.
@@ -236,5 +256,5 @@ declare function track<TServer>(server: TServer, options?: MCPAnalyticsOptions):
236
256
  declare function publishCustomEvent(serverOrSessionId: unknown, eventData?: CustomEventData): Promise<void>;
237
257
  type IdentifyFunction = MCPAnalyticsOptions["identify"];
238
258
  //#endregion
239
- export { type CustomEventData, IdentifyFunction, type MCPAnalyticsContextOptions, type MCPAnalyticsOptions, PostHogMCPAnalyticsProperty, type RedactFunction, type UserIdentity, publishCustomEvent, track };
259
+ export { type CustomEventData, IdentifyFunction, type MCPAnalyticsContextOptions, type MCPAnalyticsIntentSource, type MCPAnalyticsOptions, POSTHOG_MCP_ANALYTICS_SOURCE, PostHogMCPAnalyticsEvent, PostHogMCPAnalyticsProperty, type RedactFunction, type UserIdentity, publishCustomEvent, track };
240
260
  //# sourceMappingURL=index.d.cts.map
package/dist/index.d.mts CHANGED
@@ -27,6 +27,7 @@ interface MCPAnalyticsOptions {
27
27
  eventTags?: (request: MCPRequestLike, extra?: CompatibleRequestHandlerExtra) => Record<string, string> | null | Promise<Record<string, string> | null>;
28
28
  host?: string;
29
29
  identify?: (request: MCPRequestLike, extra?: CompatibleRequestHandlerExtra) => Promise<UserIdentity | null>;
30
+ intentFallback?: (request: MCPRequestLike, extra?: CompatibleRequestHandlerExtra) => MaybePromise<string | null | undefined>;
30
31
  posthogClient?: PostHogCaptureClient;
31
32
  posthogOptions?: Pick<PostHogOptions, "fetch" | "flushAt" | "flushInterval" | "host" | "requestTimeout" | "waitUntil" | "waitUntilDebounceMs" | "waitUntilMaxWaitMs">;
32
33
  redactSensitiveInformation?: RedactFunction;
@@ -35,6 +36,8 @@ interface MCPAnalyticsOptions {
35
36
  interface MCPAnalyticsContextOptions {
36
37
  description?: string;
37
38
  }
39
+ type MaybePromise<T> = T | Promise<T>;
40
+ type MCPAnalyticsIntentSource = "context_parameter" | "inferred";
38
41
  type RedactFunction = (text: string) => Promise<string>;
39
42
  interface CompatibleRequestHandlerExtra {
40
43
  headers?: Record<string, string | string[]>;
@@ -61,6 +64,21 @@ interface CustomEventData {
61
64
  }
62
65
  //#endregion
63
66
  //#region src/modules/constants.d.ts
67
+ declare const POSTHOG_MCP_ANALYTICS_SOURCE = "posthog_mcp_analytics";
68
+ declare const PostHogMCPAnalyticsEvent: {
69
+ readonly AiSpan: "$ai_span";
70
+ readonly Custom: "mcp_custom";
71
+ readonly Exception: "$exception";
72
+ readonly Identify: "$identify";
73
+ readonly Initialize: "mcp_initialize";
74
+ readonly PromptGet: "mcp_prompt_get";
75
+ readonly PromptsList: "mcp_prompts_list";
76
+ readonly ResourceRead: "mcp_resource_read";
77
+ readonly ResourcesList: "mcp_resources_list";
78
+ readonly ToolCall: "mcp_tool_call";
79
+ readonly ToolsList: "mcp_tools_list";
80
+ };
81
+ type PostHogMCPAnalyticsEvent = (typeof PostHogMCPAnalyticsEvent)[keyof typeof PostHogMCPAnalyticsEvent];
64
82
  declare const PostHogMCPAnalyticsProperty: {
65
83
  readonly AiInputState: "$ai_input_state";
66
84
  readonly AiIsError: "$ai_is_error";
@@ -76,6 +94,7 @@ declare const PostHogMCPAnalyticsProperty: {
76
94
  readonly DurationMs: "$mcp_duration_ms";
77
95
  readonly IsError: "$mcp_is_error";
78
96
  readonly Intent: "$mcp_intent";
97
+ readonly IntentSource: "$mcp_intent_source";
79
98
  readonly Parameters: "$mcp_parameters";
80
99
  readonly ResourceName: "$mcp_resource_name";
81
100
  readonly Response: "$mcp_response";
@@ -99,6 +118,7 @@ type PostHogMCPAnalyticsProperty = (typeof PostHogMCPAnalyticsProperty)[keyof ty
99
118
  * @param options.enableAITracing - Emits `$ai_span` events for tool calls so MCP activity appears in PostHog LLM analytics. Defaults to false.
100
119
  * @param options.enableTracing - Enables tracking of tool calls and usage patterns.
101
120
  * @param options.context - Enables the required "context" parameter on tools to capture user intent. Pass false to disable, or an object with a custom description.
121
+ * @param options.intentFallback - Optional consumer-supplied callback invoked when a tool call has no explicit `context` argument. Return a short user intent string to capture as `$mcp_intent` (with `$mcp_intent_source = "inferred"`). The SDK does not infer anything on its own — this is purely a slot for your own derivation logic (e.g. a switch on `request.params.name`, an LLM call, etc.). Runs on the hot path of every uncontextualized tool call.
102
122
  * @param options.identify - Async function to identify users and attach custom data to their sessions.
103
123
  * @param options.redactSensitiveInformation - Function to redact sensitive data before sending to PostHog.
104
124
  * @param options.eventTags - Callback invoked on every auto-captured event (tool calls, tool lists, initialize) to attach string key-value tags. Tags are intended to be indexed and queryable in PostHog — use them for structured metadata you'll want to filter or group by (e.g., trace IDs, environments, regions). Tags are validated client-side: keys must be ≤32 chars matching `[a-zA-Z0-9$_.:\- ]`, values must be strings ≤200 chars with no newlines, max 50 entries per event. Invalid entries are silently dropped with a warning logged to `~/posthog-mcp-analytics.log`. If the callback throws or returns null, tags are omitted. Receives the same `(request, extra)` arguments as `identify`.
@@ -235,5 +255,5 @@ declare function track<TServer>(server: TServer, options?: MCPAnalyticsOptions):
235
255
  declare function publishCustomEvent(serverOrSessionId: unknown, eventData?: CustomEventData): Promise<void>;
236
256
  type IdentifyFunction = MCPAnalyticsOptions["identify"];
237
257
  //#endregion
238
- export { type CustomEventData, IdentifyFunction, type MCPAnalyticsContextOptions, type MCPAnalyticsOptions, PostHogMCPAnalyticsProperty, type RedactFunction, type UserIdentity, publishCustomEvent, track };
258
+ export { type CustomEventData, IdentifyFunction, type MCPAnalyticsContextOptions, type MCPAnalyticsIntentSource, type MCPAnalyticsOptions, POSTHOG_MCP_ANALYTICS_SOURCE, PostHogMCPAnalyticsEvent, PostHogMCPAnalyticsProperty, type RedactFunction, type UserIdentity, publishCustomEvent, track };
239
259
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -119,15 +119,14 @@ function getMCPCompatibleErrorMessage(error) {
119
119
  return "Unknown error";
120
120
  }
121
121
  //#endregion
122
- //#region src/modules/ids.ts
123
- function newPrefixedId(prefix) {
124
- return `${prefix}_${randomUUID()}`;
125
- }
126
- function deterministicPrefixedId(prefix, input) {
127
- return `${prefix}_${createHash("sha256").update(input).digest("hex").slice(0, 32)}`;
128
- }
129
- //#endregion
130
122
  //#region src/modules/event-types.ts
123
+ /**
124
+ * Internal SDK event vocabulary.
125
+ *
126
+ * These values are not imported from `@modelcontextprotocol/sdk`; they are the
127
+ * protocol-shaped event types this SDK observes before mapping them to PostHog
128
+ * event names.
129
+ */
131
130
  const MCPAnalyticsEventType = {
132
131
  identify: "posthog:identify",
133
132
  custom: "posthog:custom",
@@ -140,6 +139,14 @@ const MCPAnalyticsEventType = {
140
139
  mcpToolsList: "mcp:tools/list"
141
140
  };
142
141
  //#endregion
142
+ //#region src/modules/ids.ts
143
+ function newPrefixedId(prefix) {
144
+ return `${prefix}_${randomUUID()}`;
145
+ }
146
+ function deterministicPrefixedId(prefix, input) {
147
+ return `${prefix}_${createHash("sha256").update(input).digest("hex").slice(0, 32)}`;
148
+ }
149
+ //#endregion
143
150
  //#region src/modules/validation.ts
144
151
  const TAG_KEY_REGEX = /^[a-zA-Z0-9$_.:\- ]+$/;
145
152
  const MAX_TAG_KEY_LENGTH = 32;
@@ -343,6 +350,19 @@ function getRequestResourceName(request) {
343
350
  //#region src/modules/constants.ts
344
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."`;
345
352
  const POSTHOG_MCP_ANALYTICS_SOURCE = "posthog_mcp_analytics";
353
+ const PostHogMCPAnalyticsEvent = {
354
+ AiSpan: "$ai_span",
355
+ Custom: "mcp_custom",
356
+ Exception: "$exception",
357
+ Identify: "$identify",
358
+ Initialize: "mcp_initialize",
359
+ PromptGet: "mcp_prompt_get",
360
+ PromptsList: "mcp_prompts_list",
361
+ ResourceRead: "mcp_resource_read",
362
+ ResourcesList: "mcp_resources_list",
363
+ ToolCall: "mcp_tool_call",
364
+ ToolsList: "mcp_tools_list"
365
+ };
346
366
  const PostHogMCPAnalyticsProperty = {
347
367
  AiInputState: "$ai_input_state",
348
368
  AiIsError: "$ai_is_error",
@@ -358,6 +378,7 @@ const PostHogMCPAnalyticsProperty = {
358
378
  DurationMs: "$mcp_duration_ms",
359
379
  IsError: "$mcp_is_error",
360
380
  Intent: "$mcp_intent",
381
+ IntentSource: "$mcp_intent_source",
361
382
  Parameters: "$mcp_parameters",
362
383
  ResourceName: "$mcp_resource_name",
363
384
  Response: "$mcp_response",
@@ -369,8 +390,17 @@ const PostHogMCPAnalyticsProperty = {
369
390
  };
370
391
  //#endregion
371
392
  //#region src/modules/posthog-events.ts
372
- const MCP_EVENT_PREFIX_REGEX = /^mcp:/;
373
- const SLASH_REGEX = /\//g;
393
+ const BUILT_IN_EVENT_NAME_BY_TYPE = {
394
+ [MCPAnalyticsEventType.custom]: PostHogMCPAnalyticsEvent.Custom,
395
+ [MCPAnalyticsEventType.identify]: PostHogMCPAnalyticsEvent.Identify,
396
+ [MCPAnalyticsEventType.mcpInitialize]: PostHogMCPAnalyticsEvent.Initialize,
397
+ [MCPAnalyticsEventType.mcpPromptsGet]: PostHogMCPAnalyticsEvent.PromptGet,
398
+ [MCPAnalyticsEventType.mcpPromptsList]: PostHogMCPAnalyticsEvent.PromptsList,
399
+ [MCPAnalyticsEventType.mcpResourcesList]: PostHogMCPAnalyticsEvent.ResourcesList,
400
+ [MCPAnalyticsEventType.mcpResourcesRead]: PostHogMCPAnalyticsEvent.ResourceRead,
401
+ [MCPAnalyticsEventType.mcpToolsCall]: PostHogMCPAnalyticsEvent.ToolCall,
402
+ [MCPAnalyticsEventType.mcpToolsList]: PostHogMCPAnalyticsEvent.ToolsList
403
+ };
374
404
  function getDistinctId(event) {
375
405
  return event.identifyActorGivenId || event.sessionId || "anonymous";
376
406
  }
@@ -385,7 +415,6 @@ function buildPostHogCaptureEvents(event, options = {}) {
385
415
  }
386
416
  function buildCaptureEvent(event, options) {
387
417
  const distinctId = getDistinctId(event);
388
- const eventName = mapEventType(event.eventType);
389
418
  const timestamp = getTimestamp(event);
390
419
  const properties = {
391
420
  [PostHogMCPAnalyticsProperty.SessionId]: event.sessionId,
@@ -395,7 +424,7 @@ function buildCaptureEvent(event, options) {
395
424
  addTraceReferenceProperties(event, properties, options);
396
425
  addCustomEventProperties(event, properties);
397
426
  return {
398
- event: eventName,
427
+ event: BUILT_IN_EVENT_NAME_BY_TYPE[event.eventType],
399
428
  distinct_id: distinctId,
400
429
  properties,
401
430
  timestamp,
@@ -427,6 +456,7 @@ function addCommonEventProperties(event, properties) {
427
456
  if (event.clientName) properties[PostHogMCPAnalyticsProperty.ClientName] = event.clientName;
428
457
  if (event.clientVersion) properties[PostHogMCPAnalyticsProperty.ClientVersion] = event.clientVersion;
429
458
  if (event.userIntent) properties[PostHogMCPAnalyticsProperty.Intent] = event.userIntent;
459
+ if (event.userIntentSource) properties[PostHogMCPAnalyticsProperty.IntentSource] = event.userIntentSource;
430
460
  if (event.isError !== void 0) properties[PostHogMCPAnalyticsProperty.IsError] = event.isError;
431
461
  if (event.parameters !== void 0) properties[PostHogMCPAnalyticsProperty.Parameters] = event.parameters;
432
462
  if (event.response !== void 0) properties[PostHogMCPAnalyticsProperty.Response] = event.response;
@@ -460,7 +490,7 @@ function buildExceptionEvent(event) {
460
490
  if (event.clientName) properties[PostHogMCPAnalyticsProperty.ClientName] = event.clientName;
461
491
  if (event.clientVersion) properties[PostHogMCPAnalyticsProperty.ClientVersion] = event.clientVersion;
462
492
  return {
463
- event: "$exception",
493
+ event: PostHogMCPAnalyticsEvent.Exception,
464
494
  distinct_id: distinctId,
465
495
  properties,
466
496
  timestamp,
@@ -486,27 +516,17 @@ function buildAISpanEvent(event) {
486
516
  if (event.serverName) properties[PostHogMCPAnalyticsProperty.ServerName] = event.serverName;
487
517
  if (event.clientName) properties[PostHogMCPAnalyticsProperty.ClientName] = event.clientName;
488
518
  if (event.userIntent) properties[PostHogMCPAnalyticsProperty.Intent] = event.userIntent;
519
+ if (event.userIntentSource) properties[PostHogMCPAnalyticsProperty.IntentSource] = event.userIntentSource;
489
520
  if (event.tags) for (const [key, value] of Object.entries(event.tags)) properties[key] = value;
490
521
  if (event.properties) for (const [key, value] of Object.entries(event.properties)) properties[key] = value;
491
522
  return {
492
- event: "$ai_span",
523
+ event: PostHogMCPAnalyticsEvent.AiSpan,
493
524
  distinct_id: distinctId,
494
525
  properties,
495
526
  timestamp,
496
527
  type: "capture"
497
528
  };
498
529
  }
499
- function mapEventType(eventType) {
500
- return {
501
- [MCPAnalyticsEventType.mcpToolsCall]: "mcp_tool_call",
502
- [MCPAnalyticsEventType.mcpToolsList]: "mcp_tools_list",
503
- [MCPAnalyticsEventType.mcpInitialize]: "mcp_initialize",
504
- [MCPAnalyticsEventType.mcpResourcesRead]: "mcp_resource_read",
505
- [MCPAnalyticsEventType.mcpResourcesList]: "mcp_resources_list",
506
- [MCPAnalyticsEventType.mcpPromptsGet]: "mcp_prompt_get",
507
- [MCPAnalyticsEventType.mcpPromptsList]: "mcp_prompts_list"
508
- }[eventType] || `mcp_${eventType.replace(MCP_EVENT_PREFIX_REGEX, "").replace(SLASH_REGEX, "_")}`;
509
- }
510
530
  //#endregion
511
531
  //#region src/modules/redaction.ts
512
532
  /**
@@ -700,7 +720,7 @@ function sanitizeParameters(obj) {
700
720
  }
701
721
  //#endregion
702
722
  //#region package.json
703
- var version = "0.0.1";
723
+ var version = "0.0.3";
704
724
  //#endregion
705
725
  //#region src/modules/session.ts
706
726
  function newSessionId() {
@@ -1084,7 +1104,6 @@ var EventQueue = class {
1084
1104
  timestamp: new Date(captureEvent.timestamp)
1085
1105
  });
1086
1106
  writeToLog(`Queued PostHog event ${event.id} | ${event.eventType} | ${event.duration} ms | ${event.identifyActorGivenId || "anonymous"}`);
1087
- writeToLog(`Event details: ${JSON.stringify(event)}`);
1088
1107
  } catch (error) {
1089
1108
  writeToLog(`Failed to queue PostHog event ${event.id}: ${getMCPCompatibleErrorMessage(error)}`);
1090
1109
  throw error;
@@ -1147,7 +1166,7 @@ function publishEvent(server, eventInput) {
1147
1166
  id: eventInput.id || "",
1148
1167
  sessionId: eventInput.sessionId || data.sessionId,
1149
1168
  apiKey: data.apiKey,
1150
- eventType: eventInput.eventType || "",
1169
+ eventType: eventInput.eventType || MCPAnalyticsEventType.custom,
1151
1170
  timestamp: eventInput.timestamp || /* @__PURE__ */ new Date(),
1152
1171
  duration,
1153
1172
  ipAddress: sessionInfo.ipAddress,
@@ -1164,6 +1183,7 @@ function publishEvent(server, eventInput) {
1164
1183
  parameters: eventInput.parameters,
1165
1184
  response: eventInput.response,
1166
1185
  userIntent: eventInput.userIntent,
1186
+ userIntentSource: eventInput.userIntentSource,
1167
1187
  isError: eventInput.isError,
1168
1188
  error: eventInput.error,
1169
1189
  redactionFn: eventInput.redactionFn,
@@ -1902,6 +1922,49 @@ function setupMCPAnalyticsTools(server) {
1902
1922
  }
1903
1923
  }
1904
1924
  //#endregion
1925
+ //#region src/modules/intent.ts
1926
+ function getContextArgument$2(request) {
1927
+ const context = request.params?.arguments?.context;
1928
+ return typeof context === "string" && context.trim() ? context : void 0;
1929
+ }
1930
+ function normalizeIntent(intent) {
1931
+ if (typeof intent !== "string") return null;
1932
+ const trimmed = intent.trim();
1933
+ return trimmed ? trimmed : null;
1934
+ }
1935
+ async function runIntentFallback(data, request, extra) {
1936
+ if (!data.options.intentFallback) return null;
1937
+ try {
1938
+ const intent = normalizeIntent(await data.options.intentFallback(request, extra));
1939
+ return intent ? {
1940
+ intent,
1941
+ source: "inferred"
1942
+ } : null;
1943
+ } catch (error) {
1944
+ writeToLog(`intentFallback callback error: ${error}`);
1945
+ return null;
1946
+ }
1947
+ }
1948
+ async function resolveToolCallIntent(data, request, extra) {
1949
+ const contextArgument = getContextArgument$2(request);
1950
+ if (isContextEnabled(data.options.context) && request.params?.name !== "get_more_tools" && contextArgument) return {
1951
+ intent: contextArgument,
1952
+ source: "context_parameter"
1953
+ };
1954
+ return await runIntentFallback(data, request, extra);
1955
+ }
1956
+ function setEventIntent(event, resolvedIntent) {
1957
+ if (!resolvedIntent) return;
1958
+ event.userIntent = resolvedIntent.intent;
1959
+ event.userIntentSource = resolvedIntent.source;
1960
+ }
1961
+ function setExplicitContextIntent(event, context) {
1962
+ const intent = normalizeIntent(context);
1963
+ if (!intent) return;
1964
+ event.userIntent = intent;
1965
+ event.userIntentSource = "context_parameter";
1966
+ }
1967
+ //#endregion
1905
1968
  //#region src/modules/tracing.ts
1906
1969
  function isToolResultError$1(result) {
1907
1970
  return !!result && typeof result === "object" && "isError" in result && result.isError === true;
@@ -2049,18 +2112,20 @@ async function handleToolCallRequest(server, originalCallToolHandler, request, e
2049
2112
  try {
2050
2113
  await handleIdentify(server, data, request, extra);
2051
2114
  await applyResolvedMetadata$1(event, data, request, extra);
2052
- setToolCallContext(event, data.options.context, request);
2115
+ setEventIntent(event, await resolveToolCallIntent(data, request, extra));
2053
2116
  const result = await executeToolCall(server, originalCallToolHandler, request, extra, event);
2054
2117
  if (isToolResultError$1(result)) {
2055
2118
  event.isError = true;
2056
2119
  event.error = captureException(result);
2057
- }
2120
+ } else event.isError = false;
2058
2121
  event.response = result;
2122
+ event.duration = getEventDuration(event);
2059
2123
  publishEvent(server, event);
2060
2124
  return result;
2061
2125
  } catch (error) {
2062
2126
  event.isError = true;
2063
2127
  event.error = captureException(error);
2128
+ event.duration = getEventDuration(event);
2064
2129
  publishEvent(server, event);
2065
2130
  throw error;
2066
2131
  }
@@ -2068,7 +2133,7 @@ async function handleToolCallRequest(server, originalCallToolHandler, request, e
2068
2133
  async function executeToolCall(server, originalCallToolHandler, request, extra, event) {
2069
2134
  if (request.params?.name === "get_more_tools") {
2070
2135
  const context = getContextArgument$1(request) || "";
2071
- event.userIntent = context;
2136
+ setExplicitContextIntent(event, context);
2072
2137
  return handleReportMissing({ context });
2073
2138
  }
2074
2139
  if (originalCallToolHandler) return await originalCallToolHandler(request, extra);
@@ -2084,11 +2149,6 @@ async function applyResolvedMetadata$1(event, data, request, extra) {
2084
2149
  const resolvedProperties = await resolveEventProperties(data, request, extra);
2085
2150
  if (resolvedProperties) event.properties = resolvedProperties;
2086
2151
  }
2087
- function setToolCallContext(event, context, request) {
2088
- if (!(isContextEnabled(context) && request.params?.name !== "get_more_tools")) return;
2089
- const contextArgument = getContextArgument$1(request);
2090
- if (contextArgument) event.userIntent = contextArgument;
2091
- }
2092
2152
  function getContextArgument$1(request) {
2093
2153
  const context = request.params?.arguments?.context;
2094
2154
  return typeof context === "string" ? context : void 0;
@@ -2322,8 +2382,7 @@ async function initializeToolCallEvent(server, request, extra, startTime) {
2322
2382
  await handleIdentify(server, data, request, extra);
2323
2383
  event.sessionId = data.sessionId;
2324
2384
  await applyResolvedMetadata(event, data, request, extra);
2325
- const contextArgument = getContextArgument(request);
2326
- if (isContextEnabled(data.options.context) && contextArgument) event.userIntent = contextArgument;
2385
+ setEventIntent(event, await resolveToolCallIntent(data, request, extra));
2327
2386
  return {
2328
2387
  event,
2329
2388
  shouldPublishEvent: true
@@ -2346,7 +2405,10 @@ async function executeReportMissingTool(server, request, tracing, startTime) {
2346
2405
  try {
2347
2406
  const context = getContextArgument(request) || "";
2348
2407
  const result = await handleReportMissing({ context });
2349
- publishSuccessfulToolEvent(server, tracing, result, startTime, { userIntent: context });
2408
+ publishSuccessfulToolEvent(server, tracing, result, startTime, {
2409
+ userIntent: context,
2410
+ userIntentSource: "context_parameter"
2411
+ });
2350
2412
  return result;
2351
2413
  } catch (error) {
2352
2414
  publishFailedToolEvent(server, tracing, error, startTime);
@@ -2374,12 +2436,15 @@ function getContextArgument(request) {
2374
2436
  }
2375
2437
  function publishSuccessfulToolEvent(server, tracing, result, startTime, options = {}) {
2376
2438
  if (!(tracing.event && tracing.shouldPublishEvent)) return;
2377
- if (options.userIntent) tracing.event.userIntent = options.userIntent;
2439
+ if (options.userIntent) {
2440
+ setExplicitContextIntent(tracing.event, options.userIntent);
2441
+ if (options.userIntentSource) tracing.event.userIntentSource = options.userIntentSource;
2442
+ }
2378
2443
  if (isToolResultError(result)) {
2379
2444
  tracing.event.isError = true;
2380
2445
  tracing.event.error = captureException(options.capturedError || result);
2381
2446
  options.clearCapturedError?.();
2382
- }
2447
+ } else tracing.event.isError = false;
2383
2448
  tracing.event.response = result;
2384
2449
  tracing.event.duration = Date.now() - startTime.getTime();
2385
2450
  publishEvent(server, tracing.event);
@@ -2416,6 +2481,7 @@ function setupTracking(server) {
2416
2481
  * @param options.enableAITracing - Emits `$ai_span` events for tool calls so MCP activity appears in PostHog LLM analytics. Defaults to false.
2417
2482
  * @param options.enableTracing - Enables tracking of tool calls and usage patterns.
2418
2483
  * @param options.context - Enables the required "context" parameter on tools to capture user intent. Pass false to disable, or an object with a custom description.
2484
+ * @param options.intentFallback - Optional consumer-supplied callback invoked when a tool call has no explicit `context` argument. Return a short user intent string to capture as `$mcp_intent` (with `$mcp_intent_source = "inferred"`). The SDK does not infer anything on its own — this is purely a slot for your own derivation logic (e.g. a switch on `request.params.name`, an LLM call, etc.). Runs on the hot path of every uncontextualized tool call.
2419
2485
  * @param options.identify - Async function to identify users and attach custom data to their sessions.
2420
2486
  * @param options.redactSensitiveInformation - Function to redact sensitive data before sending to PostHog.
2421
2487
  * @param options.eventTags - Callback invoked on every auto-captured event (tool calls, tool lists, initialize) to attach string key-value tags. Tags are intended to be indexed and queryable in PostHog — use them for structured metadata you'll want to filter or group by (e.g., trace IDs, environments, regions). Tags are validated client-side: keys must be ≤32 chars matching `[a-zA-Z0-9$_.:\- ]`, values must be strings ≤200 chars with no newlines, max 50 entries per event. Invalid entries are silently dropped with a warning logged to `~/posthog-mcp-analytics.log`. If the callback throws or returns null, tags are omitted. Receives the same `(request, extra)` arguments as `identify`.
@@ -2543,6 +2609,7 @@ function buildTrackingData(lowLevelServer, options) {
2543
2609
  enableAITracing: options.enableAITracing ?? false,
2544
2610
  enableTracing: options.enableTracing ?? true,
2545
2611
  context: options.context,
2612
+ intentFallback: options.intentFallback,
2546
2613
  identify: options.identify,
2547
2614
  redactSensitiveInformation: options.redactSensitiveInformation,
2548
2615
  eventTags: options.eventTags,
@@ -2687,6 +2754,6 @@ function publishResolvedCustomEvent(target, event) {
2687
2754
  eventQueue.add(event);
2688
2755
  }
2689
2756
  //#endregion
2690
- export { PostHogMCPAnalyticsProperty, publishCustomEvent, track };
2757
+ export { POSTHOG_MCP_ANALYTICS_SOURCE, PostHogMCPAnalyticsEvent, PostHogMCPAnalyticsProperty, publishCustomEvent, track };
2691
2758
 
2692
2759
  //# sourceMappingURL=index.mjs.map