@posthog/mcp 0.16.3 → 0.17.0
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 +36 -12
- package/dist/extensions/instrument-highlevel.d.ts.map +1 -1
- package/dist/extensions/instrument-highlevel.js +2 -2
- package/dist/extensions/instrument-highlevel.mjs +3 -3
- package/dist/extensions/instrument-lowlevel.d.ts.map +1 -1
- package/dist/extensions/instrument-lowlevel.js +3 -2
- package/dist/extensions/instrument-lowlevel.mjs +4 -3
- package/dist/extensions/instrumentation.d.ts +0 -20
- package/dist/extensions/instrumentation.d.ts.map +1 -1
- package/dist/extensions/instrumentation.js +33 -18
- package/dist/extensions/instrumentation.mjs +35 -17
- package/dist/extensions/model-parameters.d.ts +1 -1
- package/dist/extensions/model-parameters.d.ts.map +1 -1
- package/dist/extensions/model-parameters.js +1 -1
- package/dist/extensions/model-parameters.mjs +1 -1
- package/dist/extensions/posthog-mcp.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.mjs +3 -2
- package/dist/types.d.ts +3 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.mjs +1 -1
- package/package.json +5 -4
- package/src/extensions/instrument-highlevel.ts +2 -11
- package/src/extensions/instrument-lowlevel.ts +3 -11
- package/src/extensions/instrumentation.ts +55 -52
- package/src/extensions/model-parameters.ts +4 -2
- package/src/extensions/posthog-mcp.ts +1 -1
- package/src/index.ts +6 -2
- package/src/types.ts +3 -3
- package/src/version.ts +1 -1
package/README.md
CHANGED
|
@@ -80,7 +80,9 @@ Protocol revision is a property of each **request**, not of the server: a v2 ser
|
|
|
80
80
|
|
|
81
81
|
- **On `2026-07-28`** there is no `initialize` and no session header — the revision removed
|
|
82
82
|
protocol-level sessions, and this SDK will not mint one. Session correlation therefore comes from
|
|
83
|
-
`enableConversationId`, which is **
|
|
83
|
+
`enableConversationId`, which is **on by default**. Without it every request is its own `$session_id`.
|
|
84
|
+
The `get_more_tools` and `send_feedback` virtual tools also use this handle, including calls handled
|
|
85
|
+
by a fresh server instance.
|
|
84
86
|
- **On `2025-11-25`**, the session id and the client's name and version are exchanged once at
|
|
85
87
|
`initialize`. If your server builds a fresh `McpServer` per HTTP request — which
|
|
86
88
|
`createMcpHandler` does by default — the instance serving a later `tools/call` never saw that
|
|
@@ -158,9 +160,31 @@ over-redacting ordinary prose. It also applies only to `$mcp_intent`: structured
|
|
|
158
160
|
results are left as-is, because the same shapes are often legitimate data there. If you need a stronger
|
|
159
161
|
guarantee, `context: false` and the `beforeSend` hook above remain the ways to drop the field entirely.
|
|
160
162
|
|
|
163
|
+
### Defaults and opt-outs
|
|
164
|
+
|
|
165
|
+
On fresh low-level instances, tools with an `outputSchema` deliver new handles through `content` only. Clients that consume only `structuredContent` will not echo those handles, so correlation is not guaranteed for that combination. Unknown ownership can also cause a prompt-back block to be appended for a schema that discovery would not extend; disable `enableConversationId` if that contract is unsuitable.
|
|
166
|
+
|
|
167
|
+
A valid echoed conversation handle takes precedence over transport sessions. A request that already carries a transport session or a PostHog session token does not mint a new handle or append a prompt-back block. Requests carrying neither use the conversation fallback.
|
|
168
|
+
|
|
169
|
+
Intent, model capture, conversation correlation, and exception capture are enabled by default.
|
|
170
|
+
Missing-capability reporting and feedback collection remain disabled.
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
instrument(server, posthog, { captureModel: false, enableConversationId: false })
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Model capture adds a required `llm_model` argument to compatible tool schemas. Dispatch never
|
|
177
|
+
enforces it, so servers keep working; strict-schema clients see the new field. Set
|
|
178
|
+
`captureModel: false` to leave schemas untouched. Conversation correlation adds an optional
|
|
179
|
+
`conversation_id` argument and returns a handle in eligible tool results. Clients must echo that
|
|
180
|
+
handle to group later calls; calls without it mint new handles. Set `enableConversationId: false`
|
|
181
|
+
to retain transport-based session grouping and unchanged response content. Custom `PostHogMCP`
|
|
182
|
+
dispatchers enable model capture by default, but continue to supply their own session IDs.
|
|
183
|
+
The reasoning behind these defaults is in [ADR-0013](./docs/adr/0013-analytics-capture-is-on-by-default.md).
|
|
184
|
+
|
|
161
185
|
### What `$mcp_llm_model` records, and when it stays empty
|
|
162
186
|
|
|
163
|
-
`captureModel` is **
|
|
187
|
+
`captureModel` is **on** by default. The SDK records the best model id visible to the
|
|
164
188
|
server as `$mcp_llm_model`. Recognized client metadata wins with source `client_metadata`. Otherwise,
|
|
165
189
|
the SDK injects a required `llm_model` parameter and records the answer with source `self_reported`.
|
|
166
190
|
|
|
@@ -171,32 +195,32 @@ Missing, blank, and `unknown` values are recorded as nothing.
|
|
|
171
195
|
The recognized metadata path is Codex's `x-codex-turn-metadata.model` field inside request `_meta`.
|
|
172
196
|
Other clients keep using self-report until they expose a stable model field.
|
|
173
197
|
|
|
174
|
-
|
|
175
|
-
|
|
198
|
+
Like `context`, self-report follows the ownership rule of ADR-0011: reading `llm_model` fails
|
|
199
|
+
open where the SDK cannot tell who declared it, stripping it requires proof that the SDK did:
|
|
176
200
|
|
|
177
201
|
- `instrument(server)` on a high-level `McpServer` resolves ownership for your registered tools per
|
|
178
202
|
request from the live tool registry, so those work even on a fresh instance.
|
|
179
203
|
- The `get_more_tools` virtual tool works on any instance and on either server type: the SDK writes
|
|
180
204
|
that descriptor itself, so what it declares is known without a listing.
|
|
181
|
-
- Instrumenting a low-level `Server` learns ownership
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
205
|
+
- Instrumenting a low-level `Server` learns ownership while serving `tools/list`. A fresh instance
|
|
206
|
+
that never served one — `createMcpHandler`, or `@rekog/mcp-nest` in its stateless mode — has no
|
|
207
|
+
answer, so it records `llm_model` as the self-reported model and strips nothing. A tool that
|
|
208
|
+
declares its own `llm_model` on such an instance is therefore recorded under `$mcp_llm_model`
|
|
209
|
+
until a listing says otherwise; `captureModel: false` or dropping the property in `beforeSend`
|
|
210
|
+
are the escapes. The SDK never replays your listing handler on the call path to find out.
|
|
187
211
|
|
|
188
212
|
As with `context`, what matters is instance lifetime rather than statelessness: a transport-stateless
|
|
189
213
|
server (`sessionIdGenerator: undefined`) that keeps one long-lived server object learns ownership
|
|
190
214
|
from the first `tools/list` and keeps it.
|
|
191
215
|
|
|
192
|
-
For a custom dispatcher,
|
|
216
|
+
For a custom dispatcher, `PostHogMCP` enables the same option by default. Its `prepareToolList()` helper
|
|
193
217
|
injects the field and records ownership by tool name; `prepareToolCall()` returns `llmModel` and
|
|
194
218
|
`llmModelSource` while removing the SDK-owned argument before dispatch. Pass both fields to
|
|
195
219
|
`captureToolCall()`. Pass the original tool descriptor on each call so this also works when
|
|
196
220
|
`tools/list` and `tools/call` reach different server replicas:
|
|
197
221
|
|
|
198
222
|
```ts
|
|
199
|
-
const posthog = new PostHogMCP(process.env.POSTHOG_PROJECT_TOKEN
|
|
223
|
+
const posthog = new PostHogMCP(process.env.POSTHOG_PROJECT_TOKEN)
|
|
200
224
|
|
|
201
225
|
const tools = posthog.prepareToolList(serverTools)
|
|
202
226
|
const originalTool = serverTools.find((tool) => tool.name === toolName)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrument-highlevel.d.ts","sourceRoot":"","sources":["../../src/extensions/instrument-highlevel.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAGV,sBAAsB,EAIvB,MAAM,UAAU,CAAA;AAmBjB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"instrument-highlevel.d.ts","sourceRoot":"","sources":["../../src/extensions/instrument-highlevel.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAGV,sBAAsB,EAIvB,MAAM,UAAU,CAAA;AAmBjB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAuRxC,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,sBAAsB,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,CA+BhG"}
|
|
@@ -172,7 +172,7 @@ async function handleToolCallRequest(highLevelServer, server, originalCallToolHa
|
|
|
172
172
|
extra,
|
|
173
173
|
eventType: external_event_types_js_namespaceObject.MCPAnalyticsEventType.mcpMissingCapability,
|
|
174
174
|
explicitContextIntent: context,
|
|
175
|
-
parameterOwnership: (0,
|
|
175
|
+
parameterOwnership: (0, external_analytics_parameters_js_namespaceObject.getAnalyticsParameterOwnership)((0, external_tools_js_namespaceObject.getReportMissingToolDescriptor)(toolName).inputSchema),
|
|
176
176
|
execute: async ()=>(0, external_tools_js_namespaceObject.handleReportMissing)({
|
|
177
177
|
context
|
|
178
178
|
}, data.logger)
|
|
@@ -191,7 +191,7 @@ async function handleToolCallRequest(highLevelServer, server, originalCallToolHa
|
|
|
191
191
|
explicitContextIntent: (0, external_feedback_js_namespaceObject.buildFeedbackIntent)(report),
|
|
192
192
|
omitCapturedParameters: true,
|
|
193
193
|
extraEventProperties: (0, external_feedback_js_namespaceObject.buildFeedbackEventProperties)(report),
|
|
194
|
-
parameterOwnership: (0,
|
|
194
|
+
parameterOwnership: (0, external_analytics_parameters_js_namespaceObject.getAnalyticsParameterOwnership)((0, external_feedback_js_namespaceObject.getFeedbackToolDescriptor)(feedbackOptions).inputSchema),
|
|
195
195
|
execute: async ()=>(0, external_feedback_js_namespaceObject.handleFeedback)(report, feedbackOptions, data.logger)
|
|
196
196
|
});
|
|
197
197
|
}
|
|
@@ -6,7 +6,7 @@ import { MCPAnalyticsEventType } from "./event-types.mjs";
|
|
|
6
6
|
import { getServerTrackingData } from "./internal.mjs";
|
|
7
7
|
import { createWrappedTool, getToolFunction, hasToolFunction } from "./mcp-sdk-compat.mjs";
|
|
8
8
|
import { getReportMissingToolDescriptor, handleReportMissing, resolveMissingCapabilityToolName } from "./tools.mjs";
|
|
9
|
-
import { captureToolCall,
|
|
9
|
+
import { captureToolCall, handleInitializeRequest, handleListToolsRequest, isToolAdvertised, patchRequestHandlers, readToolMetaCategory, traceResourceRequest } from "./instrumentation.mjs";
|
|
10
10
|
import { getContextArgument } from "./tracing-helpers.mjs";
|
|
11
11
|
const wrappedCallbacks = new WeakMap();
|
|
12
12
|
const MCP_ANALYTICS_PROCESSED = Symbol('__posthog_mcp_analytics_processed__');
|
|
@@ -140,7 +140,7 @@ async function handleToolCallRequest(highLevelServer, server, originalCallToolHa
|
|
|
140
140
|
extra,
|
|
141
141
|
eventType: MCPAnalyticsEventType.mcpMissingCapability,
|
|
142
142
|
explicitContextIntent: context,
|
|
143
|
-
parameterOwnership:
|
|
143
|
+
parameterOwnership: getAnalyticsParameterOwnership(getReportMissingToolDescriptor(toolName).inputSchema),
|
|
144
144
|
execute: async ()=>handleReportMissing({
|
|
145
145
|
context
|
|
146
146
|
}, data.logger)
|
|
@@ -159,7 +159,7 @@ async function handleToolCallRequest(highLevelServer, server, originalCallToolHa
|
|
|
159
159
|
explicitContextIntent: buildFeedbackIntent(report),
|
|
160
160
|
omitCapturedParameters: true,
|
|
161
161
|
extraEventProperties: buildFeedbackEventProperties(report),
|
|
162
|
-
parameterOwnership:
|
|
162
|
+
parameterOwnership: getAnalyticsParameterOwnership(getFeedbackToolDescriptor(feedbackOptions).inputSchema),
|
|
163
163
|
execute: async ()=>handleFeedback(report, feedbackOptions, data.logger)
|
|
164
164
|
});
|
|
165
165
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrument-lowlevel.d.ts","sourceRoot":"","sources":["../../src/extensions/instrument-lowlevel.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAiD,aAAa,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"instrument-lowlevel.d.ts","sourceRoot":"","sources":["../../src/extensions/instrument-lowlevel.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAiD,aAAa,EAAE,MAAM,UAAU,CAAA;AAa5F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAoBxC;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,GAAG,IAAI,CA8BtF"}
|
|
@@ -31,6 +31,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
31
31
|
instrumentLowLevelServer: ()=>instrumentLowLevelServer
|
|
32
32
|
});
|
|
33
33
|
const external_feedback_js_namespaceObject = require("./feedback.js");
|
|
34
|
+
const external_analytics_parameters_js_namespaceObject = require("./analytics-parameters.js");
|
|
34
35
|
const external_event_types_js_namespaceObject = require("./event-types.js");
|
|
35
36
|
const external_internal_js_namespaceObject = require("./internal.js");
|
|
36
37
|
const external_tools_js_namespaceObject = require("./tools.js");
|
|
@@ -75,7 +76,7 @@ async function handleToolCallRequest(server, originalCallToolHandler, request, e
|
|
|
75
76
|
extra,
|
|
76
77
|
eventType: external_event_types_js_namespaceObject.MCPAnalyticsEventType.mcpMissingCapability,
|
|
77
78
|
explicitContextIntent: context,
|
|
78
|
-
parameterOwnership: (0,
|
|
79
|
+
parameterOwnership: (0, external_analytics_parameters_js_namespaceObject.getAnalyticsParameterOwnership)((0, external_tools_js_namespaceObject.getReportMissingToolDescriptor)(toolName).inputSchema),
|
|
79
80
|
execute: async ()=>(0, external_tools_js_namespaceObject.handleReportMissing)({
|
|
80
81
|
context
|
|
81
82
|
}, data.logger)
|
|
@@ -94,7 +95,7 @@ async function handleToolCallRequest(server, originalCallToolHandler, request, e
|
|
|
94
95
|
explicitContextIntent: (0, external_feedback_js_namespaceObject.buildFeedbackIntent)(report),
|
|
95
96
|
omitCapturedParameters: true,
|
|
96
97
|
extraEventProperties: (0, external_feedback_js_namespaceObject.buildFeedbackEventProperties)(report),
|
|
97
|
-
parameterOwnership: (0,
|
|
98
|
+
parameterOwnership: (0, external_analytics_parameters_js_namespaceObject.getAnalyticsParameterOwnership)((0, external_feedback_js_namespaceObject.getFeedbackToolDescriptor)(feedbackOptions).inputSchema),
|
|
98
99
|
execute: async ()=>(0, external_feedback_js_namespaceObject.handleFeedback)(report, feedbackOptions, data.logger)
|
|
99
100
|
});
|
|
100
101
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { SEND_FEEDBACK_TOOL_NAME, buildFeedbackEventProperties, buildFeedbackIntent, getFeedbackToolDescriptor, handleFeedback, parseFeedbackReport, resolveCollectFeedbackOptions } from "./feedback.mjs";
|
|
2
|
+
import { getAnalyticsParameterOwnership } from "./analytics-parameters.mjs";
|
|
2
3
|
import { MCPAnalyticsEventType } from "./event-types.mjs";
|
|
3
4
|
import { getServerTrackingData } from "./internal.mjs";
|
|
4
5
|
import { getReportMissingToolDescriptor, handleReportMissing, resolveMissingCapabilityToolName } from "./tools.mjs";
|
|
5
|
-
import { captureToolCall,
|
|
6
|
+
import { captureToolCall, handleInitializeRequest, handleListToolsRequest, isToolAdvertised, patchRequestHandlers, registerFallbackRequestHandler, traceResourceRequest } from "./instrumentation.mjs";
|
|
6
7
|
import { getContextArgument } from "./tracing-helpers.mjs";
|
|
7
8
|
function instrumentLowLevelServer(server, logger) {
|
|
8
9
|
try {
|
|
@@ -43,7 +44,7 @@ async function handleToolCallRequest(server, originalCallToolHandler, request, e
|
|
|
43
44
|
extra,
|
|
44
45
|
eventType: MCPAnalyticsEventType.mcpMissingCapability,
|
|
45
46
|
explicitContextIntent: context,
|
|
46
|
-
parameterOwnership:
|
|
47
|
+
parameterOwnership: getAnalyticsParameterOwnership(getReportMissingToolDescriptor(toolName).inputSchema),
|
|
47
48
|
execute: async ()=>handleReportMissing({
|
|
48
49
|
context
|
|
49
50
|
}, data.logger)
|
|
@@ -62,7 +63,7 @@ async function handleToolCallRequest(server, originalCallToolHandler, request, e
|
|
|
62
63
|
explicitContextIntent: buildFeedbackIntent(report),
|
|
63
64
|
omitCapturedParameters: true,
|
|
64
65
|
extraEventProperties: buildFeedbackEventProperties(report),
|
|
65
|
-
parameterOwnership:
|
|
66
|
+
parameterOwnership: getAnalyticsParameterOwnership(getFeedbackToolDescriptor(feedbackOptions).inputSchema),
|
|
66
67
|
execute: async ()=>handleFeedback(report, feedbackOptions, data.logger)
|
|
67
68
|
});
|
|
68
69
|
}
|
|
@@ -100,26 +100,6 @@ export declare function registerFallbackRequestHandler(server: MCPServerLike, ha
|
|
|
100
100
|
* bare server to instrument() and only then registers its handlers.
|
|
101
101
|
*/
|
|
102
102
|
export declare function patchRequestHandlers(server: MCPServerLike, patches: Record<string, HandlerPatch>): void;
|
|
103
|
-
/**
|
|
104
|
-
* Ownership for an SDK-owned virtual tool (`get_more_tools`, `send_feedback`),
|
|
105
|
-
* resolved from its own descriptor rather than the `tools/list` cache.
|
|
106
|
-
*
|
|
107
|
-
* A virtual-tool branch is only entered when the application does not advertise
|
|
108
|
-
* a tool by this name, so the descriptor is the SDK's own and what it declares
|
|
109
|
-
* is known statically. An instance that never served a listing — a per-request
|
|
110
|
-
* `McpServer`/`Server`, the topology in ADR-0011 — would otherwise read every
|
|
111
|
-
* injected parameter as not-ours and neither capture nor strip it.
|
|
112
|
-
*
|
|
113
|
-
* `conversation_id` still comes from the cache on purpose: resolving it here too
|
|
114
|
-
* would start minting a handle, and appending its prompt-back block, on
|
|
115
|
-
* instances that today mint none. That changes session anchoring (ADR-0004)
|
|
116
|
-
* rather than closing this gap.
|
|
117
|
-
*
|
|
118
|
-
* `virtualToolInputSchema` is required (not defaulted to one specific virtual
|
|
119
|
-
* tool's descriptor) so every call site names the tool it means; each caller
|
|
120
|
-
* passes its own descriptor's `inputSchema`.
|
|
121
|
-
*/
|
|
122
|
-
export declare function getVirtualToolParameterOwnership(data: MCPAnalyticsData, toolName: string, virtualToolInputSchema: unknown): AnalyticsParameterOwnership;
|
|
123
103
|
/**
|
|
124
104
|
* Checks the server's raw listing for a real owner of a candidate virtual tool.
|
|
125
105
|
* This does not depend on a previous client request and does not call the
|
|
@@ -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,UAAU,EACV,gBAAgB,EAChB,cAAc,EACd,aAAa,EAId,MAAM,UAAU,CAAA;AAqBjB,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAIrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAUxC;;;;;;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;;;;;OAKG;IACH,SAAS,CAAC,EAAE,qBAAqB,CAAA;IACjC;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,UAAU,CAAA;IACjC;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC;;;;;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":"AAKA,OAAO,KAAK,EACV,2BAA2B,EAC3B,6BAA6B,EAC7B,uBAAuB,EACvB,UAAU,EACV,gBAAgB,EAChB,cAAc,EACd,aAAa,EAId,MAAM,UAAU,CAAA;AAqBjB,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAIrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAUxC;;;;;;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;;;;;OAKG;IACH,SAAS,CAAC,EAAE,qBAAqB,CAAA;IACjC;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,UAAU,CAAA;IACjC;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAA;CAClC;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CAyEnF;AA+QD,KAAK,iBAAiB,GAAG,OAAO,qBAAqB,CAAC,gBAAgB,GAAG,OAAO,qBAAqB,CAAC,gBAAgB,CAAA;AAuDtH,sFAAsF;AACtF,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,iBAAiB,EAAE,MAAM,EAAE,QAAQ,GAAG,YAAY,CAGjG;AAgDD;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,CACzB,MAAM,EAAE,aAAa,EACrB,eAAe,EAAE,iBAAiB,EAClC,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,6BAA6B,GAAG,SAAS,KAC7C,OAAO,CAAC,OAAO,CAAC,CAAA;AAkBrB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,aAAa,EACrB,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,iBAAiB,EAClC,KAAK,EAAE,YAAY,GAClB,IAAI,CAGN;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,IAAI,CA0CvG;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,6BAA6B,GAAG,SAAS,EAChD,MAAM,EAAE,QAAQ,GACf,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAoB9B;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,aAAa,EACrB,wBAAwB,EAAE,iBAAiB,EAC3C,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,6BAA6B,GAAG,SAAS,EAChD,MAAM,EAAE,QAAQ,GACf,OAAO,CAAC,uBAAuB,CAAC,CAmElC;AAoID,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC1B,KAAK,EAAE,uBAAuB,CAAC,OAAO,CAAC,GAAG,SAAS,GAClD,IAAI,CASN;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAGtE;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC1B,KAAK,EAAE,uBAAuB,CAAC,OAAO,CAAC,GAAG,SAAS,GAClD,IAAI,CAUN;AAuHD;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,aAAa,EACrB,yBAAyB,EAAE,iBAAiB,EAC5C,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,6BAA6B,GAAG,SAAS,EAChD,MAAM,EAAE,QAAQ,GACf,OAAO,CAAC,OAAO,CAAC,CA2ElB"}
|
|
@@ -31,7 +31,6 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
31
31
|
cacheToolCategories: ()=>cacheToolCategories,
|
|
32
32
|
cacheToolDescriptions: ()=>cacheToolDescriptions,
|
|
33
33
|
captureToolCall: ()=>captureToolCall,
|
|
34
|
-
getVirtualToolParameterOwnership: ()=>getVirtualToolParameterOwnership,
|
|
35
34
|
handleInitializeRequest: ()=>handleInitializeRequest,
|
|
36
35
|
handleListToolsRequest: ()=>handleListToolsRequest,
|
|
37
36
|
isToolAdvertised: ()=>isToolAdvertised,
|
|
@@ -65,9 +64,9 @@ async function captureToolCall(params) {
|
|
|
65
64
|
const resolvedEventType = eventType ?? external_event_types_js_namespaceObject.MCPAnalyticsEventType.mcpToolsCall;
|
|
66
65
|
const isVirtualAnalyticsTool = resolvedEventType === external_event_types_js_namespaceObject.MCPAnalyticsEventType.mcpMissingCapability || resolvedEventType === external_event_types_js_namespaceObject.MCPAnalyticsEventType.mcpFeedback;
|
|
67
66
|
const ownership = getActiveAnalyticsParameterOwnership(data, request.params?.name, parameterOwnership, isVirtualAnalyticsTool);
|
|
68
|
-
const canCaptureContextIntent =
|
|
69
|
-
const conversation = (
|
|
70
|
-
const downstreamRequest = cloneRequestWithoutOwnedAnalyticsArguments(request, ownership);
|
|
67
|
+
const canCaptureContextIntent = ownership.read.context;
|
|
68
|
+
const conversation = resolveToolConversation(ownership.read.conversationId, request.params?.arguments, extra);
|
|
69
|
+
const downstreamRequest = cloneRequestWithoutOwnedAnalyticsArguments(request, ownership.strip);
|
|
71
70
|
const startTime = new Date();
|
|
72
71
|
const preparedEvent = await prepareToolCallEvent(server, data, request, downstreamRequest, extra, startTime, conversation, ownership, resolvedEventType, canCaptureContextIntent);
|
|
73
72
|
if (preparedEvent && explicitContextIntent) (0, external_intent_js_namespaceObject.setExplicitContextIntent)(preparedEvent.event, explicitContextIntent);
|
|
@@ -87,15 +86,39 @@ async function captureToolCall(params) {
|
|
|
87
86
|
publishSuccessfulToolEvent(server, preparedEvent, finalResult, startTime, data.logger, takeCapturedError, result);
|
|
88
87
|
return finalResult;
|
|
89
88
|
}
|
|
89
|
+
function resolveToolConversation(enabled, args, extra) {
|
|
90
|
+
const conversation = (0, external_conversation_id_js_namespaceObject.resolveConversationId)(enabled, args);
|
|
91
|
+
if (!conversation.minted) return conversation;
|
|
92
|
+
const carriedSession = extra?.sessionId || (0, external_session_token_js_namespaceObject.decodeSessionId)((0, external_session_token_js_namespaceObject.readMcpSessionHeader)((0, external_request_headers_js_namespaceObject.getRequestHeaders)(extra)));
|
|
93
|
+
return carriedSession ? {
|
|
94
|
+
minted: false,
|
|
95
|
+
conversationId: void 0
|
|
96
|
+
} : conversation;
|
|
97
|
+
}
|
|
90
98
|
function getActiveAnalyticsParameterOwnership(data, toolName, override, isVirtualAnalyticsTool) {
|
|
91
99
|
const listed = toolName ? data.toolAnalyticsParameterOwnership.get(toolName) : void 0;
|
|
92
100
|
const ownership = override ?? listed;
|
|
101
|
+
const enabled = {
|
|
102
|
+
context: !isVirtualAnalyticsTool && (0, external_context_parameters_js_namespaceObject.isContextEnabled)(data.options.context),
|
|
103
|
+
conversationId: true === data.options.enableConversationId,
|
|
104
|
+
llmModel: (0, external_model_parameters_js_namespaceObject.isCaptureModelEnabled)(data.options.captureModel)
|
|
105
|
+
};
|
|
93
106
|
return {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
107
|
+
strip: enabledArgumentOwnership(enabled, ownership, false),
|
|
108
|
+
read: enabledArgumentOwnership(enabled, ownership, true),
|
|
109
|
+
outputInstructions: enabled.conversationId && listed?.outputInstructions === true
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function enabledArgumentOwnership(enabled, ownership, whenUnknown) {
|
|
113
|
+
const resolved = ownership ?? {
|
|
114
|
+
context: whenUnknown,
|
|
115
|
+
conversationId: whenUnknown,
|
|
116
|
+
llmModel: whenUnknown
|
|
117
|
+
};
|
|
118
|
+
return {
|
|
119
|
+
context: enabled.context && resolved.context,
|
|
120
|
+
conversationId: enabled.conversationId && resolved.conversationId,
|
|
121
|
+
llmModel: enabled.llmModel && resolved.llmModel
|
|
99
122
|
};
|
|
100
123
|
}
|
|
101
124
|
function cloneRequestWithoutOwnedAnalyticsArguments(request, ownership) {
|
|
@@ -132,7 +155,7 @@ async function prepareToolCallEvent(server, data, request, downstreamRequest, ex
|
|
|
132
155
|
await (0, external_tracing_helpers_js_namespaceObject.applyResolvedMetadata)(event, data, request, extra);
|
|
133
156
|
(0, external_intent_js_namespaceObject.setEventIntent)(event, await (0, external_intent_js_namespaceObject.resolveToolCallIntent)(data, request, canCaptureContextIntent, extra));
|
|
134
157
|
if ((0, external_model_parameters_js_namespaceObject.isCaptureModelEnabled)(data.options.captureModel)) {
|
|
135
|
-
const resolvedModel = (0, external_model_parameters_js_namespaceObject.resolveModel)(request, ownership.llmModel);
|
|
158
|
+
const resolvedModel = (0, external_model_parameters_js_namespaceObject.resolveModel)(request, ownership.read.llmModel);
|
|
136
159
|
(0, external_model_parameters_js_namespaceObject.setEventModel)(event, resolvedModel?.model, resolvedModel?.source);
|
|
137
160
|
}
|
|
138
161
|
return {
|
|
@@ -296,12 +319,6 @@ function patchRequestHandlers(server, patches) {
|
|
|
296
319
|
return result;
|
|
297
320
|
};
|
|
298
321
|
}
|
|
299
|
-
function getVirtualToolParameterOwnership(data, toolName, virtualToolInputSchema) {
|
|
300
|
-
return {
|
|
301
|
-
...(0, external_analytics_parameters_js_namespaceObject.getAnalyticsParameterOwnership)(virtualToolInputSchema),
|
|
302
|
-
conversationId: data.toolAnalyticsParameterOwnership.get(toolName)?.conversationId === true
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
322
|
async function isToolAdvertised(server, toolName, extra, logger) {
|
|
306
323
|
const listHandler = originalRequestHandlers.get(server)?.get('tools/list');
|
|
307
324
|
if (!listHandler || !server._requestHandlers.has('tools/list')) return;
|
|
@@ -547,7 +564,6 @@ function readProtocolVersion(result, request) {
|
|
|
547
564
|
exports.cacheToolCategories = __webpack_exports__.cacheToolCategories;
|
|
548
565
|
exports.cacheToolDescriptions = __webpack_exports__.cacheToolDescriptions;
|
|
549
566
|
exports.captureToolCall = __webpack_exports__.captureToolCall;
|
|
550
|
-
exports.getVirtualToolParameterOwnership = __webpack_exports__.getVirtualToolParameterOwnership;
|
|
551
567
|
exports.handleInitializeRequest = __webpack_exports__.handleInitializeRequest;
|
|
552
568
|
exports.handleListToolsRequest = __webpack_exports__.handleListToolsRequest;
|
|
553
569
|
exports.isToolAdvertised = __webpack_exports__.isToolAdvertised;
|
|
@@ -559,7 +575,6 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
|
559
575
|
"cacheToolCategories",
|
|
560
576
|
"cacheToolDescriptions",
|
|
561
577
|
"captureToolCall",
|
|
562
|
-
"getVirtualToolParameterOwnership",
|
|
563
578
|
"handleInitializeRequest",
|
|
564
579
|
"handleListToolsRequest",
|
|
565
580
|
"isToolAdvertised",
|
|
@@ -14,7 +14,7 @@ import { buildCapturedMcpParameters } from "./mcp-payloads.mjs";
|
|
|
14
14
|
import { readRequestHandlerMethod } from "./mcp-sdk-compat.mjs";
|
|
15
15
|
import { getRequestHeaders } from "./request-headers.mjs";
|
|
16
16
|
import { getSessionId, getSessionInfo, isModernEraRequest, newSessionId } from "./session.mjs";
|
|
17
|
-
import { encodeSessionId, readMcpSessionHeader, writeSessionIdToTransport } from "./session-token.mjs";
|
|
17
|
+
import { decodeSessionId, encodeSessionId, readMcpSessionHeader, writeSessionIdToTransport } from "./session-token.mjs";
|
|
18
18
|
import { SEND_FEEDBACK_TOOL_NAME, getFeedbackToolDescriptor, resolveCollectFeedbackOptions } from "./feedback.mjs";
|
|
19
19
|
import { getReportMissingToolDescriptor, resolveMissingCapabilityToolName } from "./tools.mjs";
|
|
20
20
|
import { applyResolvedMetadata, isToolResultError } from "./tracing-helpers.mjs";
|
|
@@ -23,9 +23,9 @@ async function captureToolCall(params) {
|
|
|
23
23
|
const resolvedEventType = eventType ?? MCPAnalyticsEventType.mcpToolsCall;
|
|
24
24
|
const isVirtualAnalyticsTool = resolvedEventType === MCPAnalyticsEventType.mcpMissingCapability || resolvedEventType === MCPAnalyticsEventType.mcpFeedback;
|
|
25
25
|
const ownership = getActiveAnalyticsParameterOwnership(data, request.params?.name, parameterOwnership, isVirtualAnalyticsTool);
|
|
26
|
-
const canCaptureContextIntent =
|
|
27
|
-
const conversation =
|
|
28
|
-
const downstreamRequest = cloneRequestWithoutOwnedAnalyticsArguments(request, ownership);
|
|
26
|
+
const canCaptureContextIntent = ownership.read.context;
|
|
27
|
+
const conversation = resolveToolConversation(ownership.read.conversationId, request.params?.arguments, extra);
|
|
28
|
+
const downstreamRequest = cloneRequestWithoutOwnedAnalyticsArguments(request, ownership.strip);
|
|
29
29
|
const startTime = new Date();
|
|
30
30
|
const preparedEvent = await prepareToolCallEvent(server, data, request, downstreamRequest, extra, startTime, conversation, ownership, resolvedEventType, canCaptureContextIntent);
|
|
31
31
|
if (preparedEvent && explicitContextIntent) setExplicitContextIntent(preparedEvent.event, explicitContextIntent);
|
|
@@ -45,15 +45,39 @@ async function captureToolCall(params) {
|
|
|
45
45
|
publishSuccessfulToolEvent(server, preparedEvent, finalResult, startTime, data.logger, takeCapturedError, result);
|
|
46
46
|
return finalResult;
|
|
47
47
|
}
|
|
48
|
+
function resolveToolConversation(enabled, args, extra) {
|
|
49
|
+
const conversation = resolveConversationId(enabled, args);
|
|
50
|
+
if (!conversation.minted) return conversation;
|
|
51
|
+
const carriedSession = extra?.sessionId || decodeSessionId(readMcpSessionHeader(getRequestHeaders(extra)));
|
|
52
|
+
return carriedSession ? {
|
|
53
|
+
minted: false,
|
|
54
|
+
conversationId: void 0
|
|
55
|
+
} : conversation;
|
|
56
|
+
}
|
|
48
57
|
function getActiveAnalyticsParameterOwnership(data, toolName, override, isVirtualAnalyticsTool) {
|
|
49
58
|
const listed = toolName ? data.toolAnalyticsParameterOwnership.get(toolName) : void 0;
|
|
50
59
|
const ownership = override ?? listed;
|
|
60
|
+
const enabled = {
|
|
61
|
+
context: !isVirtualAnalyticsTool && isContextEnabled(data.options.context),
|
|
62
|
+
conversationId: true === data.options.enableConversationId,
|
|
63
|
+
llmModel: isCaptureModelEnabled(data.options.captureModel)
|
|
64
|
+
};
|
|
51
65
|
return {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
66
|
+
strip: enabledArgumentOwnership(enabled, ownership, false),
|
|
67
|
+
read: enabledArgumentOwnership(enabled, ownership, true),
|
|
68
|
+
outputInstructions: enabled.conversationId && listed?.outputInstructions === true
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function enabledArgumentOwnership(enabled, ownership, whenUnknown) {
|
|
72
|
+
const resolved = ownership ?? {
|
|
73
|
+
context: whenUnknown,
|
|
74
|
+
conversationId: whenUnknown,
|
|
75
|
+
llmModel: whenUnknown
|
|
76
|
+
};
|
|
77
|
+
return {
|
|
78
|
+
context: enabled.context && resolved.context,
|
|
79
|
+
conversationId: enabled.conversationId && resolved.conversationId,
|
|
80
|
+
llmModel: enabled.llmModel && resolved.llmModel
|
|
57
81
|
};
|
|
58
82
|
}
|
|
59
83
|
function cloneRequestWithoutOwnedAnalyticsArguments(request, ownership) {
|
|
@@ -90,7 +114,7 @@ async function prepareToolCallEvent(server, data, request, downstreamRequest, ex
|
|
|
90
114
|
await applyResolvedMetadata(event, data, request, extra);
|
|
91
115
|
setEventIntent(event, await resolveToolCallIntent(data, request, canCaptureContextIntent, extra));
|
|
92
116
|
if (isCaptureModelEnabled(data.options.captureModel)) {
|
|
93
|
-
const resolvedModel = resolveModel(request, ownership.llmModel);
|
|
117
|
+
const resolvedModel = resolveModel(request, ownership.read.llmModel);
|
|
94
118
|
setEventModel(event, resolvedModel?.model, resolvedModel?.source);
|
|
95
119
|
}
|
|
96
120
|
return {
|
|
@@ -254,12 +278,6 @@ function patchRequestHandlers(server, patches) {
|
|
|
254
278
|
return result;
|
|
255
279
|
};
|
|
256
280
|
}
|
|
257
|
-
function getVirtualToolParameterOwnership(data, toolName, virtualToolInputSchema) {
|
|
258
|
-
return {
|
|
259
|
-
...getAnalyticsParameterOwnership(virtualToolInputSchema),
|
|
260
|
-
conversationId: data.toolAnalyticsParameterOwnership.get(toolName)?.conversationId === true
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
281
|
async function isToolAdvertised(server, toolName, extra, logger) {
|
|
264
282
|
const listHandler = originalRequestHandlers.get(server)?.get('tools/list');
|
|
265
283
|
if (!listHandler || !server._requestHandlers.has('tools/list')) return;
|
|
@@ -502,4 +520,4 @@ function readProtocolVersion(result, request) {
|
|
|
502
520
|
const requested = request.params?.protocolVersion;
|
|
503
521
|
return 'string' == typeof requested && requested.length > 0 ? requested : void 0;
|
|
504
522
|
}
|
|
505
|
-
export { cacheToolCategories, cacheToolDescriptions, captureToolCall,
|
|
523
|
+
export { cacheToolCategories, cacheToolDescriptions, captureToolCall, handleInitializeRequest, handleListToolsRequest, isToolAdvertised, patchRequestHandlers, readToolMetaCategory, registerFallbackRequestHandler, traceResourceRequest };
|
|
@@ -24,7 +24,7 @@ export interface ModelInjectableTool {
|
|
|
24
24
|
name?: string;
|
|
25
25
|
[key: string]: unknown;
|
|
26
26
|
}
|
|
27
|
-
/**
|
|
27
|
+
/** Model capture is enabled unless explicitly disabled. */
|
|
28
28
|
export declare function isCaptureModelEnabled(captureModel: MCPAnalyticsOptions['captureModel']): boolean;
|
|
29
29
|
export declare function getModelDescription(captureModel: MCPAnalyticsOptions['captureModel']): string | undefined;
|
|
30
30
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-parameters.d.ts","sourceRoot":"","sources":["../../src/extensions/model-parameters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACtG,OAAO,EAA+B,KAAK,6BAA6B,EAAE,MAAM,wBAAwB,CAAA;AAExG,OAAO,EAAO,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAA;AAE7C;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,EAAE,6BAA6B,CAAA;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,
|
|
1
|
+
{"version":3,"file":"model-parameters.d.ts","sourceRoot":"","sources":["../../src/extensions/model-parameters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACtG,OAAO,EAA+B,KAAK,6BAA6B,EAAE,MAAM,wBAAwB,CAAA;AAExG,OAAO,EAAO,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAA;AAE7C;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,EAAE,6BAA6B,CAAA;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,2DAA2D;AAC3D,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,mBAAmB,CAAC,cAAc,CAAC,GAAG,OAAO,CAIhG;AAED,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,mBAAmB,CAAC,cAAc,CAAC,GAAG,MAAM,GAAG,SAAS,CAEzG;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,SAAS,mBAAmB,EACvE,IAAI,EAAE,KAAK,EACX,wBAAwB,CAAC,EAAE,MAAM,EACjC,MAAM,GAAE,QAAc,GACrB,KAAK,CAQP;AAED,wBAAgB,wBAAwB,CAAC,KAAK,SAAS,mBAAmB,EACxE,KAAK,EAAE,KAAK,EAAE,EACd,wBAAwB,CAAC,EAAE,MAAM,EACjC,MAAM,GAAE,QAAc,GACrB,KAAK,EAAE,CAET;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAE5E;AAID,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,uBAAuB,CAAA;CAChC;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,CAiB3G;AAiBD,wBAAgB,aAAa,CAC3B,KAAK,EAAE,QAAQ,EACf,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,MAAM,GAAE,uBAAyC,GAChD,IAAI,CAON"}
|
|
@@ -40,7 +40,7 @@ const external_analytics_parameters_js_namespaceObject = require("./analytics-pa
|
|
|
40
40
|
const external_constants_js_namespaceObject = require("./constants.js");
|
|
41
41
|
const external_logger_js_namespaceObject = require("./logger.js");
|
|
42
42
|
function isCaptureModelEnabled(captureModel) {
|
|
43
|
-
return true === captureModel || 'object' == typeof captureModel && null !== captureModel;
|
|
43
|
+
return void 0 === captureModel || true === captureModel || 'object' == typeof captureModel && null !== captureModel;
|
|
44
44
|
}
|
|
45
45
|
function getModelDescription(captureModel) {
|
|
46
46
|
return 'object' == typeof captureModel && null !== captureModel ? captureModel.description : void 0;
|
|
@@ -2,7 +2,7 @@ import { addAnalyticsParameterToTool } from "./analytics-parameters.mjs";
|
|
|
2
2
|
import { DEFAULT_MODEL_PARAMETER_DESCRIPTION } from "./constants.mjs";
|
|
3
3
|
import { log } from "./logger.mjs";
|
|
4
4
|
function isCaptureModelEnabled(captureModel) {
|
|
5
|
-
return true === captureModel || 'object' == typeof captureModel && null !== captureModel;
|
|
5
|
+
return void 0 === captureModel || true === captureModel || 'object' == typeof captureModel && null !== captureModel;
|
|
6
6
|
}
|
|
7
7
|
function getModelDescription(captureModel) {
|
|
8
8
|
return 'object' == typeof captureModel && null !== captureModel ? captureModel.description : void 0;
|
|
@@ -26,7 +26,7 @@ export interface PostHogMCPOptions extends PostHogOptions {
|
|
|
26
26
|
collectFeedback?: CollectFeedbackConfig;
|
|
27
27
|
/**
|
|
28
28
|
* Capture the calling model from recognized client metadata, with an injected
|
|
29
|
-
* `llm_model` argument as fallback.
|
|
29
|
+
* `llm_model` argument as fallback. On by default; `false` disables it.
|
|
30
30
|
*/
|
|
31
31
|
captureModel?: MCPAnalyticsOptions['captureModel'];
|
|
32
32
|
}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAQ3C,OAAO,EAAE,6BAA6B,EAAkB,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAIlG,OAAO,KAAK,EAGV,YAAY,EAEZ,mBAAmB,EAGpB,MAAM,SAAS,CAAA;AAEhB;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,iBAAS,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAE,mBAAwB,GAAG,YAAY,CA4CtG;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,iBAAS,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAKjH;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAQ3C,OAAO,EAAE,6BAA6B,EAAkB,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAIlG,OAAO,KAAK,EAGV,YAAY,EAEZ,mBAAmB,EAGpB,MAAM,SAAS,CAAA;AAEhB;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,iBAAS,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAE,mBAAwB,GAAG,YAAY,CA4CtG;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,iBAAS,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAKjH;AA4FD,OAAO,EAAE,6BAA6B,EAAE,YAAY,EAAE,CAAA;AAItD,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,KAAK,mBAAmB,GACzB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,EACxB,2BAA2B,GAC5B,MAAM,wBAAwB,CAAA;AAG/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAChE,OAAO,EAAE,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AACnF,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAI/C,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AAC3D,YAAY,EACV,mBAAmB,EACnB,2BAA2B,EAC3B,sBAAsB,EACtB,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,YAAY,EACZ,gBAAgB,EAChB,0BAA0B,EAC1B,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,mBAAmB,EACnB,4BAA4B,EAC5B,gBAAgB,EAChB,sBAAsB,EACtB,sBAAsB,EACtB,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,GACb,MAAM,SAAS,CAAA;AAChB,MAAM,MAAM,gBAAgB,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAA;AAC9D,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -106,7 +106,8 @@ function getLowLevelServer(server) {
|
|
|
106
106
|
const DEFAULT_OPTIONS = {
|
|
107
107
|
reportMissing: false,
|
|
108
108
|
collectFeedback: false,
|
|
109
|
-
enableConversationId:
|
|
109
|
+
enableConversationId: true,
|
|
110
|
+
captureModel: true
|
|
110
111
|
};
|
|
111
112
|
function buildTrackingData(lowLevelServer, options, sink, logger) {
|
|
112
113
|
return {
|
|
@@ -121,7 +122,7 @@ function buildTrackingData(lowLevelServer, options, sink, logger) {
|
|
|
121
122
|
sessionInfo: (0, session_js_namespaceObject.getSessionInfo)(lowLevelServer, void 0),
|
|
122
123
|
options: {
|
|
123
124
|
...DEFAULT_OPTIONS,
|
|
124
|
-
...options
|
|
125
|
+
...Object.fromEntries(Object.entries(options).filter(([, value])=>void 0 !== value))
|
|
125
126
|
},
|
|
126
127
|
sessionSource: 'generated'
|
|
127
128
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -52,7 +52,8 @@ function getLowLevelServer(server) {
|
|
|
52
52
|
const DEFAULT_OPTIONS = {
|
|
53
53
|
reportMissing: false,
|
|
54
54
|
collectFeedback: false,
|
|
55
|
-
enableConversationId:
|
|
55
|
+
enableConversationId: true,
|
|
56
|
+
captureModel: true
|
|
56
57
|
};
|
|
57
58
|
function buildTrackingData(lowLevelServer, options, sink, logger) {
|
|
58
59
|
return {
|
|
@@ -67,7 +68,7 @@ function buildTrackingData(lowLevelServer, options, sink, logger) {
|
|
|
67
68
|
sessionInfo: getSessionInfo(lowLevelServer, void 0),
|
|
68
69
|
options: {
|
|
69
70
|
...DEFAULT_OPTIONS,
|
|
70
|
-
...options
|
|
71
|
+
...Object.fromEntries(Object.entries(options).filter(([, value])=>void 0 !== value))
|
|
71
72
|
},
|
|
72
73
|
sessionSource: 'generated'
|
|
73
74
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -110,7 +110,7 @@ export interface MCPAnalyticsOptions {
|
|
|
110
110
|
*/
|
|
111
111
|
missingCapabilityToolName?: string;
|
|
112
112
|
/**
|
|
113
|
-
*
|
|
113
|
+
* Enable session correlation for the MCP **2026-07-28** revision, which removed
|
|
114
114
|
* protocol-level sessions: no `initialize`, no `mcp-session-id` header, and a fresh
|
|
115
115
|
* server instance per HTTP request. With none of those left to anchor on, the only
|
|
116
116
|
* thing that can carry a session across calls is the agent itself.
|
|
@@ -120,7 +120,7 @@ export interface MCPAnalyticsOptions {
|
|
|
120
120
|
* that handle — so calls correlate across reconnects, restarts, and per-request
|
|
121
121
|
* instances.
|
|
122
122
|
*
|
|
123
|
-
*
|
|
123
|
+
* On by default, and fully inert when disabled: no parameter is injected, no schema is
|
|
124
124
|
* touched, no prompt-back is appended, and `$session_id` resolves exactly as it did
|
|
125
125
|
* before (the request's own session id, else this instance's).
|
|
126
126
|
*/
|
|
@@ -136,7 +136,7 @@ export interface MCPAnalyticsOptions {
|
|
|
136
136
|
/**
|
|
137
137
|
* Capture the calling model as `$mcp_llm_model`. Recognized client metadata
|
|
138
138
|
* takes precedence, with an injected `llm_model` parameter as the fallback.
|
|
139
|
-
*
|
|
139
|
+
* On by default; set to `false` to disable capture.
|
|
140
140
|
*
|
|
141
141
|
* MCP does not standardize model identity. Some clients expose it through
|
|
142
142
|
* vendor metadata; other harnesses inject it into the agent's system prompt
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version = "0.
|
|
1
|
+
export declare const version = "0.17.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
package/dist/version.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const version = '0.
|
|
1
|
+
const version = '0.17.0';
|
|
2
2
|
export { version };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@posthog/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
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.54.
|
|
31
|
+
"@posthog/core": "^1.54.4"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@babel/preset-env": "^7.27.1",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"zod4": "npm:zod@^4.4.3",
|
|
53
53
|
"@vitest/coverage-v8": "^1.6.1",
|
|
54
54
|
"vitest": "^1.6.1",
|
|
55
|
-
"
|
|
56
|
-
"posthog-
|
|
55
|
+
"posthog-node": "^5.52.4",
|
|
56
|
+
"@posthog-tooling/tsconfig-base": "1.1.1"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"@modelcontextprotocol/sdk": ">=1.26.0",
|
|
@@ -84,6 +84,7 @@
|
|
|
84
84
|
}
|
|
85
85
|
},
|
|
86
86
|
"scripts": {
|
|
87
|
+
"check-types": "pnpm prebuild && tsc --noEmit --project tsconfig.build.json",
|
|
87
88
|
"clean": "rimraf dist coverage",
|
|
88
89
|
"lint": "oxlint --report-unused-disable-directives-severity error src && oxfmt --check harness",
|
|
89
90
|
"lint:fix": "oxlint --report-unused-disable-directives-severity error src --fix && oxfmt --write harness",
|
|
@@ -38,7 +38,6 @@ import {
|
|
|
38
38
|
traceResourceRequest,
|
|
39
39
|
patchRequestHandlers,
|
|
40
40
|
captureToolCall,
|
|
41
|
-
getVirtualToolParameterOwnership,
|
|
42
41
|
isToolAdvertised,
|
|
43
42
|
readToolMetaCategory,
|
|
44
43
|
type HandlerPatch,
|
|
@@ -260,11 +259,7 @@ async function handleToolCallRequest(
|
|
|
260
259
|
extra,
|
|
261
260
|
eventType: MCPAnalyticsEventType.mcpMissingCapability,
|
|
262
261
|
explicitContextIntent: context,
|
|
263
|
-
parameterOwnership:
|
|
264
|
-
data,
|
|
265
|
-
toolName,
|
|
266
|
-
getReportMissingToolDescriptor(toolName).inputSchema
|
|
267
|
-
),
|
|
262
|
+
parameterOwnership: getAnalyticsParameterOwnership(getReportMissingToolDescriptor(toolName).inputSchema),
|
|
268
263
|
execute: async () => handleReportMissing({ context }, data.logger),
|
|
269
264
|
})
|
|
270
265
|
}
|
|
@@ -284,11 +279,7 @@ async function handleToolCallRequest(
|
|
|
284
279
|
explicitContextIntent: buildFeedbackIntent(report),
|
|
285
280
|
omitCapturedParameters: true,
|
|
286
281
|
extraEventProperties: buildFeedbackEventProperties(report),
|
|
287
|
-
parameterOwnership:
|
|
288
|
-
data,
|
|
289
|
-
toolName,
|
|
290
|
-
getFeedbackToolDescriptor(feedbackOptions).inputSchema
|
|
291
|
-
),
|
|
282
|
+
parameterOwnership: getAnalyticsParameterOwnership(getFeedbackToolDescriptor(feedbackOptions).inputSchema),
|
|
292
283
|
execute: async () => handleFeedback(report, feedbackOptions, data.logger),
|
|
293
284
|
})
|
|
294
285
|
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
resolveCollectFeedbackOptions,
|
|
14
14
|
SEND_FEEDBACK_TOOL_NAME,
|
|
15
15
|
} from './feedback'
|
|
16
|
+
import { getAnalyticsParameterOwnership } from './analytics-parameters'
|
|
16
17
|
import { MCPAnalyticsEventType } from './event-types'
|
|
17
18
|
import { getServerTrackingData } from './internal'
|
|
18
19
|
import type { LoggerFn } from './logger'
|
|
@@ -24,7 +25,6 @@ import {
|
|
|
24
25
|
patchRequestHandlers,
|
|
25
26
|
registerFallbackRequestHandler,
|
|
26
27
|
captureToolCall,
|
|
27
|
-
getVirtualToolParameterOwnership,
|
|
28
28
|
isToolAdvertised,
|
|
29
29
|
type HandlerPatch,
|
|
30
30
|
} from './instrumentation'
|
|
@@ -107,11 +107,7 @@ async function handleToolCallRequest(
|
|
|
107
107
|
extra,
|
|
108
108
|
eventType: MCPAnalyticsEventType.mcpMissingCapability,
|
|
109
109
|
explicitContextIntent: context,
|
|
110
|
-
parameterOwnership:
|
|
111
|
-
data,
|
|
112
|
-
toolName,
|
|
113
|
-
getReportMissingToolDescriptor(toolName).inputSchema
|
|
114
|
-
),
|
|
110
|
+
parameterOwnership: getAnalyticsParameterOwnership(getReportMissingToolDescriptor(toolName).inputSchema),
|
|
115
111
|
execute: async () => handleReportMissing({ context }, data.logger),
|
|
116
112
|
})
|
|
117
113
|
}
|
|
@@ -131,11 +127,7 @@ async function handleToolCallRequest(
|
|
|
131
127
|
explicitContextIntent: buildFeedbackIntent(report),
|
|
132
128
|
omitCapturedParameters: true,
|
|
133
129
|
extraEventProperties: buildFeedbackEventProperties(report),
|
|
134
|
-
parameterOwnership:
|
|
135
|
-
data,
|
|
136
|
-
toolName,
|
|
137
|
-
getFeedbackToolDescriptor(feedbackOptions).inputSchema
|
|
138
|
-
),
|
|
130
|
+
parameterOwnership: getAnalyticsParameterOwnership(getFeedbackToolDescriptor(feedbackOptions).inputSchema),
|
|
139
131
|
execute: async () => handleFeedback(report, feedbackOptions, data.logger),
|
|
140
132
|
})
|
|
141
133
|
}
|
|
@@ -44,7 +44,7 @@ import { buildCapturedMcpParameters } from './mcp-payloads'
|
|
|
44
44
|
import { readRequestHandlerMethod } from './mcp-sdk-compat'
|
|
45
45
|
import { getRequestHeaders } from './request-headers'
|
|
46
46
|
import { getSessionId, getSessionInfo, isModernEraRequest, newSessionId } from './session'
|
|
47
|
-
import { encodeSessionId, readMcpSessionHeader, writeSessionIdToTransport } from './session-token'
|
|
47
|
+
import { decodeSessionId, encodeSessionId, readMcpSessionHeader, writeSessionIdToTransport } from './session-token'
|
|
48
48
|
import { getFeedbackToolDescriptor, resolveCollectFeedbackOptions, SEND_FEEDBACK_TOOL_NAME } from './feedback'
|
|
49
49
|
import { getReportMissingToolDescriptor, resolveMissingCapabilityToolName } from './tools'
|
|
50
50
|
import { applyResolvedMetadata, isToolResultError } from './tracing-helpers'
|
|
@@ -141,13 +141,11 @@ export async function captureToolCall(params: TraceToolCallParams): Promise<unkn
|
|
|
141
141
|
// Reading the argument and removing it are separate questions: deleting one the
|
|
142
142
|
// application declared costs the customer their call, so the strip below still
|
|
143
143
|
// requires positive ownership, while reading it when ownership is unresolved
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const conversation = resolveConversationId(ownership.conversationId, request.params?.arguments)
|
|
150
|
-
const downstreamRequest = cloneRequestWithoutOwnedAnalyticsArguments(request, ownership)
|
|
144
|
+
// can mislabel a property. Conversation minting also writes a prompt-back;
|
|
145
|
+
// that separate tradeoff and its carried-session guard are recorded in ADR-0013.
|
|
146
|
+
const canCaptureContextIntent = ownership.read.context
|
|
147
|
+
const conversation = resolveToolConversation(ownership.read.conversationId, request.params?.arguments, extra)
|
|
148
|
+
const downstreamRequest = cloneRequestWithoutOwnedAnalyticsArguments(request, ownership.strip)
|
|
151
149
|
|
|
152
150
|
// Prepare the event in isolation: if identity/metadata/intent resolution
|
|
153
151
|
// throws, we drop instrumentation for this call but still run the tool.
|
|
@@ -189,17 +187,36 @@ export async function captureToolCall(params: TraceToolCallParams): Promise<unkn
|
|
|
189
187
|
return finalResult
|
|
190
188
|
}
|
|
191
189
|
|
|
190
|
+
function resolveToolConversation(
|
|
191
|
+
enabled: boolean,
|
|
192
|
+
args: unknown,
|
|
193
|
+
extra?: CompatibleRequestHandlerExtra
|
|
194
|
+
): ConversationIdResolution {
|
|
195
|
+
const conversation = resolveConversationId(enabled, args)
|
|
196
|
+
// Echoed handles span reconnects; a new handle must not replace a session
|
|
197
|
+
// already carried by this request or add a needless prompt-back to its result.
|
|
198
|
+
if (!conversation.minted) return conversation
|
|
199
|
+
const carriedSession = extra?.sessionId || decodeSessionId(readMcpSessionHeader(getRequestHeaders(extra)))
|
|
200
|
+
return carriedSession ? { minted: false, conversationId: undefined } : conversation
|
|
201
|
+
}
|
|
202
|
+
|
|
192
203
|
interface PreparedToolEvent {
|
|
193
204
|
event: McpEvent
|
|
194
205
|
requestAttribution: SessionInfo
|
|
195
206
|
}
|
|
196
207
|
|
|
197
208
|
/**
|
|
198
|
-
* Ownership for one request
|
|
199
|
-
*
|
|
209
|
+
* Ownership for one request. The `strip` flags gate stripping and require
|
|
210
|
+
* positive ownership; `read` gates capture and additionally fails open where
|
|
211
|
+
* ownership could not be resolved — "no answer" must not read the same as "the
|
|
212
|
+
* application owns it". ADR-0011.
|
|
200
213
|
*/
|
|
201
|
-
|
|
202
|
-
|
|
214
|
+
type ArgumentOwnership = Pick<AnalyticsParameterOwnership, 'context' | 'conversationId' | 'llmModel'>
|
|
215
|
+
|
|
216
|
+
interface ActiveAnalyticsParameterOwnership {
|
|
217
|
+
strip: ArgumentOwnership
|
|
218
|
+
read: ArgumentOwnership
|
|
219
|
+
outputInstructions: boolean
|
|
203
220
|
}
|
|
204
221
|
|
|
205
222
|
function getActiveAnalyticsParameterOwnership(
|
|
@@ -210,11 +227,14 @@ function getActiveAnalyticsParameterOwnership(
|
|
|
210
227
|
): ActiveAnalyticsParameterOwnership {
|
|
211
228
|
const listed = toolName ? data.toolAnalyticsParameterOwnership.get(toolName) : undefined
|
|
212
229
|
const ownership = override ?? listed
|
|
230
|
+
const enabled = {
|
|
231
|
+
context: !isVirtualAnalyticsTool && isContextEnabled(data.options.context),
|
|
232
|
+
conversationId: data.options.enableConversationId === true,
|
|
233
|
+
llmModel: isCaptureModelEnabled(data.options.captureModel),
|
|
234
|
+
}
|
|
213
235
|
return {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
conversationId: data.options.enableConversationId === true && ownership?.conversationId === true,
|
|
217
|
-
llmModel: isCaptureModelEnabled(data.options.captureModel) && ownership?.llmModel === true,
|
|
236
|
+
strip: enabledArgumentOwnership(enabled, ownership, false),
|
|
237
|
+
read: enabledArgumentOwnership(enabled, ownership, true),
|
|
218
238
|
// Deliberately read off `listed`, never the override: only the advertised
|
|
219
239
|
// JSON Schema can say whether `tools/list` declared `_mcp_instructions` (an
|
|
220
240
|
// override is built from the live registry, which holds Zod on the
|
|
@@ -222,13 +242,26 @@ function getActiveAnalyticsParameterOwnership(
|
|
|
222
242
|
// and fails closed — writing an undeclared key fails the customer's entire
|
|
223
243
|
// tool result under `additionalProperties: false`. See ADR-0004 for the
|
|
224
244
|
// per-request-instance gap this leaves and the planned fix.
|
|
225
|
-
outputInstructions:
|
|
245
|
+
outputInstructions: enabled.conversationId && listed?.outputInstructions === true,
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function enabledArgumentOwnership(
|
|
250
|
+
enabled: ArgumentOwnership,
|
|
251
|
+
ownership: ArgumentOwnership | undefined,
|
|
252
|
+
whenUnknown: boolean
|
|
253
|
+
): ArgumentOwnership {
|
|
254
|
+
const resolved = ownership ?? { context: whenUnknown, conversationId: whenUnknown, llmModel: whenUnknown }
|
|
255
|
+
return {
|
|
256
|
+
context: enabled.context && resolved.context,
|
|
257
|
+
conversationId: enabled.conversationId && resolved.conversationId,
|
|
258
|
+
llmModel: enabled.llmModel && resolved.llmModel,
|
|
226
259
|
}
|
|
227
260
|
}
|
|
228
261
|
|
|
229
262
|
function cloneRequestWithoutOwnedAnalyticsArguments(
|
|
230
263
|
request: MCPRequestLike,
|
|
231
|
-
ownership:
|
|
264
|
+
ownership: ArgumentOwnership
|
|
232
265
|
): MCPRequestLike {
|
|
233
266
|
const args = request.params?.arguments
|
|
234
267
|
const cleanedArgs = stripOwnedAnalyticsArguments(args, ownership)
|
|
@@ -252,7 +285,7 @@ async function prepareToolCallEvent(
|
|
|
252
285
|
extra: CompatibleRequestHandlerExtra | undefined,
|
|
253
286
|
startTime: Date,
|
|
254
287
|
conversation: ConversationIdResolution,
|
|
255
|
-
ownership:
|
|
288
|
+
ownership: ActiveAnalyticsParameterOwnership,
|
|
256
289
|
eventType: MCPAnalyticsEventType,
|
|
257
290
|
canCaptureContextIntent: boolean
|
|
258
291
|
): Promise<PreparedToolEvent | null> {
|
|
@@ -300,9 +333,9 @@ async function prepareToolCallEvent(
|
|
|
300
333
|
await applyResolvedMetadata(event, data, request, extra)
|
|
301
334
|
setEventIntent(event, await resolveToolCallIntent(data, request, canCaptureContextIntent, extra))
|
|
302
335
|
// Client metadata does not collide with an application's tool arguments.
|
|
303
|
-
// Self-report
|
|
336
|
+
// Self-report reads `llm_model` under the same fail-open rule as `context`.
|
|
304
337
|
if (isCaptureModelEnabled(data.options.captureModel)) {
|
|
305
|
-
const resolvedModel = resolveModel(request, ownership.llmModel)
|
|
338
|
+
const resolvedModel = resolveModel(request, ownership.read.llmModel)
|
|
306
339
|
setEventModel(event, resolvedModel?.model, resolvedModel?.source)
|
|
307
340
|
}
|
|
308
341
|
return { event, requestAttribution }
|
|
@@ -328,7 +361,7 @@ function applyConversationInstructions(
|
|
|
328
361
|
event: McpEvent | null,
|
|
329
362
|
result: unknown,
|
|
330
363
|
conversation: ConversationIdResolution,
|
|
331
|
-
ownership: AnalyticsParameterOwnership
|
|
364
|
+
ownership: Pick<AnalyticsParameterOwnership, 'outputInstructions'>
|
|
332
365
|
): unknown {
|
|
333
366
|
const conversationId = conversation.conversationId
|
|
334
367
|
if (!conversationId) {
|
|
@@ -640,36 +673,6 @@ export function patchRequestHandlers(server: MCPServerLike, patches: Record<stri
|
|
|
640
673
|
}) as MCPServerLike['setRequestHandler']
|
|
641
674
|
}
|
|
642
675
|
|
|
643
|
-
/**
|
|
644
|
-
* Ownership for an SDK-owned virtual tool (`get_more_tools`, `send_feedback`),
|
|
645
|
-
* resolved from its own descriptor rather than the `tools/list` cache.
|
|
646
|
-
*
|
|
647
|
-
* A virtual-tool branch is only entered when the application does not advertise
|
|
648
|
-
* a tool by this name, so the descriptor is the SDK's own and what it declares
|
|
649
|
-
* is known statically. An instance that never served a listing — a per-request
|
|
650
|
-
* `McpServer`/`Server`, the topology in ADR-0011 — would otherwise read every
|
|
651
|
-
* injected parameter as not-ours and neither capture nor strip it.
|
|
652
|
-
*
|
|
653
|
-
* `conversation_id` still comes from the cache on purpose: resolving it here too
|
|
654
|
-
* would start minting a handle, and appending its prompt-back block, on
|
|
655
|
-
* instances that today mint none. That changes session anchoring (ADR-0004)
|
|
656
|
-
* rather than closing this gap.
|
|
657
|
-
*
|
|
658
|
-
* `virtualToolInputSchema` is required (not defaulted to one specific virtual
|
|
659
|
-
* tool's descriptor) so every call site names the tool it means; each caller
|
|
660
|
-
* passes its own descriptor's `inputSchema`.
|
|
661
|
-
*/
|
|
662
|
-
export function getVirtualToolParameterOwnership(
|
|
663
|
-
data: MCPAnalyticsData,
|
|
664
|
-
toolName: string,
|
|
665
|
-
virtualToolInputSchema: unknown
|
|
666
|
-
): AnalyticsParameterOwnership {
|
|
667
|
-
return {
|
|
668
|
-
...getAnalyticsParameterOwnership(virtualToolInputSchema),
|
|
669
|
-
conversationId: data.toolAnalyticsParameterOwnership.get(toolName)?.conversationId === true,
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
|
|
673
676
|
/**
|
|
674
677
|
* Checks the server's raw listing for a real owner of a candidate virtual tool.
|
|
675
678
|
* This does not depend on a previous client request and does not call the
|
|
@@ -28,9 +28,11 @@ export interface ModelInjectableTool {
|
|
|
28
28
|
[key: string]: unknown
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
/**
|
|
31
|
+
/** Model capture is enabled unless explicitly disabled. */
|
|
32
32
|
export function isCaptureModelEnabled(captureModel: MCPAnalyticsOptions['captureModel']): boolean {
|
|
33
|
-
return
|
|
33
|
+
return (
|
|
34
|
+
captureModel === undefined || captureModel === true || (typeof captureModel === 'object' && captureModel !== null)
|
|
35
|
+
)
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
export function getModelDescription(captureModel: MCPAnalyticsOptions['captureModel']): string | undefined {
|
|
@@ -71,7 +71,7 @@ export interface PostHogMCPOptions extends PostHogOptions {
|
|
|
71
71
|
collectFeedback?: CollectFeedbackConfig
|
|
72
72
|
/**
|
|
73
73
|
* Capture the calling model from recognized client metadata, with an injected
|
|
74
|
-
* `llm_model` argument as fallback.
|
|
74
|
+
* `llm_model` argument as fallback. On by default; `false` disables it.
|
|
75
75
|
*/
|
|
76
76
|
captureModel?: MCPAnalyticsOptions['captureModel']
|
|
77
77
|
}
|
package/src/index.ts
CHANGED
|
@@ -142,7 +142,8 @@ function getLowLevelServer(server: MCPServerLike | HighLevelMCPServerLike): MCPS
|
|
|
142
142
|
const DEFAULT_OPTIONS = {
|
|
143
143
|
reportMissing: false,
|
|
144
144
|
collectFeedback: false,
|
|
145
|
-
enableConversationId:
|
|
145
|
+
enableConversationId: true,
|
|
146
|
+
captureModel: true,
|
|
146
147
|
} satisfies Partial<MCPAnalyticsOptions>
|
|
147
148
|
|
|
148
149
|
function buildTrackingData(
|
|
@@ -161,7 +162,10 @@ function buildTrackingData(
|
|
|
161
162
|
toolCategories: new Map<string, string>(),
|
|
162
163
|
toolDescriptions: new Map<string, string>(),
|
|
163
164
|
sessionInfo: getSessionInfo(lowLevelServer, undefined),
|
|
164
|
-
options: {
|
|
165
|
+
options: {
|
|
166
|
+
...DEFAULT_OPTIONS,
|
|
167
|
+
...Object.fromEntries(Object.entries(options).filter(([, value]) => value !== undefined)),
|
|
168
|
+
},
|
|
165
169
|
sessionSource: 'generated',
|
|
166
170
|
}
|
|
167
171
|
}
|
package/src/types.ts
CHANGED
|
@@ -122,7 +122,7 @@ export interface MCPAnalyticsOptions {
|
|
|
122
122
|
*/
|
|
123
123
|
missingCapabilityToolName?: string
|
|
124
124
|
/**
|
|
125
|
-
*
|
|
125
|
+
* Enable session correlation for the MCP **2026-07-28** revision, which removed
|
|
126
126
|
* protocol-level sessions: no `initialize`, no `mcp-session-id` header, and a fresh
|
|
127
127
|
* server instance per HTTP request. With none of those left to anchor on, the only
|
|
128
128
|
* thing that can carry a session across calls is the agent itself.
|
|
@@ -132,7 +132,7 @@ export interface MCPAnalyticsOptions {
|
|
|
132
132
|
* that handle — so calls correlate across reconnects, restarts, and per-request
|
|
133
133
|
* instances.
|
|
134
134
|
*
|
|
135
|
-
*
|
|
135
|
+
* On by default, and fully inert when disabled: no parameter is injected, no schema is
|
|
136
136
|
* touched, no prompt-back is appended, and `$session_id` resolves exactly as it did
|
|
137
137
|
* before (the request's own session id, else this instance's).
|
|
138
138
|
*/
|
|
@@ -148,7 +148,7 @@ export interface MCPAnalyticsOptions {
|
|
|
148
148
|
/**
|
|
149
149
|
* Capture the calling model as `$mcp_llm_model`. Recognized client metadata
|
|
150
150
|
* takes precedence, with an injected `llm_model` parameter as the fallback.
|
|
151
|
-
*
|
|
151
|
+
* On by default; set to `false` to disable capture.
|
|
152
152
|
*
|
|
153
153
|
* MCP does not standardize model identity. Some clients expose it through
|
|
154
154
|
* vendor metadata; other harnesses inject it into the agent's system prompt
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '0.
|
|
1
|
+
export const version = '0.17.0'
|