@posthog/mcp 0.10.5 → 0.10.7
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/extensions/capture.d.ts +2 -2
- package/dist/extensions/capture.d.ts.map +1 -1
- package/dist/extensions/capture.js +2 -2
- package/dist/extensions/capture.mjs +2 -2
- package/dist/extensions/context-parameters.d.ts.map +1 -1
- package/dist/extensions/context-parameters.js +1 -4
- package/dist/extensions/context-parameters.mjs +1 -4
- package/dist/extensions/conversation-id.d.ts +1 -1
- package/dist/extensions/conversation-id.d.ts.map +1 -1
- package/dist/extensions/conversation-id.js +2 -2
- package/dist/extensions/conversation-id.mjs +2 -2
- package/dist/extensions/instrument-highlevel.d.ts.map +1 -1
- package/dist/extensions/instrument-highlevel.js +5 -5
- package/dist/extensions/instrument-highlevel.mjs +6 -6
- package/dist/extensions/instrument-lowlevel.d.ts.map +1 -1
- package/dist/extensions/instrument-lowlevel.js +9 -4
- package/dist/extensions/instrument-lowlevel.mjs +10 -5
- package/dist/extensions/instrumentation.d.ts +6 -6
- package/dist/extensions/instrumentation.d.ts.map +1 -1
- package/dist/extensions/instrumentation.js +112 -42
- package/dist/extensions/instrumentation.mjs +110 -43
- package/dist/extensions/intent.d.ts.map +1 -1
- package/dist/extensions/intent.js +1 -1
- package/dist/extensions/intent.mjs +1 -1
- package/dist/extensions/internal.d.ts +3 -2
- package/dist/extensions/internal.d.ts.map +1 -1
- package/dist/extensions/internal.js +25 -9
- package/dist/extensions/internal.mjs +19 -6
- package/dist/extensions/session.js +3 -1
- package/dist/extensions/session.mjs +3 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.mjs +1 -1
- package/package.json +2 -2
- package/src/extensions/capture.ts +13 -6
- package/src/extensions/context-parameters.ts +1 -7
- package/src/extensions/conversation-id.ts +2 -1
- package/src/extensions/instrument-highlevel.ts +8 -13
- package/src/extensions/instrument-lowlevel.ts +17 -6
- package/src/extensions/instrumentation.ts +166 -51
- package/src/extensions/intent.ts +1 -6
- package/src/extensions/internal.ts +21 -6
- package/src/extensions/session.ts +1 -1
- package/src/version.ts +1 -1
|
@@ -23,7 +23,7 @@ async function runIntentFallback(data, request, extra) {
|
|
|
23
23
|
}
|
|
24
24
|
async function resolveToolCallIntent(data, request, analyticsOwnsContext, extra) {
|
|
25
25
|
const contextArgument = getContextArgument(request);
|
|
26
|
-
if (analyticsOwnsContext && isContextEnabled(data.options.context) &&
|
|
26
|
+
if (analyticsOwnsContext && isContextEnabled(data.options.context) && contextArgument) return {
|
|
27
27
|
intent: contextArgument,
|
|
28
28
|
source: 'context_parameter'
|
|
29
29
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CompatibleRequestHandlerExtra, MCPAnalyticsData, MCPRequestLike, MCPServerLike, UserIdentity } from '../types';
|
|
1
|
+
import type { CompatibleRequestHandlerExtra, MCPAnalyticsData, MCPRequestLike, MCPServerLike, SessionInfo, UserIdentity } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* Bounded LRU cache for session identities, capped at `maxSize` entries so a
|
|
4
4
|
* long-lived server can't accumulate identities for unboundedly many sessions.
|
|
@@ -24,7 +24,8 @@ export declare function mergeIdentities(previous: UserIdentity | undefined, next
|
|
|
24
24
|
* `$identify` event at most once per session: at `initialize`, when a
|
|
25
25
|
* long-lived server first sees the identity, or when it materially changes.
|
|
26
26
|
*/
|
|
27
|
-
export declare function handleIdentify(server: MCPServerLike, data: MCPAnalyticsData, sessionId: string, request: MCPRequestLike, extra?: CompatibleRequestHandlerExtra): Promise<
|
|
27
|
+
export declare function handleIdentify(server: MCPServerLike, data: MCPAnalyticsData, sessionId: string, request: MCPRequestLike, requestAttribution: SessionInfo, extra?: CompatibleRequestHandlerExtra): Promise<UserIdentity | undefined>;
|
|
28
|
+
export declare function withIdentity(sessionInfo: SessionInfo, identity: UserIdentity | undefined): SessionInfo;
|
|
28
29
|
/**
|
|
29
30
|
* Resolves the `eventProperties` callback. Returns null when no callback is
|
|
30
31
|
* configured, or when it returns nullish or throws — a throw never propagates
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../../src/extensions/internal.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,6BAA6B,EAC7B,gBAAgB,EAChB,cAAc,EACd,aAAa,EAEb,YAAY,EACb,MAAM,UAAU,CAAA;AAKjB;;;;;GAKG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkC;IACzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;gBAErB,OAAO,SAAO;IAI1B,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAWhD,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,IAAI;IAapD,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAI/B,IAAI,IAAI,MAAM;CAGf;AAID,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,GAAG,gBAAgB,GAAG,SAAS,CAEzF;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAEzF;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,OAAO,CA4B5E;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,GAAG,SAAS,EAAE,IAAI,EAAE,YAAY,GAAG,YAAY,CAapG;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,gBAAgB,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,cAAc,EACvB,KAAK,CAAC,EAAE,6BAA6B,GACpC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../../src/extensions/internal.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,6BAA6B,EAC7B,gBAAgB,EAChB,cAAc,EACd,aAAa,EAEb,WAAW,EACX,YAAY,EACb,MAAM,UAAU,CAAA;AAKjB;;;;;GAKG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkC;IACzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;gBAErB,OAAO,SAAO;IAI1B,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAWhD,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,IAAI;IAapD,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAI/B,IAAI,IAAI,MAAM;CAGf;AAID,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,GAAG,gBAAgB,GAAG,SAAS,CAEzF;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAEzF;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,OAAO,CA4B5E;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,GAAG,SAAS,EAAE,IAAI,EAAE,YAAY,GAAG,YAAY,CAapG;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,gBAAgB,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,cAAc,EACvB,kBAAkB,EAAE,WAAW,EAC/B,KAAK,CAAC,EAAE,6BAA6B,GACpC,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAsDnC;AAED,wBAAgB,YAAY,CAAC,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,GAAG,SAAS,GAAG,WAAW,CAOtG;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,cAAc,EACvB,KAAK,CAAC,EAAE,6BAA6B,GACpC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAUzC"}
|
|
@@ -26,11 +26,12 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
26
26
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
27
|
mergeIdentities: ()=>mergeIdentities,
|
|
28
28
|
areIdentitiesEqual: ()=>areIdentitiesEqual,
|
|
29
|
+
getServerTrackingData: ()=>getServerTrackingData,
|
|
30
|
+
setServerTrackingData: ()=>setServerTrackingData,
|
|
29
31
|
resolveEventProperties: ()=>resolveEventProperties,
|
|
32
|
+
withIdentity: ()=>withIdentity,
|
|
30
33
|
handleIdentify: ()=>handleIdentify,
|
|
31
|
-
|
|
32
|
-
IdentityCache: ()=>IdentityCache,
|
|
33
|
-
setServerTrackingData: ()=>setServerTrackingData
|
|
34
|
+
IdentityCache: ()=>IdentityCache
|
|
34
35
|
});
|
|
35
36
|
const external_event_types_js_namespaceObject = require("./event-types.js");
|
|
36
37
|
const external_capture_js_namespaceObject = require("./capture.js");
|
|
@@ -94,8 +95,10 @@ function mergeIdentities(previous, next) {
|
|
|
94
95
|
groups: next.groups ?? previous.groups
|
|
95
96
|
};
|
|
96
97
|
}
|
|
97
|
-
async function handleIdentify(server, data, sessionId, request, extra) {
|
|
98
|
-
|
|
98
|
+
async function handleIdentify(server, data, sessionId, request, requestAttribution, extra) {
|
|
99
|
+
const identityBeforeRequest = data.identifiedSessions.get(sessionId);
|
|
100
|
+
const sessionSourceBeforeIdentify = data.sessionSource;
|
|
101
|
+
if (!data.options.identify) return identityBeforeRequest;
|
|
99
102
|
const identifyEvent = {
|
|
100
103
|
sessionId,
|
|
101
104
|
resourceName: getRequestResourceName(request),
|
|
@@ -114,17 +117,28 @@ async function handleIdentify(server, data, sessionId, request, extra) {
|
|
|
114
117
|
const mergedIdentity = mergeIdentities(previousIdentity, identityResult);
|
|
115
118
|
const changed = void 0 !== previousIdentity && !areIdentitiesEqual(previousIdentity, mergedIdentity);
|
|
116
119
|
const firstSeen = void 0 === previousIdentity;
|
|
117
|
-
const announcedAtInitialize = 'token' ===
|
|
120
|
+
const announcedAtInitialize = 'token' === sessionSourceBeforeIdentify && 'initialize' !== request.method;
|
|
118
121
|
const shouldPublish = changed || firstSeen && !announcedAtInitialize;
|
|
119
122
|
data.identifiedSessions.set(sessionId, mergedIdentity);
|
|
120
123
|
if (shouldPublish) {
|
|
121
124
|
data.logger(`Identified session ${sessionId}`);
|
|
122
|
-
(0, external_capture_js_namespaceObject.captureEvent)(server, identifyEvent, data.logger);
|
|
125
|
+
(0, external_capture_js_namespaceObject.captureEvent)(server, identifyEvent, data.logger, withIdentity(requestAttribution, mergedIdentity));
|
|
123
126
|
}
|
|
124
|
-
|
|
127
|
+
return mergedIdentity;
|
|
128
|
+
}
|
|
129
|
+
data.logger(`Warning: Supplied identify function returned null for session ${sessionId}`);
|
|
125
130
|
} catch (error) {
|
|
126
131
|
data.logger(`Error: User supplied identify function threw an error while identifying session ${sessionId} - ${error}`);
|
|
127
132
|
}
|
|
133
|
+
return identityBeforeRequest;
|
|
134
|
+
}
|
|
135
|
+
function withIdentity(sessionInfo, identity) {
|
|
136
|
+
return {
|
|
137
|
+
...sessionInfo,
|
|
138
|
+
identifyActorGivenId: identity?.distinctId,
|
|
139
|
+
identifyActorData: identity?.properties || {},
|
|
140
|
+
identifyActorGroups: identity?.groups
|
|
141
|
+
};
|
|
128
142
|
}
|
|
129
143
|
async function resolveEventProperties(data, request, extra) {
|
|
130
144
|
if (!data.options.eventProperties) return null;
|
|
@@ -148,6 +162,7 @@ exports.handleIdentify = __webpack_exports__.handleIdentify;
|
|
|
148
162
|
exports.mergeIdentities = __webpack_exports__.mergeIdentities;
|
|
149
163
|
exports.resolveEventProperties = __webpack_exports__.resolveEventProperties;
|
|
150
164
|
exports.setServerTrackingData = __webpack_exports__.setServerTrackingData;
|
|
165
|
+
exports.withIdentity = __webpack_exports__.withIdentity;
|
|
151
166
|
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
152
167
|
"IdentityCache",
|
|
153
168
|
"areIdentitiesEqual",
|
|
@@ -155,7 +170,8 @@ for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
|
155
170
|
"handleIdentify",
|
|
156
171
|
"mergeIdentities",
|
|
157
172
|
"resolveEventProperties",
|
|
158
|
-
"setServerTrackingData"
|
|
173
|
+
"setServerTrackingData",
|
|
174
|
+
"withIdentity"
|
|
159
175
|
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
160
176
|
Object.defineProperty(exports, '__esModule', {
|
|
161
177
|
value: true
|
|
@@ -60,8 +60,10 @@ function mergeIdentities(previous, next) {
|
|
|
60
60
|
groups: next.groups ?? previous.groups
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
|
-
async function handleIdentify(server, data, sessionId, request, extra) {
|
|
64
|
-
|
|
63
|
+
async function handleIdentify(server, data, sessionId, request, requestAttribution, extra) {
|
|
64
|
+
const identityBeforeRequest = data.identifiedSessions.get(sessionId);
|
|
65
|
+
const sessionSourceBeforeIdentify = data.sessionSource;
|
|
66
|
+
if (!data.options.identify) return identityBeforeRequest;
|
|
65
67
|
const identifyEvent = {
|
|
66
68
|
sessionId,
|
|
67
69
|
resourceName: getRequestResourceName(request),
|
|
@@ -80,17 +82,28 @@ async function handleIdentify(server, data, sessionId, request, extra) {
|
|
|
80
82
|
const mergedIdentity = mergeIdentities(previousIdentity, identityResult);
|
|
81
83
|
const changed = void 0 !== previousIdentity && !areIdentitiesEqual(previousIdentity, mergedIdentity);
|
|
82
84
|
const firstSeen = void 0 === previousIdentity;
|
|
83
|
-
const announcedAtInitialize = 'token' ===
|
|
85
|
+
const announcedAtInitialize = 'token' === sessionSourceBeforeIdentify && 'initialize' !== request.method;
|
|
84
86
|
const shouldPublish = changed || firstSeen && !announcedAtInitialize;
|
|
85
87
|
data.identifiedSessions.set(sessionId, mergedIdentity);
|
|
86
88
|
if (shouldPublish) {
|
|
87
89
|
data.logger(`Identified session ${sessionId}`);
|
|
88
|
-
captureEvent(server, identifyEvent, data.logger);
|
|
90
|
+
captureEvent(server, identifyEvent, data.logger, withIdentity(requestAttribution, mergedIdentity));
|
|
89
91
|
}
|
|
90
|
-
|
|
92
|
+
return mergedIdentity;
|
|
93
|
+
}
|
|
94
|
+
data.logger(`Warning: Supplied identify function returned null for session ${sessionId}`);
|
|
91
95
|
} catch (error) {
|
|
92
96
|
data.logger(`Error: User supplied identify function threw an error while identifying session ${sessionId} - ${error}`);
|
|
93
97
|
}
|
|
98
|
+
return identityBeforeRequest;
|
|
99
|
+
}
|
|
100
|
+
function withIdentity(sessionInfo, identity) {
|
|
101
|
+
return {
|
|
102
|
+
...sessionInfo,
|
|
103
|
+
identifyActorGivenId: identity?.distinctId,
|
|
104
|
+
identifyActorData: identity?.properties || {},
|
|
105
|
+
identifyActorGroups: identity?.groups
|
|
106
|
+
};
|
|
94
107
|
}
|
|
95
108
|
async function resolveEventProperties(data, request, extra) {
|
|
96
109
|
if (!data.options.eventProperties) return null;
|
|
@@ -107,4 +120,4 @@ function getRequestResourceName(request) {
|
|
|
107
120
|
if (!params || 'object' != typeof params || !('name' in params)) return 'Unknown';
|
|
108
121
|
return 'string' == typeof params.name ? params.name : 'Unknown';
|
|
109
122
|
}
|
|
110
|
-
export { IdentityCache, areIdentitiesEqual, getServerTrackingData, handleIdentify, mergeIdentities, resolveEventProperties, setServerTrackingData };
|
|
123
|
+
export { IdentityCache, areIdentitiesEqual, getServerTrackingData, handleIdentify, mergeIdentities, resolveEventProperties, setServerTrackingData, withIdentity };
|
|
@@ -90,7 +90,9 @@ function getSessionInfo(server, data, sessionId) {
|
|
|
90
90
|
if (!data) return sessionInfo;
|
|
91
91
|
data.sessionInfo = sessionInfo;
|
|
92
92
|
(0, external_internal_js_namespaceObject.setServerTrackingData)(server, data);
|
|
93
|
-
return
|
|
93
|
+
return {
|
|
94
|
+
...sessionInfo
|
|
95
|
+
};
|
|
94
96
|
}
|
|
95
97
|
exports.deriveSessionIdFromMCPSession = __webpack_exports__.deriveSessionIdFromMCPSession;
|
|
96
98
|
exports.getSessionId = __webpack_exports__.getSessionId;
|
|
@@ -59,6 +59,8 @@ function getSessionInfo(server, data, sessionId) {
|
|
|
59
59
|
if (!data) return sessionInfo;
|
|
60
60
|
data.sessionInfo = sessionInfo;
|
|
61
61
|
setServerTrackingData(server, data);
|
|
62
|
-
return
|
|
62
|
+
return {
|
|
63
|
+
...sessionInfo
|
|
64
|
+
};
|
|
63
65
|
}
|
|
64
66
|
export { deriveSessionIdFromMCPSession, getSessionId, getSessionInfo, newSessionId };
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version = "0.10.
|
|
1
|
+
export declare const version = "0.10.7";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
|
@@ -26,7 +26,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
26
26
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
27
|
version: ()=>version
|
|
28
28
|
});
|
|
29
|
-
const version = '0.10.
|
|
29
|
+
const version = '0.10.7';
|
|
30
30
|
exports.version = __webpack_exports__.version;
|
|
31
31
|
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
32
32
|
"version"
|
package/dist/version.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const version = '0.10.
|
|
1
|
+
const version = '0.10.7';
|
|
2
2
|
export { version };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@posthog/mcp",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.7",
|
|
4
4
|
"bugs": {
|
|
5
5
|
"url": "https://github.com/PostHog/posthog-js/issues"
|
|
6
6
|
},
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"jest": "29.7.0",
|
|
41
41
|
"zod": "^3.25.0",
|
|
42
42
|
"@posthog-tooling/tsconfig-base": "1.1.1",
|
|
43
|
-
"posthog-node": "^5.47.
|
|
43
|
+
"posthog-node": "^5.47.9"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"@modelcontextprotocol/sdk": ">=1.26.0",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Copyright (c) 2025 AgentCat, Inc. (formerly MCPcat)
|
|
4
4
|
// Licensed under the MIT License: https://github.com/agentcathq/agentcat-typescript-sdk/blob/main/LICENSE
|
|
5
5
|
|
|
6
|
-
import type { MCPServerLike, McpEvent } from '../types'
|
|
6
|
+
import type { MCPServerLike, McpEvent, SessionInfo } from '../types'
|
|
7
7
|
import { MCPAnalyticsEventType } from './event-types'
|
|
8
8
|
import { getServerTrackingData } from './internal'
|
|
9
9
|
import type { LoggerFn } from './logger'
|
|
@@ -19,7 +19,12 @@ import { getSessionInfo } from './session'
|
|
|
19
19
|
* Auto-capture callers (tool calls, listings, identify) intentionally ignore the
|
|
20
20
|
* return value, keeping the tool path isolated from analytics latency/errors.
|
|
21
21
|
*/
|
|
22
|
-
export function captureEvent(
|
|
22
|
+
export function captureEvent(
|
|
23
|
+
server: MCPServerLike,
|
|
24
|
+
eventInput: McpEvent,
|
|
25
|
+
logger: LoggerFn,
|
|
26
|
+
requestAttribution?: SessionInfo
|
|
27
|
+
): Promise<void> | undefined {
|
|
23
28
|
const data = getServerTrackingData(server)
|
|
24
29
|
if (!data) {
|
|
25
30
|
logger('Warning: Server tracking data not found. Event will not be published.')
|
|
@@ -31,7 +36,10 @@ export function captureEvent(server: MCPServerLike, eventInput: McpEvent, logger
|
|
|
31
36
|
return
|
|
32
37
|
}
|
|
33
38
|
|
|
34
|
-
|
|
39
|
+
// Instrumented request paths pass an immutable snapshot resolved before any
|
|
40
|
+
// user callback or handler can yield to a concurrent request. Custom capture
|
|
41
|
+
// has no request context, so it intentionally falls back to current state.
|
|
42
|
+
const sessionInfo = requestAttribution ?? getSessionInfo(server, data, eventInput.sessionId)
|
|
35
43
|
|
|
36
44
|
const duration =
|
|
37
45
|
eventInput.duration || (eventInput.timestamp ? Date.now() - eventInput.timestamp.getTime() : undefined)
|
|
@@ -48,9 +56,8 @@ export function captureEvent(server: MCPServerLike, eventInput: McpEvent, logger
|
|
|
48
56
|
sdkVersion: sessionInfo.sdkVersion,
|
|
49
57
|
serverName: sessionInfo.serverName,
|
|
50
58
|
serverVersion: sessionInfo.serverVersion,
|
|
51
|
-
// Prefer
|
|
52
|
-
//
|
|
53
|
-
// request from another client can't misattribute this one.
|
|
59
|
+
// Prefer client metadata stamped onto this event from the request's `_meta`
|
|
60
|
+
// over sessionInfo, so concurrent stateless requests stay isolated.
|
|
54
61
|
clientName: eventInput.clientName ?? sessionInfo.clientName,
|
|
55
62
|
clientVersion: eventInput.clientVersion ?? sessionInfo.clientVersion,
|
|
56
63
|
identifyActorGivenId: sessionInfo.identifyActorGivenId,
|
|
@@ -108,11 +108,5 @@ export function addContextParameterToTools<TTool extends ContextInjectableTool>(
|
|
|
108
108
|
contextDescriptionOverride?: string,
|
|
109
109
|
logger: LoggerFn = log
|
|
110
110
|
): TTool[] {
|
|
111
|
-
return tools.map((tool) =>
|
|
112
|
-
// Skip get_more_tools - it has its own special context parameter
|
|
113
|
-
if (tool.name === 'get_more_tools') {
|
|
114
|
-
return tool
|
|
115
|
-
}
|
|
116
|
-
return addContextParameterToTool(tool, contextDescriptionOverride, logger)
|
|
117
|
-
})
|
|
111
|
+
return tools.map((tool) => addContextParameterToTool(tool, contextDescriptionOverride, logger))
|
|
118
112
|
}
|
|
@@ -73,10 +73,11 @@ export function addConversationIdToTool<TTool extends ConversationIdInjectableTo
|
|
|
73
73
|
export function addConversationIdToTools<TTool extends ConversationIdInjectableTool>(
|
|
74
74
|
tools: TTool[],
|
|
75
75
|
missingCapabilityToolName: string = GET_MORE_TOOLS_NAME,
|
|
76
|
+
skipMissingCapabilityTool = true,
|
|
76
77
|
logger: LoggerFn = log
|
|
77
78
|
): TTool[] {
|
|
78
79
|
return tools.map((tool) => {
|
|
79
|
-
if (tool.name === missingCapabilityToolName) {
|
|
80
|
+
if (skipMissingCapabilityTool && tool.name === missingCapabilityToolName) {
|
|
80
81
|
return tool
|
|
81
82
|
}
|
|
82
83
|
return addConversationIdToTool(tool, logger)
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
handleListToolsRequest,
|
|
28
28
|
patchRequestHandlers,
|
|
29
29
|
captureToolCall,
|
|
30
|
+
isToolAdvertised,
|
|
30
31
|
readToolMetaCategory,
|
|
31
32
|
type HandlerPatch,
|
|
32
33
|
} from './instrumentation'
|
|
@@ -162,8 +163,6 @@ function addTracingToToolCallbackInternal(
|
|
|
162
163
|
): RegisteredTool {
|
|
163
164
|
const originalCallback = getToolFunction(tool)
|
|
164
165
|
const options = getServerTrackingData(server.server as MCPServerLike)?.options
|
|
165
|
-
const missingToolName = resolveMissingCapabilityToolName(options)
|
|
166
|
-
|
|
167
166
|
if (wrappedCallbacks.has(originalCallback)) {
|
|
168
167
|
logger(`Tool ${toolName} callback already wrapped, skipping re-wrap`)
|
|
169
168
|
return tool
|
|
@@ -188,16 +187,9 @@ function addTracingToToolCallbackInternal(
|
|
|
188
187
|
|
|
189
188
|
const inputSchema = getCurrentInputSchema()
|
|
190
189
|
const cleanedArgs = stripOwnedAnalyticsArguments(args, {
|
|
191
|
-
context:
|
|
192
|
-
|
|
193
|
-
isContextEnabled(options?.context) &&
|
|
194
|
-
analyticsOwnsParameter(inputSchema, 'context'),
|
|
195
|
-
conversationId:
|
|
196
|
-
toolName !== missingToolName &&
|
|
197
|
-
options?.enableConversationId === true &&
|
|
198
|
-
analyticsOwnsParameter(inputSchema, 'conversation_id'),
|
|
190
|
+
context: isContextEnabled(options?.context) && analyticsOwnsParameter(inputSchema, 'context'),
|
|
191
|
+
conversationId: options?.enableConversationId === true && analyticsOwnsParameter(inputSchema, 'conversation_id'),
|
|
199
192
|
})
|
|
200
|
-
|
|
201
193
|
try {
|
|
202
194
|
if (cleanedArgs === undefined) {
|
|
203
195
|
const handler = originalCallback as (extra: CompatibleRequestHandlerExtra) => Promise<CallToolResult>
|
|
@@ -242,7 +234,11 @@ async function handleToolCallRequest(
|
|
|
242
234
|
return await originalCallToolHandler(request, extra)
|
|
243
235
|
}
|
|
244
236
|
|
|
245
|
-
|
|
237
|
+
const toolName = request.params?.name
|
|
238
|
+
const isMissingCapabilityCandidate =
|
|
239
|
+
data.options.reportMissing && toolName === resolveMissingCapabilityToolName(data.options)
|
|
240
|
+
|
|
241
|
+
if (isMissingCapabilityCandidate && (await isToolAdvertised(server, toolName, extra, data.logger)) === false) {
|
|
246
242
|
const context = getContextArgument(request) || ''
|
|
247
243
|
return await captureToolCall({
|
|
248
244
|
server,
|
|
@@ -255,7 +251,6 @@ async function handleToolCallRequest(
|
|
|
255
251
|
})
|
|
256
252
|
}
|
|
257
253
|
|
|
258
|
-
const toolName = request.params?.name
|
|
259
254
|
const registeredTool = toolName ? highLevelServer._registeredTools[toolName] : undefined
|
|
260
255
|
|
|
261
256
|
// Strip SDK-owned arguments before the MCP SDK validates the registered Zod
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
handleListToolsRequest,
|
|
15
15
|
patchRequestHandlers,
|
|
16
16
|
captureToolCall,
|
|
17
|
+
isToolAdvertised,
|
|
17
18
|
type HandlerPatch,
|
|
18
19
|
} from './instrumentation'
|
|
19
20
|
import { getContextArgument } from './tracing-helpers'
|
|
@@ -32,19 +33,25 @@ type MCPRequestExtra = Parameters<MCPRequestHandler>[1]
|
|
|
32
33
|
export function instrumentLowLevelServer(server: MCPServerLike, logger: LoggerFn): void {
|
|
33
34
|
try {
|
|
34
35
|
// Patch already existing handlers, and patch setRequestHandler to capture dynamically created handlers.
|
|
36
|
+
const hadCallToolHandler = server._requestHandlers.has('tools/call')
|
|
35
37
|
const handlers: Record<string, HandlerPatch> = {
|
|
36
38
|
initialize: (server, originalHandler, request, extra) =>
|
|
37
39
|
handleInitializeRequest(server, originalHandler, request, extra, logger),
|
|
38
40
|
'tools/list': (server, originalHandler, request, extra) =>
|
|
39
41
|
handleListToolsRequest(server, originalHandler, request, extra, logger),
|
|
42
|
+
'tools/call': (server, originalHandler, request, extra) =>
|
|
43
|
+
handleToolCallRequest(server, originalHandler, request, extra, logger),
|
|
40
44
|
}
|
|
41
45
|
patchRequestHandlers(server, handlers)
|
|
42
46
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
if (!hadCallToolHandler) {
|
|
48
|
+
// Register a raw fallback through the patched setter so reportMissing works
|
|
49
|
+
// even before an application dispatcher is attached. A later registration
|
|
50
|
+
// replaces it and is wrapped by patchRequestHandlers.
|
|
51
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
52
|
+
throw new Error(`Unknown tool: ${request.params?.name || 'unknown'}`)
|
|
53
|
+
})
|
|
54
|
+
}
|
|
48
55
|
} catch (error) {
|
|
49
56
|
logger(`Warning: Failed to setup tool call instrumentation - ${error}`)
|
|
50
57
|
throw error
|
|
@@ -66,7 +73,11 @@ async function handleToolCallRequest(
|
|
|
66
73
|
return await originalCallToolHandler?.(request, extra)
|
|
67
74
|
}
|
|
68
75
|
|
|
69
|
-
|
|
76
|
+
const toolName = request.params?.name
|
|
77
|
+
const isMissingCapabilityCandidate =
|
|
78
|
+
data.options.reportMissing && toolName === resolveMissingCapabilityToolName(data.options)
|
|
79
|
+
|
|
80
|
+
if (isMissingCapabilityCandidate && (await isToolAdvertised(server, toolName, extra, data.logger)) === false) {
|
|
70
81
|
const context = getContextArgument(request) || ''
|
|
71
82
|
return await captureToolCall({
|
|
72
83
|
server,
|