@posthog/mcp 0.10.7 → 0.10.9
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/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/instrument-lowlevel.d.ts.map +1 -1
- package/dist/extensions/instrument-lowlevel.js +6 -5
- package/dist/extensions/instrument-lowlevel.mjs +7 -6
- package/dist/extensions/instrumentation.d.ts +22 -0
- package/dist/extensions/instrumentation.d.ts.map +1 -1
- package/dist/extensions/instrumentation.js +48 -23
- package/dist/extensions/instrumentation.mjs +46 -24
- package/dist/extensions/internal.d.ts +8 -1
- package/dist/extensions/internal.d.ts.map +1 -1
- package/dist/extensions/internal.js +2 -2
- package/dist/extensions/internal.mjs +2 -2
- package/dist/extensions/mcp-sdk-compat.d.ts +10 -0
- package/dist/extensions/mcp-sdk-compat.d.ts.map +1 -1
- package/dist/extensions/mcp-sdk-compat.js +11 -2
- package/dist/extensions/mcp-sdk-compat.mjs +7 -1
- 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 +32 -14
- package/dist/extensions/session.mjs +30 -15
- 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 +4 -4
- package/src/extensions/analytics-parameters.ts +15 -2
- package/src/extensions/conversation-id.ts +51 -29
- package/src/extensions/instrument-highlevel.ts +3 -1
- package/src/extensions/instrument-lowlevel.ts +14 -9
- package/src/extensions/instrumentation.ts +130 -38
- package/src/extensions/internal.ts +10 -2
- package/src/extensions/mcp-sdk-compat.ts +18 -0
- package/src/extensions/output-instructions.ts +231 -0
- package/src/extensions/session.ts +127 -28
- package/src/types.ts +23 -1
- package/src/version.ts +1 -1
|
@@ -2,13 +2,14 @@ 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
10
|
import { getServerTrackingData, handleIdentify, setServerTrackingData, withIdentity } from "./internal.mjs";
|
|
10
11
|
import { buildCapturedMcpParameters } from "./mcp-payloads.mjs";
|
|
11
|
-
import {
|
|
12
|
+
import { readRequestHandlerMethod } from "./mcp-sdk-compat.mjs";
|
|
12
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";
|
|
@@ -17,7 +18,7 @@ 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
24
|
const preparedEvent = await prepareToolCallEvent(server, data, request, downstreamRequest, extra, startTime, conversation, ownership, resolvedEventType);
|
|
@@ -29,15 +30,17 @@ async function captureToolCall(params) {
|
|
|
29
30
|
publishFailedToolEvent(server, preparedEvent, error, startTime, conversation, data.logger);
|
|
30
31
|
throw error;
|
|
31
32
|
}
|
|
32
|
-
const finalResult =
|
|
33
|
+
const finalResult = applyConversationInstructions(preparedEvent?.event ?? null, result, conversation, ownership);
|
|
33
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,9 +57,9 @@ 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);
|
|
60
|
+
const sessionId = getSessionId(server, extra, conversation.conversationId);
|
|
58
61
|
const sessionInfo = getSessionInfo(server, data, sessionId);
|
|
59
|
-
const identity = await handleIdentify(server, data, sessionId, request, sessionInfo, extra);
|
|
62
|
+
const identity = await handleIdentify(server, data, sessionId, request, sessionInfo, extra, !!conversation.conversationId);
|
|
60
63
|
const requestAttribution = withIdentity(sessionInfo, identity);
|
|
61
64
|
const toolName = request.params?.name;
|
|
62
65
|
const event = {
|
|
@@ -81,11 +84,22 @@ async function prepareToolCallEvent(server, data, request, downstreamRequest, ex
|
|
|
81
84
|
return null;
|
|
82
85
|
}
|
|
83
86
|
}
|
|
84
|
-
function
|
|
85
|
-
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
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;
|
|
89
103
|
}
|
|
90
104
|
function publishSuccessfulToolEvent(server, preparedEvent, result, startTime, logger, takeCapturedError) {
|
|
91
105
|
if (!preparedEvent) return;
|
|
@@ -125,6 +139,10 @@ function rememberOriginalRequestHandler(server, handlerName, originalHandler) {
|
|
|
125
139
|
}
|
|
126
140
|
handlers.set(handlerName, originalHandler);
|
|
127
141
|
}
|
|
142
|
+
function registerFallbackRequestHandler(server, handlerName, fallbackHandler, patch) {
|
|
143
|
+
rememberOriginalRequestHandler(server, handlerName, fallbackHandler);
|
|
144
|
+
server._requestHandlers.set(handlerName, (request, extra)=>patch(server, fallbackHandler, request, extra));
|
|
145
|
+
}
|
|
128
146
|
function patchRequestHandlers(server, patches) {
|
|
129
147
|
for (const [handlerName, patch] of Object.entries(patches)){
|
|
130
148
|
const originalHandler = server._requestHandlers.get(handlerName);
|
|
@@ -134,12 +152,11 @@ function patchRequestHandlers(server, patches) {
|
|
|
134
152
|
}
|
|
135
153
|
}
|
|
136
154
|
const originalSetRequestHandler = server.setRequestHandler.bind(server);
|
|
137
|
-
server.setRequestHandler = (
|
|
138
|
-
const
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const result = originalSetRequestHandler(requestSchema, originalHandler);
|
|
155
|
+
server.setRequestHandler = (...args)=>{
|
|
156
|
+
const handlerName = readRequestHandlerMethod(args[0]);
|
|
157
|
+
const patch = void 0 !== handlerName && Object.prototype.hasOwnProperty.call(patches, handlerName) ? patches[handlerName] : void 0;
|
|
158
|
+
if (void 0 === handlerName || !patch) return originalSetRequestHandler(...args);
|
|
159
|
+
const result = originalSetRequestHandler(...args);
|
|
143
160
|
const registeredHandler = server._requestHandlers.get(handlerName);
|
|
144
161
|
if (registeredHandler) {
|
|
145
162
|
rememberOriginalRequestHandler(server, handlerName, registeredHandler);
|
|
@@ -205,7 +222,7 @@ async function handleListToolsRequest(server, originalListToolsHandler, request,
|
|
|
205
222
|
};
|
|
206
223
|
}
|
|
207
224
|
function cacheToolAnalyticsParameterOwnership(cache, tools) {
|
|
208
|
-
for (const tool of tools)if (tool?.name) cache.set(tool.name, getAnalyticsParameterOwnership(tool.inputSchema));
|
|
225
|
+
for (const tool of tools)if (tool?.name) cache.set(tool.name, getAnalyticsParameterOwnership(tool.inputSchema, tool.outputSchema));
|
|
209
226
|
}
|
|
210
227
|
function collectListedToolNames(tools) {
|
|
211
228
|
if (!tools || 0 === tools.length) return;
|
|
@@ -223,16 +240,21 @@ async function getTracedToolsList(server, originalListToolsHandler, request, ext
|
|
|
223
240
|
if (data && isContextEnabled(data.options.context)) tools = addContextParameterToTools(tools, getContextDescription(data.options.context), data.logger);
|
|
224
241
|
if (data) {
|
|
225
242
|
const missingToolName = resolveMissingCapabilityToolName(data.options);
|
|
226
|
-
let injectedMissingCapabilityTool = false;
|
|
227
243
|
if (data.options.reportMissing) {
|
|
228
244
|
const alreadyPresent = tools.some((tool)=>tool?.name === missingToolName);
|
|
229
245
|
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.`);
|
|
230
246
|
else {
|
|
231
|
-
|
|
232
|
-
|
|
247
|
+
const virtualTool = getReportMissingToolDescriptor(missingToolName);
|
|
248
|
+
tools.push(virtualTool);
|
|
249
|
+
cacheToolAnalyticsParameterOwnership(data.toolAnalyticsParameterOwnership, [
|
|
250
|
+
virtualTool
|
|
251
|
+
]);
|
|
233
252
|
}
|
|
234
253
|
}
|
|
235
|
-
if (data.options.enableConversationId)
|
|
254
|
+
if (data.options.enableConversationId) {
|
|
255
|
+
tools = addConversationIdToTools(tools, data.logger);
|
|
256
|
+
tools = addInstructionsToOutputSchemas(tools, data.logger);
|
|
257
|
+
}
|
|
236
258
|
}
|
|
237
259
|
if (data) {
|
|
238
260
|
cacheToolDescriptions(data.toolDescriptions, tools);
|
|
@@ -367,4 +389,4 @@ function readProtocolVersion(result, request) {
|
|
|
367
389
|
const requested = request.params?.protocolVersion;
|
|
368
390
|
return 'string' == typeof requested && requested.length > 0 ? requested : void 0;
|
|
369
391
|
}
|
|
370
|
-
export { cacheToolCategories, cacheToolDescriptions, captureToolCall, handleInitializeRequest, handleListToolsRequest, isToolAdvertised, patchRequestHandlers, readToolMetaCategory };
|
|
392
|
+
export { cacheToolCategories, cacheToolDescriptions, captureToolCall, handleInitializeRequest, handleListToolsRequest, isToolAdvertised, patchRequestHandlers, readToolMetaCategory, registerFallbackRequestHandler };
|
|
@@ -24,7 +24,14 @@ 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, requestAttribution: SessionInfo, 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>;
|
|
28
35
|
export declare function withIdentity(sessionInfo: SessionInfo, identity: UserIdentity | undefined): SessionInfo;
|
|
29
36
|
/**
|
|
30
37
|
* Resolves the `eventProperties` callback. Returns null when no callback is
|
|
@@ -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,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,
|
|
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"}
|
|
@@ -95,7 +95,7 @@ function mergeIdentities(previous, next) {
|
|
|
95
95
|
groups: next.groups ?? previous.groups
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
|
-
async function handleIdentify(server, data, sessionId, request, requestAttribution, extra) {
|
|
98
|
+
async function handleIdentify(server, data, sessionId, request, requestAttribution, extra, sessionFromConversation = false) {
|
|
99
99
|
const identityBeforeRequest = data.identifiedSessions.get(sessionId);
|
|
100
100
|
const sessionSourceBeforeIdentify = data.sessionSource;
|
|
101
101
|
if (!data.options.identify) return identityBeforeRequest;
|
|
@@ -117,7 +117,7 @@ async function handleIdentify(server, data, sessionId, request, requestAttributi
|
|
|
117
117
|
const mergedIdentity = mergeIdentities(previousIdentity, identityResult);
|
|
118
118
|
const changed = void 0 !== previousIdentity && !areIdentitiesEqual(previousIdentity, mergedIdentity);
|
|
119
119
|
const firstSeen = void 0 === previousIdentity;
|
|
120
|
-
const announcedAtInitialize = 'token' === sessionSourceBeforeIdentify && 'initialize' !== request.method;
|
|
120
|
+
const announcedAtInitialize = !sessionFromConversation && 'token' === sessionSourceBeforeIdentify && 'initialize' !== request.method;
|
|
121
121
|
const shouldPublish = changed || firstSeen && !announcedAtInitialize;
|
|
122
122
|
data.identifiedSessions.set(sessionId, mergedIdentity);
|
|
123
123
|
if (shouldPublish) {
|
|
@@ -60,7 +60,7 @@ function mergeIdentities(previous, next) {
|
|
|
60
60
|
groups: next.groups ?? previous.groups
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
|
-
async function handleIdentify(server, data, sessionId, request, requestAttribution, extra) {
|
|
63
|
+
async function handleIdentify(server, data, sessionId, request, requestAttribution, extra, sessionFromConversation = false) {
|
|
64
64
|
const identityBeforeRequest = data.identifiedSessions.get(sessionId);
|
|
65
65
|
const sessionSourceBeforeIdentify = data.sessionSource;
|
|
66
66
|
if (!data.options.identify) return identityBeforeRequest;
|
|
@@ -82,7 +82,7 @@ async function handleIdentify(server, data, sessionId, request, requestAttributi
|
|
|
82
82
|
const mergedIdentity = mergeIdentities(previousIdentity, identityResult);
|
|
83
83
|
const changed = void 0 !== previousIdentity && !areIdentitiesEqual(previousIdentity, mergedIdentity);
|
|
84
84
|
const firstSeen = void 0 === previousIdentity;
|
|
85
|
-
const announcedAtInitialize = 'token' === sessionSourceBeforeIdentify && 'initialize' !== request.method;
|
|
85
|
+
const announcedAtInitialize = !sessionFromConversation && 'token' === sessionSourceBeforeIdentify && 'initialize' !== request.method;
|
|
86
86
|
const shouldPublish = changed || firstSeen && !announcedAtInitialize;
|
|
87
87
|
data.identifiedSessions.set(sessionId, mergedIdentity);
|
|
88
88
|
if (shouldPublish) {
|
|
@@ -30,5 +30,15 @@ export declare function createWrappedTool(originalTool: RegisteredTool, wrappedF
|
|
|
30
30
|
export declare function isZ4Schema(schema: unknown): boolean;
|
|
31
31
|
export declare function isZodRawShapeCompat(schema: unknown): schema is Record<string, unknown>;
|
|
32
32
|
export declare function getObjectShape(schema: unknown): Record<string, unknown> | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Reads the method name a `setRequestHandler` registration is for.
|
|
35
|
+
*
|
|
36
|
+
* The two SDK majors disagree on what the first argument is: v1 passes the Zod
|
|
37
|
+
* schema for the request and carries the method as a literal on its shape, v2
|
|
38
|
+
* passes the method string itself. A registration we cannot name is one we
|
|
39
|
+
* cannot match against a patch — which is how a handler registered after
|
|
40
|
+
* `instrument()` on v2 ends up replacing our wrapper instead of being wrapped.
|
|
41
|
+
*/
|
|
42
|
+
export declare function readRequestHandlerMethod(requestSchema: unknown): string | undefined;
|
|
33
43
|
export declare function getLiteralValue(schema: unknown): unknown;
|
|
34
44
|
//# sourceMappingURL=mcp-sdk-compat.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-sdk-compat.d.ts","sourceRoot":"","sources":["../../src/extensions/mcp-sdk-compat.ts"],"names":[],"mappings":"AAKA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAK5D,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,SAAS,CAAA;AAEpD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,GAAG,YAAY,CAQlE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,cAAc,GAAG,eAAe,CAKxE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,cAAc,CAMrE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,GAAG,cAAc,CAM7G;AAyBD,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAKnD;AAaD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEtF;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CA6BnF;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAkCxD"}
|
|
1
|
+
{"version":3,"file":"mcp-sdk-compat.d.ts","sourceRoot":"","sources":["../../src/extensions/mcp-sdk-compat.ts"],"names":[],"mappings":"AAKA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAK5D,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,SAAS,CAAA;AAEpD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,GAAG,YAAY,CAQlE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,cAAc,GAAG,eAAe,CAKxE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,cAAc,CAMrE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,GAAG,cAAc,CAM7G;AAyBD,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAKnD;AAaD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEtF;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CA6BnF;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,aAAa,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAOnF;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAkCxD"}
|
|
@@ -31,7 +31,8 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
31
31
|
getToolFunctionKey: ()=>getToolFunctionKey,
|
|
32
32
|
hasToolFunction: ()=>hasToolFunction,
|
|
33
33
|
isZ4Schema: ()=>isZ4Schema,
|
|
34
|
-
isZodRawShapeCompat: ()=>isZodRawShapeCompat
|
|
34
|
+
isZodRawShapeCompat: ()=>isZodRawShapeCompat,
|
|
35
|
+
readRequestHandlerMethod: ()=>readRequestHandlerMethod
|
|
35
36
|
});
|
|
36
37
|
function getToolFunction(tool) {
|
|
37
38
|
if ('handler' in tool && 'function' == typeof tool.handler) return tool.handler;
|
|
@@ -82,6 +83,12 @@ function getObjectShape(schema) {
|
|
|
82
83
|
}
|
|
83
84
|
return rawShape;
|
|
84
85
|
}
|
|
86
|
+
function readRequestHandlerMethod(requestSchema) {
|
|
87
|
+
if ('string' == typeof requestSchema) return requestSchema;
|
|
88
|
+
const shape = getObjectShape(requestSchema);
|
|
89
|
+
const method = shape?.method ? getLiteralValue(shape.method) : void 0;
|
|
90
|
+
return 'string' == typeof method ? method : void 0;
|
|
91
|
+
}
|
|
85
92
|
function getLiteralValue(schema) {
|
|
86
93
|
if (!schema || 'object' != typeof schema) return;
|
|
87
94
|
if (isZ4Schema(schema)) {
|
|
@@ -106,6 +113,7 @@ exports.getToolFunctionKey = __webpack_exports__.getToolFunctionKey;
|
|
|
106
113
|
exports.hasToolFunction = __webpack_exports__.hasToolFunction;
|
|
107
114
|
exports.isZ4Schema = __webpack_exports__.isZ4Schema;
|
|
108
115
|
exports.isZodRawShapeCompat = __webpack_exports__.isZodRawShapeCompat;
|
|
116
|
+
exports.readRequestHandlerMethod = __webpack_exports__.readRequestHandlerMethod;
|
|
109
117
|
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
110
118
|
"createWrappedTool",
|
|
111
119
|
"getLiteralValue",
|
|
@@ -114,7 +122,8 @@ for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
|
114
122
|
"getToolFunctionKey",
|
|
115
123
|
"hasToolFunction",
|
|
116
124
|
"isZ4Schema",
|
|
117
|
-
"isZodRawShapeCompat"
|
|
125
|
+
"isZodRawShapeCompat",
|
|
126
|
+
"readRequestHandlerMethod"
|
|
118
127
|
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
119
128
|
Object.defineProperty(exports, '__esModule', {
|
|
120
129
|
value: true
|
|
@@ -47,6 +47,12 @@ function getObjectShape(schema) {
|
|
|
47
47
|
}
|
|
48
48
|
return rawShape;
|
|
49
49
|
}
|
|
50
|
+
function readRequestHandlerMethod(requestSchema) {
|
|
51
|
+
if ('string' == typeof requestSchema) return requestSchema;
|
|
52
|
+
const shape = getObjectShape(requestSchema);
|
|
53
|
+
const method = shape?.method ? getLiteralValue(shape.method) : void 0;
|
|
54
|
+
return 'string' == typeof method ? method : void 0;
|
|
55
|
+
}
|
|
50
56
|
function getLiteralValue(schema) {
|
|
51
57
|
if (!schema || 'object' != typeof schema) return;
|
|
52
58
|
if (isZ4Schema(schema)) {
|
|
@@ -63,4 +69,4 @@ function getLiteralValue(schema) {
|
|
|
63
69
|
const directValue = schema.value;
|
|
64
70
|
if (void 0 !== directValue) return directValue;
|
|
65
71
|
}
|
|
66
|
-
export { createWrappedTool, getLiteralValue, getObjectShape, getToolFunction, getToolFunctionKey, hasToolFunction, isZ4Schema, isZodRawShapeCompat };
|
|
72
|
+
export { createWrappedTool, getLiteralValue, getObjectShape, getToolFunction, getToolFunctionKey, hasToolFunction, isZ4Schema, isZodRawShapeCompat, readRequestHandlerMethod };
|
|
@@ -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"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
mirrorInstructionsIntoStructuredContent: ()=>mirrorInstructionsIntoStructuredContent,
|
|
28
|
+
addInstructionsToOutputSchema: ()=>addInstructionsToOutputSchema,
|
|
29
|
+
addInstructionsToOutputSchemas: ()=>addInstructionsToOutputSchemas,
|
|
30
|
+
MCP_INSTRUCTIONS_KEY: ()=>MCP_INSTRUCTIONS_KEY,
|
|
31
|
+
buildConversationInstructions: ()=>buildConversationInstructions,
|
|
32
|
+
canDeclareOutputInstructions: ()=>canDeclareOutputInstructions
|
|
33
|
+
});
|
|
34
|
+
const external_logger_js_namespaceObject = require("./logger.js");
|
|
35
|
+
const MCP_INSTRUCTIONS_KEY = '_mcp_instructions';
|
|
36
|
+
const INSTRUCTIONS_FIELD_DESCRIPTION = 'Server-issued handles for this conversation, and what to do with them. Read and follow.';
|
|
37
|
+
const CONVERSATION_ID_FIELD_DESCRIPTION = 'Echo this exact value as the conversation_id argument on every subsequent tool call.';
|
|
38
|
+
function canDeclareOutputInstructions(outputSchema) {
|
|
39
|
+
if (!outputSchema || 'object' != typeof outputSchema) return false;
|
|
40
|
+
if (isSchemaObjectLike(outputSchema)) return false;
|
|
41
|
+
const schema = outputSchema;
|
|
42
|
+
if (schema.$ref || schema.oneOf || schema.allOf || schema.anyOf) return false;
|
|
43
|
+
const { properties } = schema;
|
|
44
|
+
if (void 0 !== properties && ('object' != typeof properties || null === properties || Array.isArray(properties))) return false;
|
|
45
|
+
return !properties || !Object.prototype.hasOwnProperty.call(properties, MCP_INSTRUCTIONS_KEY);
|
|
46
|
+
}
|
|
47
|
+
function isZodLike(entry) {
|
|
48
|
+
return !!entry && 'object' == typeof entry && ('_def' in entry || '_zod' in entry || '~standard' in entry);
|
|
49
|
+
}
|
|
50
|
+
function looksLikeJsonSchema(candidate) {
|
|
51
|
+
if ('string' == typeof candidate.type || 'string' == typeof candidate.$ref) return true;
|
|
52
|
+
if (Array.isArray(candidate.oneOf) || Array.isArray(candidate.allOf) || Array.isArray(candidate.anyOf)) return true;
|
|
53
|
+
const { properties } = candidate;
|
|
54
|
+
return !!properties && 'object' == typeof properties && !isZodLike(properties);
|
|
55
|
+
}
|
|
56
|
+
function isSchemaObjectLike(value) {
|
|
57
|
+
const candidate = value;
|
|
58
|
+
if (isZodLike(candidate)) return true;
|
|
59
|
+
if (looksLikeJsonSchema(candidate)) return false;
|
|
60
|
+
return Object.values(candidate).some(isZodLike);
|
|
61
|
+
}
|
|
62
|
+
function addInstructionsToOutputSchema(tool, logger = external_logger_js_namespaceObject.log) {
|
|
63
|
+
const toolName = tool.name || 'unknown';
|
|
64
|
+
const original = tool.outputSchema;
|
|
65
|
+
if (!original || 'object' != typeof original) return tool;
|
|
66
|
+
if (!canDeclareOutputInstructions(original)) {
|
|
67
|
+
const schema = original;
|
|
68
|
+
logger(schema.properties && Object.prototype.hasOwnProperty.call(schema.properties, MCP_INSTRUCTIONS_KEY) ? `WARN: Tool "${toolName}" already declares '${MCP_INSTRUCTIONS_KEY}' in its output schema. Leaving it alone.` : `WARN: Tool "${toolName}" has a complex output schema (oneOf/allOf/anyOf/$ref). Skipping '${MCP_INSTRUCTIONS_KEY}' declaration; its session handle stays content-only.`);
|
|
69
|
+
return tool;
|
|
70
|
+
}
|
|
71
|
+
const modifiedTool = {
|
|
72
|
+
...tool
|
|
73
|
+
};
|
|
74
|
+
const outputSchema = JSON.parse(JSON.stringify(original));
|
|
75
|
+
if (!outputSchema.properties) outputSchema.properties = {};
|
|
76
|
+
outputSchema.properties[MCP_INSTRUCTIONS_KEY] = {
|
|
77
|
+
type: 'object',
|
|
78
|
+
description: INSTRUCTIONS_FIELD_DESCRIPTION,
|
|
79
|
+
properties: {
|
|
80
|
+
conversation_id: {
|
|
81
|
+
type: 'string',
|
|
82
|
+
description: CONVERSATION_ID_FIELD_DESCRIPTION
|
|
83
|
+
},
|
|
84
|
+
instructions: {
|
|
85
|
+
type: 'string'
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
modifiedTool.outputSchema = outputSchema;
|
|
90
|
+
return modifiedTool;
|
|
91
|
+
}
|
|
92
|
+
function addInstructionsToOutputSchemas(tools, logger = external_logger_js_namespaceObject.log) {
|
|
93
|
+
return tools.map((tool)=>addInstructionsToOutputSchema(tool, logger));
|
|
94
|
+
}
|
|
95
|
+
const ECHO_INSTRUCTION = 'Send this conversation_id as an argument on every subsequent tool call in this conversation.';
|
|
96
|
+
function buildConversationInstructions(conversationId) {
|
|
97
|
+
return {
|
|
98
|
+
conversation_id: conversationId,
|
|
99
|
+
instructions: ECHO_INSTRUCTION
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function mirrorInstructionsIntoStructuredContent(result, conversationId) {
|
|
103
|
+
if (!result || 'object' != typeof result) return result;
|
|
104
|
+
const structuredContent = result.structuredContent;
|
|
105
|
+
if (!structuredContent || 'object' != typeof structuredContent || Array.isArray(structuredContent)) return result;
|
|
106
|
+
if (Object.prototype.hasOwnProperty.call(structuredContent, MCP_INSTRUCTIONS_KEY)) return result;
|
|
107
|
+
return {
|
|
108
|
+
...result,
|
|
109
|
+
structuredContent: {
|
|
110
|
+
...structuredContent,
|
|
111
|
+
[MCP_INSTRUCTIONS_KEY]: buildConversationInstructions(conversationId)
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
exports.MCP_INSTRUCTIONS_KEY = __webpack_exports__.MCP_INSTRUCTIONS_KEY;
|
|
116
|
+
exports.addInstructionsToOutputSchema = __webpack_exports__.addInstructionsToOutputSchema;
|
|
117
|
+
exports.addInstructionsToOutputSchemas = __webpack_exports__.addInstructionsToOutputSchemas;
|
|
118
|
+
exports.buildConversationInstructions = __webpack_exports__.buildConversationInstructions;
|
|
119
|
+
exports.canDeclareOutputInstructions = __webpack_exports__.canDeclareOutputInstructions;
|
|
120
|
+
exports.mirrorInstructionsIntoStructuredContent = __webpack_exports__.mirrorInstructionsIntoStructuredContent;
|
|
121
|
+
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
122
|
+
"MCP_INSTRUCTIONS_KEY",
|
|
123
|
+
"addInstructionsToOutputSchema",
|
|
124
|
+
"addInstructionsToOutputSchemas",
|
|
125
|
+
"buildConversationInstructions",
|
|
126
|
+
"canDeclareOutputInstructions",
|
|
127
|
+
"mirrorInstructionsIntoStructuredContent"
|
|
128
|
+
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
129
|
+
Object.defineProperty(exports, '__esModule', {
|
|
130
|
+
value: true
|
|
131
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { log } from "./logger.mjs";
|
|
2
|
+
const MCP_INSTRUCTIONS_KEY = '_mcp_instructions';
|
|
3
|
+
const INSTRUCTIONS_FIELD_DESCRIPTION = 'Server-issued handles for this conversation, and what to do with them. Read and follow.';
|
|
4
|
+
const CONVERSATION_ID_FIELD_DESCRIPTION = 'Echo this exact value as the conversation_id argument on every subsequent tool call.';
|
|
5
|
+
function canDeclareOutputInstructions(outputSchema) {
|
|
6
|
+
if (!outputSchema || 'object' != typeof outputSchema) return false;
|
|
7
|
+
if (isSchemaObjectLike(outputSchema)) return false;
|
|
8
|
+
const schema = outputSchema;
|
|
9
|
+
if (schema.$ref || schema.oneOf || schema.allOf || schema.anyOf) return false;
|
|
10
|
+
const { properties } = schema;
|
|
11
|
+
if (void 0 !== properties && ('object' != typeof properties || null === properties || Array.isArray(properties))) return false;
|
|
12
|
+
return !properties || !Object.prototype.hasOwnProperty.call(properties, MCP_INSTRUCTIONS_KEY);
|
|
13
|
+
}
|
|
14
|
+
function isZodLike(entry) {
|
|
15
|
+
return !!entry && 'object' == typeof entry && ('_def' in entry || '_zod' in entry || '~standard' in entry);
|
|
16
|
+
}
|
|
17
|
+
function looksLikeJsonSchema(candidate) {
|
|
18
|
+
if ('string' == typeof candidate.type || 'string' == typeof candidate.$ref) return true;
|
|
19
|
+
if (Array.isArray(candidate.oneOf) || Array.isArray(candidate.allOf) || Array.isArray(candidate.anyOf)) return true;
|
|
20
|
+
const { properties } = candidate;
|
|
21
|
+
return !!properties && 'object' == typeof properties && !isZodLike(properties);
|
|
22
|
+
}
|
|
23
|
+
function isSchemaObjectLike(value) {
|
|
24
|
+
const candidate = value;
|
|
25
|
+
if (isZodLike(candidate)) return true;
|
|
26
|
+
if (looksLikeJsonSchema(candidate)) return false;
|
|
27
|
+
return Object.values(candidate).some(isZodLike);
|
|
28
|
+
}
|
|
29
|
+
function addInstructionsToOutputSchema(tool, logger = log) {
|
|
30
|
+
const toolName = tool.name || 'unknown';
|
|
31
|
+
const original = tool.outputSchema;
|
|
32
|
+
if (!original || 'object' != typeof original) return tool;
|
|
33
|
+
if (!canDeclareOutputInstructions(original)) {
|
|
34
|
+
const schema = original;
|
|
35
|
+
logger(schema.properties && Object.prototype.hasOwnProperty.call(schema.properties, MCP_INSTRUCTIONS_KEY) ? `WARN: Tool "${toolName}" already declares '${MCP_INSTRUCTIONS_KEY}' in its output schema. Leaving it alone.` : `WARN: Tool "${toolName}" has a complex output schema (oneOf/allOf/anyOf/$ref). Skipping '${MCP_INSTRUCTIONS_KEY}' declaration; its session handle stays content-only.`);
|
|
36
|
+
return tool;
|
|
37
|
+
}
|
|
38
|
+
const modifiedTool = {
|
|
39
|
+
...tool
|
|
40
|
+
};
|
|
41
|
+
const outputSchema = JSON.parse(JSON.stringify(original));
|
|
42
|
+
if (!outputSchema.properties) outputSchema.properties = {};
|
|
43
|
+
outputSchema.properties[MCP_INSTRUCTIONS_KEY] = {
|
|
44
|
+
type: 'object',
|
|
45
|
+
description: INSTRUCTIONS_FIELD_DESCRIPTION,
|
|
46
|
+
properties: {
|
|
47
|
+
conversation_id: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
description: CONVERSATION_ID_FIELD_DESCRIPTION
|
|
50
|
+
},
|
|
51
|
+
instructions: {
|
|
52
|
+
type: 'string'
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
modifiedTool.outputSchema = outputSchema;
|
|
57
|
+
return modifiedTool;
|
|
58
|
+
}
|
|
59
|
+
function addInstructionsToOutputSchemas(tools, logger = log) {
|
|
60
|
+
return tools.map((tool)=>addInstructionsToOutputSchema(tool, logger));
|
|
61
|
+
}
|
|
62
|
+
const ECHO_INSTRUCTION = 'Send this conversation_id as an argument on every subsequent tool call in this conversation.';
|
|
63
|
+
function buildConversationInstructions(conversationId) {
|
|
64
|
+
return {
|
|
65
|
+
conversation_id: conversationId,
|
|
66
|
+
instructions: ECHO_INSTRUCTION
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function mirrorInstructionsIntoStructuredContent(result, conversationId) {
|
|
70
|
+
if (!result || 'object' != typeof result) return result;
|
|
71
|
+
const structuredContent = result.structuredContent;
|
|
72
|
+
if (!structuredContent || 'object' != typeof structuredContent || Array.isArray(structuredContent)) return result;
|
|
73
|
+
if (Object.prototype.hasOwnProperty.call(structuredContent, MCP_INSTRUCTIONS_KEY)) return result;
|
|
74
|
+
return {
|
|
75
|
+
...result,
|
|
76
|
+
structuredContent: {
|
|
77
|
+
...structuredContent,
|
|
78
|
+
[MCP_INSTRUCTIONS_KEY]: buildConversationInstructions(conversationId)
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export { MCP_INSTRUCTIONS_KEY, addInstructionsToOutputSchema, addInstructionsToOutputSchemas, buildConversationInstructions, canDeclareOutputInstructions, mirrorInstructionsIntoStructuredContent };
|
|
@@ -6,12 +6,33 @@ export declare function newSessionId(): string;
|
|
|
6
6
|
*/
|
|
7
7
|
export declare function deriveSessionIdFromMCPSession(mcpSessionId: string): string;
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* Derives the SDK session id from the agent's conversation handle. Deterministic
|
|
10
|
+
* and unsalted on purpose: two pods that never met must agree on the session, and
|
|
11
|
+
* the 2026-07-28 revision leaves them no shared state to agree through.
|
|
12
|
+
*
|
|
13
|
+
* Hashed rather than used verbatim so an MCP session can never collide with a
|
|
14
|
+
* Session Replay id — a bare uuidv7 would render a "View recording" button that
|
|
15
|
+
* resolves to nothing.
|
|
16
|
+
*
|
|
17
|
+
* Exported because this *is* the cross-SDK contract: posthog-python has to
|
|
18
|
+
* reproduce it byte for byte or the same conversation splits into two sessions
|
|
19
|
+
* depending on which SDK served the call.
|
|
13
20
|
*/
|
|
14
|
-
export declare function
|
|
21
|
+
export declare function deriveSessionIdFromConversation(conversationId: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Resolves the session id for a request. Three steps, first match wins:
|
|
24
|
+
*
|
|
25
|
+
* 1. the agent carried a `conversation_id` tool argument — 2026-07-28
|
|
26
|
+
* 2. the request carried a session id — 2025-11-25
|
|
27
|
+
* 3. nothing was carried, so reuse this instance's own id — stdio
|
|
28
|
+
*
|
|
29
|
+
* The split mirrors the two protocol revisions. 2026-07-28 removed protocol-level
|
|
30
|
+
* sessions, so the only thing that can carry a session across calls is the agent
|
|
31
|
+
* itself (1). 2025-11-25 kept the session on the connection, so the request
|
|
32
|
+
* carries it (2). 3 is for stdio, where neither applies — there is no
|
|
33
|
+
* HTTP request to carry anything, and no agent handle unless the host opted in.
|
|
34
|
+
*/
|
|
35
|
+
export declare function getSessionId(server: MCPServerLike, extra?: CompatibleRequestHandlerExtra, conversationId?: string): string;
|
|
15
36
|
/**
|
|
16
37
|
* Builds the session metadata stamped onto an event. The caller passes the
|
|
17
38
|
* session id resolved for *this* request so identity attribution can't be
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/extensions/session.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,6BAA6B,EAC7B,gBAAgB,EAChB,aAAa,EAEb,WAAW,EACZ,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/extensions/session.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,6BAA6B,EAC7B,gBAAgB,EAChB,aAAa,EAEb,WAAW,EACZ,MAAM,UAAU,CAAA;AAOjB,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAE1E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,+BAA+B,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,aAAa,EACrB,KAAK,CAAC,EAAE,6BAA6B,EACrC,cAAc,CAAC,EAAE,MAAM,GACtB,MAAM,CA0CR;AA4ED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,gBAAgB,GAAG,SAAS,EAClC,SAAS,CAAC,EAAE,MAAM,GACjB,WAAW,CAmCb"}
|