@posthog/mcp 0.10.6 → 0.10.8
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/analytics-parameters.d.ts +7 -2
- package/dist/extensions/analytics-parameters.d.ts.map +1 -1
- package/dist/extensions/analytics-parameters.js +4 -2
- package/dist/extensions/analytics-parameters.mjs +4 -2
- 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/conversation-id.d.ts +37 -8
- package/dist/extensions/conversation-id.d.ts.map +1 -1
- package/dist/extensions/conversation-id.js +8 -13
- package/dist/extensions/conversation-id.mjs +8 -13
- package/dist/extensions/instrument-highlevel.d.ts.map +1 -1
- package/dist/extensions/instrument-highlevel.js +1 -1
- package/dist/extensions/instrument-highlevel.mjs +1 -1
- package/dist/extensions/instrumentation.d.ts.map +1 -1
- package/dist/extensions/instrumentation.js +86 -42
- package/dist/extensions/instrumentation.mjs +88 -44
- package/dist/extensions/internal.d.ts +10 -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/output-instructions.d.ts +70 -0
- package/dist/extensions/output-instructions.d.ts.map +1 -0
- package/dist/extensions/output-instructions.js +131 -0
- package/dist/extensions/output-instructions.mjs +82 -0
- package/dist/extensions/session.d.ts +26 -5
- package/dist/extensions/session.d.ts.map +1 -1
- package/dist/extensions/session.js +35 -15
- package/dist/extensions/session.mjs +33 -16
- package/dist/types.d.ts +23 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.mjs +1 -1
- package/package.json +3 -3
- package/src/extensions/analytics-parameters.ts +15 -2
- package/src/extensions/capture.ts +13 -6
- package/src/extensions/conversation-id.ts +51 -29
- package/src/extensions/instrument-highlevel.ts +3 -1
- package/src/extensions/instrumentation.ts +151 -60
- package/src/extensions/internal.ts +30 -7
- package/src/extensions/output-instructions.ts +231 -0
- package/src/extensions/session.ts +128 -29
- package/src/types.ts +23 -1
- package/src/version.ts +1 -1
|
@@ -2,14 +2,15 @@ import { getAnalyticsParameterOwnership, stripOwnedAnalyticsArguments } from "./
|
|
|
2
2
|
import { addContextParameterToTools, getContextDescription, isContextEnabled } from "./context-parameters.mjs";
|
|
3
3
|
import { addConversationIdToTools, canInjectConversationIdPromptBack, injectConversationIdPromptBack, resolveConversationId } from "./conversation-id.mjs";
|
|
4
4
|
import { stampMetaClientInfo } from "./client-identity.mjs";
|
|
5
|
+
import { addInstructionsToOutputSchemas, mirrorInstructionsIntoStructuredContent } from "./output-instructions.mjs";
|
|
5
6
|
import { captureEvent } from "./capture.mjs";
|
|
6
7
|
import { MCPAnalyticsEventType } from "./event-types.mjs";
|
|
7
8
|
import { captureException } from "./exceptions.mjs";
|
|
8
9
|
import { resolveToolCallIntent, setEventIntent, setExplicitContextIntent } from "./intent.mjs";
|
|
9
|
-
import { getServerTrackingData, handleIdentify, setServerTrackingData } from "./internal.mjs";
|
|
10
|
+
import { getServerTrackingData, handleIdentify, setServerTrackingData, withIdentity } from "./internal.mjs";
|
|
10
11
|
import { buildCapturedMcpParameters } from "./mcp-payloads.mjs";
|
|
11
12
|
import { getLiteralValue, getObjectShape } from "./mcp-sdk-compat.mjs";
|
|
12
|
-
import { getSessionId, newSessionId } from "./session.mjs";
|
|
13
|
+
import { getSessionId, getSessionInfo, newSessionId } from "./session.mjs";
|
|
13
14
|
import { encodeSessionId, readMcpSessionHeader, writeSessionIdToTransport } from "./session-token.mjs";
|
|
14
15
|
import { getReportMissingToolDescriptor, resolveMissingCapabilityToolName } from "./tools.mjs";
|
|
15
16
|
import { applyResolvedMetadata, isToolResultError } from "./tracing-helpers.mjs";
|
|
@@ -17,27 +18,29 @@ async function captureToolCall(params) {
|
|
|
17
18
|
const { server, data, request, extra, execute, parameterOwnership, eventType, explicitContextIntent, takeCapturedError } = params;
|
|
18
19
|
const resolvedEventType = eventType ?? MCPAnalyticsEventType.mcpToolsCall;
|
|
19
20
|
const ownership = getActiveAnalyticsParameterOwnership(data, request.params?.name, parameterOwnership, resolvedEventType === MCPAnalyticsEventType.mcpMissingCapability);
|
|
20
|
-
const conversation = resolveConversationId(ownership.conversationId, request.params?.arguments
|
|
21
|
+
const conversation = resolveConversationId(ownership.conversationId, request.params?.arguments);
|
|
21
22
|
const downstreamRequest = cloneRequestWithoutOwnedAnalyticsArguments(request, ownership);
|
|
22
23
|
const startTime = new Date();
|
|
23
|
-
const
|
|
24
|
-
if (
|
|
24
|
+
const preparedEvent = await prepareToolCallEvent(server, data, request, downstreamRequest, extra, startTime, conversation, ownership, resolvedEventType);
|
|
25
|
+
if (preparedEvent && explicitContextIntent) setExplicitContextIntent(preparedEvent.event, explicitContextIntent);
|
|
25
26
|
let result;
|
|
26
27
|
try {
|
|
27
28
|
result = await execute(downstreamRequest);
|
|
28
29
|
} catch (error) {
|
|
29
|
-
publishFailedToolEvent(server,
|
|
30
|
+
publishFailedToolEvent(server, preparedEvent, error, startTime, conversation, data.logger);
|
|
30
31
|
throw error;
|
|
31
32
|
}
|
|
32
|
-
const finalResult =
|
|
33
|
-
publishSuccessfulToolEvent(server,
|
|
33
|
+
const finalResult = applyConversationInstructions(preparedEvent?.event ?? null, result, conversation, ownership);
|
|
34
|
+
publishSuccessfulToolEvent(server, preparedEvent, finalResult, startTime, data.logger, takeCapturedError);
|
|
34
35
|
return finalResult;
|
|
35
36
|
}
|
|
36
37
|
function getActiveAnalyticsParameterOwnership(data, toolName, override, isMissingCapabilityTool) {
|
|
37
|
-
const
|
|
38
|
+
const listed = toolName ? data.toolAnalyticsParameterOwnership.get(toolName) : void 0;
|
|
39
|
+
const ownership = override ?? listed;
|
|
38
40
|
return {
|
|
39
41
|
context: !isMissingCapabilityTool && isContextEnabled(data.options.context) && ownership?.context === true,
|
|
40
|
-
conversationId:
|
|
42
|
+
conversationId: true === data.options.enableConversationId && ownership?.conversationId === true,
|
|
43
|
+
outputInstructions: true === data.options.enableConversationId && listed?.outputInstructions === true
|
|
41
44
|
};
|
|
42
45
|
}
|
|
43
46
|
function cloneRequestWithoutOwnedAnalyticsArguments(request, ownership) {
|
|
@@ -54,8 +57,10 @@ function cloneRequestWithoutOwnedAnalyticsArguments(request, ownership) {
|
|
|
54
57
|
}
|
|
55
58
|
async function prepareToolCallEvent(server, data, request, downstreamRequest, extra, startTime, conversation, ownership, eventType) {
|
|
56
59
|
try {
|
|
57
|
-
const sessionId = getSessionId(server, extra);
|
|
58
|
-
|
|
60
|
+
const sessionId = getSessionId(server, extra, conversation.conversationId);
|
|
61
|
+
const sessionInfo = getSessionInfo(server, data, sessionId);
|
|
62
|
+
const identity = await handleIdentify(server, data, sessionId, request, sessionInfo, extra, !!conversation.conversationId);
|
|
63
|
+
const requestAttribution = withIdentity(sessionInfo, identity);
|
|
59
64
|
const toolName = request.params?.name;
|
|
60
65
|
const event = {
|
|
61
66
|
sessionId,
|
|
@@ -70,20 +75,35 @@ async function prepareToolCallEvent(server, data, request, downstreamRequest, ex
|
|
|
70
75
|
stampMetaClientInfo(event, request);
|
|
71
76
|
await applyResolvedMetadata(event, data, request, extra);
|
|
72
77
|
setEventIntent(event, await resolveToolCallIntent(data, request, ownership.context, extra));
|
|
73
|
-
return
|
|
78
|
+
return {
|
|
79
|
+
event,
|
|
80
|
+
requestAttribution
|
|
81
|
+
};
|
|
74
82
|
} catch (error) {
|
|
75
83
|
data.logger(`Warning: PostHog MCP analytics instrumentation failed for tool ${request.params?.name}, the tool will still run - ${error}`);
|
|
76
84
|
return null;
|
|
77
85
|
}
|
|
78
86
|
}
|
|
79
|
-
function
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
|
|
83
|
-
|
|
87
|
+
function applyConversationInstructions(event, result, conversation, ownership) {
|
|
88
|
+
const conversationId = conversation.conversationId;
|
|
89
|
+
if (!conversationId) return result;
|
|
90
|
+
let updated = result;
|
|
91
|
+
let delivered = false;
|
|
92
|
+
if (ownership.outputInstructions) {
|
|
93
|
+
const mirrored = mirrorInstructionsIntoStructuredContent(updated, conversationId);
|
|
94
|
+
delivered = mirrored !== updated;
|
|
95
|
+
updated = mirrored;
|
|
96
|
+
}
|
|
97
|
+
if (conversation.minted && canInjectConversationIdPromptBack(updated)) {
|
|
98
|
+
updated = injectConversationIdPromptBack(updated, conversationId);
|
|
99
|
+
delivered = true;
|
|
100
|
+
}
|
|
101
|
+
if (!delivered && conversation.minted && event) event.conversationId = void 0;
|
|
102
|
+
return updated;
|
|
84
103
|
}
|
|
85
|
-
function publishSuccessfulToolEvent(server,
|
|
86
|
-
if (!
|
|
104
|
+
function publishSuccessfulToolEvent(server, preparedEvent, result, startTime, logger, takeCapturedError) {
|
|
105
|
+
if (!preparedEvent) return;
|
|
106
|
+
const { event, requestAttribution } = preparedEvent;
|
|
87
107
|
try {
|
|
88
108
|
if (isToolResultError(result)) {
|
|
89
109
|
event.isError = true;
|
|
@@ -92,19 +112,20 @@ function publishSuccessfulToolEvent(server, event, result, startTime, logger, ta
|
|
|
92
112
|
} else event.isError = false;
|
|
93
113
|
event.response = result;
|
|
94
114
|
event.duration = Date.now() - startTime.getTime();
|
|
95
|
-
captureEvent(server, event, logger);
|
|
115
|
+
captureEvent(server, event, logger, requestAttribution);
|
|
96
116
|
} catch (error) {
|
|
97
117
|
logger(`Warning: PostHog MCP analytics failed to publish tool event - ${error}`);
|
|
98
118
|
}
|
|
99
119
|
}
|
|
100
|
-
function publishFailedToolEvent(server,
|
|
101
|
-
if (!
|
|
120
|
+
function publishFailedToolEvent(server, preparedEvent, error, startTime, conversation, logger) {
|
|
121
|
+
if (!preparedEvent) return;
|
|
122
|
+
const { event, requestAttribution } = preparedEvent;
|
|
102
123
|
try {
|
|
103
124
|
if (conversation.minted) event.conversationId = void 0;
|
|
104
125
|
event.isError = true;
|
|
105
126
|
event.error = captureException(error);
|
|
106
127
|
event.duration = Date.now() - startTime.getTime();
|
|
107
|
-
captureEvent(server, event, logger);
|
|
128
|
+
captureEvent(server, event, logger, requestAttribution);
|
|
108
129
|
} catch (publishError) {
|
|
109
130
|
logger(`Warning: PostHog MCP analytics failed to publish failed tool event - ${publishError}`);
|
|
110
131
|
}
|
|
@@ -159,15 +180,17 @@ async function isToolAdvertised(server, toolName, extra, logger) {
|
|
|
159
180
|
async function handleListToolsRequest(server, originalListToolsHandler, request, extra, logger) {
|
|
160
181
|
const data = getServerTrackingData(server);
|
|
161
182
|
const startTime = new Date();
|
|
183
|
+
const sessionId = getSessionId(server, extra);
|
|
184
|
+
const requestAttribution = getSessionInfo(server, data, sessionId);
|
|
162
185
|
const event = {
|
|
163
|
-
sessionId
|
|
186
|
+
sessionId,
|
|
164
187
|
parameters: buildCapturedMcpParameters(request),
|
|
165
188
|
eventType: MCPAnalyticsEventType.mcpToolsList,
|
|
166
189
|
timestamp: startTime
|
|
167
190
|
};
|
|
168
191
|
stampMetaClientInfo(event, request);
|
|
169
192
|
if (data) await applyResolvedMetadata(event, data, request, extra);
|
|
170
|
-
const tools = await getTracedToolsList(server, originalListToolsHandler, request, extra, event, logger);
|
|
193
|
+
const tools = await getTracedToolsList(server, originalListToolsHandler, request, extra, event, logger, requestAttribution);
|
|
171
194
|
if (!data) {
|
|
172
195
|
logger('Warning: PostHog MCP analytics is unable to find server tracking data. Please ensure you have called instrument(server, options) before using tool calls.');
|
|
173
196
|
return {
|
|
@@ -179,7 +202,7 @@ async function handleListToolsRequest(server, originalListToolsHandler, request,
|
|
|
179
202
|
event.error = captureException('No tools were sent to MCP client.');
|
|
180
203
|
event.isError = true;
|
|
181
204
|
event.duration = Date.now() - startTime.getTime();
|
|
182
|
-
captureEvent(server, event, data.logger);
|
|
205
|
+
captureEvent(server, event, data.logger, requestAttribution);
|
|
183
206
|
return {
|
|
184
207
|
tools
|
|
185
208
|
};
|
|
@@ -190,20 +213,20 @@ async function handleListToolsRequest(server, originalListToolsHandler, request,
|
|
|
190
213
|
event.listedToolNames = collectListedToolNames(tools);
|
|
191
214
|
event.isError = false;
|
|
192
215
|
event.duration = Date.now() - startTime.getTime();
|
|
193
|
-
captureEvent(server, event, data.logger);
|
|
216
|
+
captureEvent(server, event, data.logger, requestAttribution);
|
|
194
217
|
return {
|
|
195
218
|
tools
|
|
196
219
|
};
|
|
197
220
|
}
|
|
198
221
|
function cacheToolAnalyticsParameterOwnership(cache, tools) {
|
|
199
|
-
for (const tool of tools)if (tool?.name) cache.set(tool.name, getAnalyticsParameterOwnership(tool.inputSchema));
|
|
222
|
+
for (const tool of tools)if (tool?.name) cache.set(tool.name, getAnalyticsParameterOwnership(tool.inputSchema, tool.outputSchema));
|
|
200
223
|
}
|
|
201
224
|
function collectListedToolNames(tools) {
|
|
202
225
|
if (!tools || 0 === tools.length) return;
|
|
203
226
|
const names = tools.map((tool)=>tool?.name).filter((name)=>'string' == typeof name);
|
|
204
227
|
return names.length > 0 ? names : void 0;
|
|
205
228
|
}
|
|
206
|
-
async function getTracedToolsList(server, originalListToolsHandler, request, extra, event, logger) {
|
|
229
|
+
async function getTracedToolsList(server, originalListToolsHandler, request, extra, event, logger, requestAttribution) {
|
|
207
230
|
try {
|
|
208
231
|
const data = getServerTrackingData(server);
|
|
209
232
|
const originalResponse = await originalListToolsHandler(request, extra);
|
|
@@ -214,16 +237,21 @@ async function getTracedToolsList(server, originalListToolsHandler, request, ext
|
|
|
214
237
|
if (data && isContextEnabled(data.options.context)) tools = addContextParameterToTools(tools, getContextDescription(data.options.context), data.logger);
|
|
215
238
|
if (data) {
|
|
216
239
|
const missingToolName = resolveMissingCapabilityToolName(data.options);
|
|
217
|
-
let injectedMissingCapabilityTool = false;
|
|
218
240
|
if (data.options.reportMissing) {
|
|
219
241
|
const alreadyPresent = tools.some((tool)=>tool?.name === missingToolName);
|
|
220
242
|
if (alreadyPresent) data.logger(`Warning: Cannot inject missing-capability tool "${missingToolName}" because a real tool already uses that name. The real tool will not be intercepted.`);
|
|
221
243
|
else {
|
|
222
|
-
|
|
223
|
-
|
|
244
|
+
const virtualTool = getReportMissingToolDescriptor(missingToolName);
|
|
245
|
+
tools.push(virtualTool);
|
|
246
|
+
cacheToolAnalyticsParameterOwnership(data.toolAnalyticsParameterOwnership, [
|
|
247
|
+
virtualTool
|
|
248
|
+
]);
|
|
224
249
|
}
|
|
225
250
|
}
|
|
226
|
-
if (data.options.enableConversationId)
|
|
251
|
+
if (data.options.enableConversationId) {
|
|
252
|
+
tools = addConversationIdToTools(tools, data.logger);
|
|
253
|
+
tools = addInstructionsToOutputSchemas(tools, data.logger);
|
|
254
|
+
}
|
|
227
255
|
}
|
|
228
256
|
if (data) {
|
|
229
257
|
cacheToolDescriptions(data.toolDescriptions, tools);
|
|
@@ -235,7 +263,7 @@ async function getTracedToolsList(server, originalListToolsHandler, request, ext
|
|
|
235
263
|
event.error = captureException(error);
|
|
236
264
|
event.isError = true;
|
|
237
265
|
event.duration = event.timestamp ? Date.now() - event.timestamp.getTime() : 0;
|
|
238
|
-
captureEvent(server, event, logger);
|
|
266
|
+
captureEvent(server, event, logger, requestAttribution);
|
|
239
267
|
throw error;
|
|
240
268
|
}
|
|
241
269
|
}
|
|
@@ -284,19 +312,19 @@ function mintStatelessSessionOnInitialize(server, data, request, extra) {
|
|
|
284
312
|
return;
|
|
285
313
|
}
|
|
286
314
|
}
|
|
287
|
-
function upgradeMintedTokenToNegotiated(server,
|
|
315
|
+
function upgradeMintedTokenToNegotiated(server, mintedSessionId, sessionInfo, negotiatedProtocolVersion, logger) {
|
|
288
316
|
try {
|
|
289
317
|
const transport = server.transport;
|
|
290
318
|
if (!transport) return;
|
|
291
319
|
const token = encodeSessionId({
|
|
292
320
|
sessionId: mintedSessionId,
|
|
293
|
-
clientName:
|
|
294
|
-
clientVersion:
|
|
321
|
+
clientName: sessionInfo.clientName,
|
|
322
|
+
clientVersion: sessionInfo.clientVersion,
|
|
295
323
|
protocolVersion: negotiatedProtocolVersion
|
|
296
324
|
});
|
|
297
325
|
writeSessionIdToTransport(transport, token);
|
|
298
326
|
} catch (error) {
|
|
299
|
-
|
|
327
|
+
logger(`Warning: PostHog MCP analytics failed to upgrade the stateless session token - ${error}`);
|
|
300
328
|
}
|
|
301
329
|
}
|
|
302
330
|
function readInitializeClientInfo(request) {
|
|
@@ -316,7 +344,15 @@ async function handleInitializeRequest(server, originalInitializeHandler, reques
|
|
|
316
344
|
}
|
|
317
345
|
const mintedSessionId = mintStatelessSessionOnInitialize(server, data, request, extra);
|
|
318
346
|
const sessionId = getSessionId(server, extra);
|
|
319
|
-
|
|
347
|
+
const sessionInfo = getSessionInfo(server, data, sessionId);
|
|
348
|
+
const initializeClientInfo = readInitializeClientInfo(request);
|
|
349
|
+
const requestSessionInfo = {
|
|
350
|
+
...sessionInfo,
|
|
351
|
+
clientName: initializeClientInfo?.name ?? sessionInfo.clientName,
|
|
352
|
+
clientVersion: initializeClientInfo?.version ?? sessionInfo.clientVersion
|
|
353
|
+
};
|
|
354
|
+
const identity = await handleIdentify(server, data, sessionId, request, requestSessionInfo, extra);
|
|
355
|
+
const requestAttribution = withIdentity(requestSessionInfo, identity);
|
|
320
356
|
const event = {
|
|
321
357
|
sessionId,
|
|
322
358
|
resourceName: request.params?.name || 'Unknown Tool Name',
|
|
@@ -330,10 +366,18 @@ async function handleInitializeRequest(server, originalInitializeHandler, reques
|
|
|
330
366
|
event.response = result;
|
|
331
367
|
const negotiatedProtocolVersion = readProtocolVersion(result, request);
|
|
332
368
|
event.protocolVersion = negotiatedProtocolVersion;
|
|
333
|
-
data.
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
369
|
+
if (data.sessionId === sessionId) {
|
|
370
|
+
data.sessionInfo = {
|
|
371
|
+
...data.sessionInfo,
|
|
372
|
+
protocolVersion: negotiatedProtocolVersion
|
|
373
|
+
};
|
|
374
|
+
setServerTrackingData(server, data);
|
|
375
|
+
}
|
|
376
|
+
if (mintedSessionId) upgradeMintedTokenToNegotiated(server, mintedSessionId, requestSessionInfo, negotiatedProtocolVersion, data.logger);
|
|
377
|
+
captureEvent(server, event, data.logger, {
|
|
378
|
+
...requestAttribution,
|
|
379
|
+
protocolVersion: negotiatedProtocolVersion
|
|
380
|
+
});
|
|
337
381
|
return result;
|
|
338
382
|
}
|
|
339
383
|
function readProtocolVersion(result, request) {
|
|
@@ -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,15 @@ 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
|
|
27
|
+
export declare function handleIdentify(server: MCPServerLike, data: MCPAnalyticsData, sessionId: string, request: MCPRequestLike, requestAttribution: SessionInfo, extra?: CompatibleRequestHandlerExtra,
|
|
28
|
+
/**
|
|
29
|
+
* True when this session id came from an agent-carried `conversation_id`
|
|
30
|
+
* rather than the transport. Such a session is brand new and was never
|
|
31
|
+
* announced at `initialize`, whatever `data.sessionSource` still says about
|
|
32
|
+
* the connection.
|
|
33
|
+
*/
|
|
34
|
+
sessionFromConversation?: boolean): Promise<UserIdentity | undefined>;
|
|
35
|
+
export declare function withIdentity(sessionInfo: SessionInfo, identity: UserIdentity | undefined): SessionInfo;
|
|
28
36
|
/**
|
|
29
37
|
* Resolves the `eventProperties` callback. Returns null when no callback is
|
|
30
38
|
* 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,
|
|
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;AACrC;;;;;GAKG;AACH,uBAAuB,UAAQ,GAC9B,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAuDnC;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, sessionFromConversation = false) {
|
|
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 = !sessionFromConversation && '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, sessionFromConversation = false) {
|
|
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 = !sessionFromConversation && '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 };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { LoggerFn } from './logger';
|
|
2
|
+
/**
|
|
3
|
+
* The `conversation_id` session handle reaches the agent as a text block appended to the
|
|
4
|
+
* tool result. That block is invisible to any client that reads
|
|
5
|
+
* `structuredContent` instead of `content` — which is what clients do whenever a
|
|
6
|
+
* tool declares an `outputSchema`. Measured against Claude Code, correlation for
|
|
7
|
+
* such tools drops to zero.
|
|
8
|
+
*
|
|
9
|
+
* The fix is to mirror the session handle into `structuredContent` as well, which this
|
|
10
|
+
* module does in two halves that must stay in that order:
|
|
11
|
+
*
|
|
12
|
+
* 1. declare `_mcp_instructions` on the tool's advertised `outputSchema`
|
|
13
|
+
* 2. write it into the result's `structuredContent`
|
|
14
|
+
*
|
|
15
|
+
* The declaration is what makes the write safe. The MCP client ajv-validates
|
|
16
|
+
* `structuredContent` against the schema from `tools/list`, and
|
|
17
|
+
* `zod-to-json-schema` emits `additionalProperties: false` for a plain
|
|
18
|
+
* `z.object`, so an undeclared key is not ignored — it fails the entire tool
|
|
19
|
+
* result. Only tools that got the declaration are ever written to.
|
|
20
|
+
*/
|
|
21
|
+
export declare const MCP_INSTRUCTIONS_KEY = "_mcp_instructions";
|
|
22
|
+
export interface OutputInstructionsInjectableTool {
|
|
23
|
+
name?: string;
|
|
24
|
+
outputSchema?: unknown;
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* True when we can safely declare {@link MCP_INSTRUCTIONS_KEY} on this tool's
|
|
29
|
+
* advertised output schema.
|
|
30
|
+
*
|
|
31
|
+
* Requires a plain-object schema we can extend. A tool with no `outputSchema`
|
|
32
|
+
* has nothing to mirror into and keeps working through `content`; a composed
|
|
33
|
+
* schema (`oneOf`/`allOf`/`anyOf`/`$ref`) has no single `properties` bag to add
|
|
34
|
+
* to. Both stay content-only, matching the policy on the input side.
|
|
35
|
+
*/
|
|
36
|
+
export declare function canDeclareOutputInstructions(outputSchema: unknown): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Returns a copy of the tool with an optional {@link MCP_INSTRUCTIONS_KEY}
|
|
39
|
+
* property added to its output schema, or the tool untouched when it has no
|
|
40
|
+
* output schema to extend.
|
|
41
|
+
*
|
|
42
|
+
* The property is never added to `required` — a result without it must stay
|
|
43
|
+
* valid, since every tool result predating this change lacks it.
|
|
44
|
+
*/
|
|
45
|
+
export declare function addInstructionsToOutputSchema<TTool extends OutputInstructionsInjectableTool>(tool: TTool, logger?: LoggerFn): TTool;
|
|
46
|
+
/**
|
|
47
|
+
* Declares {@link MCP_INSTRUCTIONS_KEY} across a tool listing. Tools without an
|
|
48
|
+
* output schema, and tools whose schema we cannot extend, pass through unchanged.
|
|
49
|
+
*/
|
|
50
|
+
export declare function addInstructionsToOutputSchemas<TTool extends OutputInstructionsInjectableTool>(tools: TTool[], logger?: LoggerFn): TTool[];
|
|
51
|
+
export interface ConversationInstructions {
|
|
52
|
+
conversation_id: string;
|
|
53
|
+
instructions: string;
|
|
54
|
+
}
|
|
55
|
+
/** The payload mirrored into `structuredContent` for a tool we declared the key on. */
|
|
56
|
+
export declare function buildConversationInstructions(conversationId: string): ConversationInstructions;
|
|
57
|
+
/**
|
|
58
|
+
* Adds {@link MCP_INSTRUCTIONS_KEY} to a result's `structuredContent`, which is
|
|
59
|
+
* where clients look once a tool declares an `outputSchema` — the `content` text
|
|
60
|
+
* block carrying the session handle is invisible to them.
|
|
61
|
+
*
|
|
62
|
+
* Unlike the text block, this rides *every* response rather than only the one
|
|
63
|
+
* that minted the session handle, so an agent that drops it can read it back.
|
|
64
|
+
*
|
|
65
|
+
* Returns the result untouched when there is no plain-object `structuredContent`
|
|
66
|
+
* to extend, or when the tool already produced its own key — customer data wins.
|
|
67
|
+
* Never mutates the input.
|
|
68
|
+
*/
|
|
69
|
+
export declare function mirrorInstructionsIntoStructuredContent(result: unknown, conversationId: string): unknown;
|
|
70
|
+
//# sourceMappingURL=output-instructions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-instructions.d.ts","sourceRoot":"","sources":["../../src/extensions/output-instructions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAIxC;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,oBAAoB,sBAAsB,CAAA;AAQvD,MAAM,WAAW,gCAAgC;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAAC,YAAY,EAAE,OAAO,GAAG,OAAO,CAgC3E;AAwCD;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,SAAS,gCAAgC,EAC1F,IAAI,EAAE,KAAK,EACX,MAAM,GAAE,QAAc,GACrB,KAAK,CA8CP;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,SAAS,gCAAgC,EAC3F,KAAK,EAAE,KAAK,EAAE,EACd,MAAM,GAAE,QAAc,GACrB,KAAK,EAAE,CAET;AAED,MAAM,WAAW,wBAAwB;IACvC,eAAe,EAAE,MAAM,CAAA;IACvB,YAAY,EAAE,MAAM,CAAA;CACrB;AAID,uFAAuF;AACvF,wBAAgB,6BAA6B,CAAC,cAAc,EAAE,MAAM,GAAG,wBAAwB,CAE9F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uCAAuC,CAAC,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAkBxG"}
|