agentfootprint 12.0.1 → 12.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CAPABILITIES.md +1 -0
  2. package/dist/esm/mcp/callChecks.d.ts +160 -0
  3. package/dist/esm/mcp/callChecks.js +237 -0
  4. package/dist/esm/mcp/callChecks.js.map +1 -0
  5. package/dist/esm/mcp/index.d.ts +3 -1
  6. package/dist/esm/mcp/index.js +7 -0
  7. package/dist/esm/mcp/index.js.map +1 -1
  8. package/dist/esm/mcp/listingGenerations.d.ts +54 -0
  9. package/dist/esm/mcp/listingGenerations.js +75 -0
  10. package/dist/esm/mcp/listingGenerations.js.map +1 -0
  11. package/dist/esm/mcp/mcpClient.js +108 -23
  12. package/dist/esm/mcp/mcpClient.js.map +1 -1
  13. package/dist/esm/mcp/toolList.d.ts +90 -0
  14. package/dist/esm/mcp/toolList.js +123 -0
  15. package/dist/esm/mcp/toolList.js.map +1 -0
  16. package/dist/esm/mcp/types.d.ts +118 -2
  17. package/dist/esm/tool-providers/index.d.ts +2 -2
  18. package/dist/esm/tool-providers/index.js +8 -0
  19. package/dist/esm/tool-providers/index.js.map +1 -1
  20. package/dist/mcp/callChecks.js +247 -0
  21. package/dist/mcp/callChecks.js.map +1 -0
  22. package/dist/mcp/index.js +10 -1
  23. package/dist/mcp/index.js.map +1 -1
  24. package/dist/mcp/listingGenerations.js +80 -0
  25. package/dist/mcp/listingGenerations.js.map +1 -0
  26. package/dist/mcp/mcpClient.js +108 -23
  27. package/dist/mcp/mcpClient.js.map +1 -1
  28. package/dist/mcp/toolList.js +128 -0
  29. package/dist/mcp/toolList.js.map +1 -0
  30. package/dist/tool-providers/index.js +9 -1
  31. package/dist/tool-providers/index.js.map +1 -1
  32. package/dist/types/mcp/callChecks.d.ts +161 -0
  33. package/dist/types/mcp/callChecks.d.ts.map +1 -0
  34. package/dist/types/mcp/index.d.ts +3 -1
  35. package/dist/types/mcp/index.d.ts.map +1 -1
  36. package/dist/types/mcp/listingGenerations.d.ts +55 -0
  37. package/dist/types/mcp/listingGenerations.d.ts.map +1 -0
  38. package/dist/types/mcp/mcpClient.d.ts.map +1 -1
  39. package/dist/types/mcp/toolList.d.ts +91 -0
  40. package/dist/types/mcp/toolList.d.ts.map +1 -0
  41. package/dist/types/mcp/types.d.ts +118 -2
  42. package/dist/types/mcp/types.d.ts.map +1 -1
  43. package/dist/types/tool-providers/index.d.ts +2 -2
  44. package/dist/types/tool-providers/index.d.ts.map +1 -1
  45. package/package.json +1 -1
@@ -51,7 +51,7 @@ export { staticTools } from './staticTools.js';
51
51
  export { gatedTools } from './gatedTools.js';
52
52
  export { skillScopedTools, skillScopedToolsTarget, SKILL_SCOPED_TOOLS_ID_PREFIX, } from '../tools/skillScopedTools.js';
53
53
  export type { ToolProvider, ToolDispatchContext, ToolGatePredicate, } from '../tools/toolProvider.js';
54
- export { mcpClient, mcpServe, mockMcpClient, gatewayTransport, GatewayAuthorizationRequiredError, McpReplayUnavailableError, MCP_TOOL_EXTRAS_KEY, retryingFetch, } from '../mcp/index.js';
54
+ export { mcpClient, mcpServe, mockMcpClient, gatewayTransport, GatewayAuthorizationRequiredError, McpReplayUnavailableError, McpToolListIncompleteError, McpOutputUncheckedError, MCP_TOOL_EXTRAS_KEY, retryingFetch, } from '../mcp/index.js';
55
55
  export { agentCoreGatewayTransport, agentCoreGatewayUrl, gatewaySearchTool, hasGatewaySearch, AGENTCORE_GATEWAY_SEARCH_TOOL, AGENTCORE_POLICY_SESSION_HEADER, AGENTCORE_SIGV4_SERVICE, } from '../adapters/mcp/agentcore.js';
56
56
  export type { AgentCoreGatewayTransportOptions, AgentCoreGatewayUrlOptions, } from '../adapters/mcp/agentcore.js';
57
57
  export { agentCoreRuntimeMcpTransport, agentCoreRuntimeUrl, } from '../adapters/mcp/agentcoreRuntime/transport.js';
@@ -59,4 +59,4 @@ export { AGENTCORE_RUNTIME_SESSION_HEADER } from '../adapters/mcp/agentcoreRunti
59
59
  export type { AgentCoreRuntimeAuth, AgentCoreRuntimeBearerAuth, AgentCoreRuntimeMcpTransportOptions, AgentCoreRuntimeRetryOptions, AgentCoreRuntimeUrlOptions, } from '../adapters/mcp/agentcoreRuntime/transport.js';
60
60
  export type { AgentCoreRuntimeSession } from '../adapters/mcp/agentcoreRuntime/session.js';
61
61
  export type { AgentCoreRuntimeSdkClient, BedrockAgentCoreRuntimeSdkModule, } from '../adapters/mcp/agentcoreRuntime/invoke.js';
62
- export type { GatewayTransportOptions, McpCallToolResult, McpClient, McpClientOptions, McpReplayDeduplication, McpReplayKind, McpReplayOptions, McpReplayPolicy, McpReplayTool, McpReplayUnavailableReason, McpConnection, McpConnectionOptions, McpSdk, McpSdkClient, ThrottleFetch, McpTransport, McpStdioTransport, McpHttpTransport, McpGatewayTransport, MockMcpClientOptions, MockMcpTool, McpServeOptions, McpServeHandle, McpServeTransport, McpStdioServeTransport, McpHttpServeTransport, McpSdkServer, McpToolExtras, } from '../mcp/index.js';
62
+ export type { GatewayTransportOptions, McpCallToolResult, McpClient, McpClientOptions, McpReplayDeduplication, McpReplayKind, McpReplayOptions, McpReplayPolicy, McpReplayTool, McpReplayUnavailableReason, McpToolListIncompleteReason, McpConnection, McpConnectionOptions, McpJsonSchemaValidator, McpSdk, McpSdkClient, ThrottleFetch, McpTransport, McpStdioTransport, McpHttpTransport, McpGatewayTransport, MockMcpClientOptions, MockMcpTool, McpServeOptions, McpServeHandle, McpServeTransport, McpStdioServeTransport, McpHttpServeTransport, McpSdkServer, McpToolExtras, } from '../mcp/index.js';
@@ -61,6 +61,14 @@ export { mcpClient, mcpServe, mockMcpClient, gatewayTransport, GatewayAuthorizat
61
61
  // cannot honour (its requests unmatched, or its answers unseen) is refused
62
62
  // before it is sent, with this error and a typed reason.
63
63
  McpReplayUnavailableError,
64
+ // A server's tool list that could not be read to its end — past 100 requests,
65
+ // or a cursor it already sent — is refused with this error and a typed
66
+ // reason; what was listed travels on it, never as a list cut short.
67
+ McpToolListIncompleteError,
68
+ // A tool whose list the SDK no longer holds, and whose output this connection
69
+ // cannot check (no JSON Schema validator reachable), is refused, never
70
+ // answered unchecked.
71
+ McpOutputUncheckedError,
64
72
  // The `_meta` key agentfootprint's tool declarations travel under, in both
65
73
  // directions (9.71.0). Public so a server this library did not write can
66
74
  // speak it, and so a client can read a bag it received.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tool-providers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EACL,gBAAgB;AAChB,kFAAkF;AAClF,kFAAkF;AAClF,sBAAsB,EACtB,4BAA4B,GAC7B,MAAM,8BAA8B,CAAC;AAOtC,uEAAuE;AACvE,wEAAwE;AACxE,0CAA0C;AAC1C,OAAO,EACL,SAAS,EACT,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,iCAAiC;AACjC,0EAA0E;AAC1E,2EAA2E;AAC3E,yDAAyD;AACzD,yBAAyB;AACzB,2EAA2E;AAC3E,yEAAyE;AACzE,wDAAwD;AACxD,mBAAmB;AACnB,4EAA4E;AAC5E,8EAA8E;AAC9E,2EAA2E;AAC3E,iCAAiC;AACjC,aAAa,GACd,MAAM,iBAAiB,CAAC;AACzB,yEAAyE;AACzE,8EAA8E;AAC9E,8EAA8E;AAC9E,yCAAyC;AACzC,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,6BAA6B,EAC7B,+BAA+B,EAC/B,uBAAuB,GACxB,MAAM,8BAA8B,CAAC;AAKtC,8EAA8E;AAC9E,4EAA4E;AAC5E,iFAAiF;AACjF,mCAAmC;AACnC,OAAO,EACL,4BAA4B,EAC5B,mBAAmB,GACpB,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAAE,gCAAgC,EAAE,MAAM,6CAA6C,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tool-providers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EACL,gBAAgB;AAChB,kFAAkF;AAClF,kFAAkF;AAClF,sBAAsB,EACtB,4BAA4B,GAC7B,MAAM,8BAA8B,CAAC;AAOtC,uEAAuE;AACvE,wEAAwE;AACxE,0CAA0C;AAC1C,OAAO,EACL,SAAS,EACT,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,iCAAiC;AACjC,0EAA0E;AAC1E,2EAA2E;AAC3E,yDAAyD;AACzD,yBAAyB;AACzB,8EAA8E;AAC9E,uEAAuE;AACvE,oEAAoE;AACpE,0BAA0B;AAC1B,8EAA8E;AAC9E,uEAAuE;AACvE,sBAAsB;AACtB,uBAAuB;AACvB,2EAA2E;AAC3E,yEAAyE;AACzE,wDAAwD;AACxD,mBAAmB;AACnB,4EAA4E;AAC5E,8EAA8E;AAC9E,2EAA2E;AAC3E,iCAAiC;AACjC,aAAa,GACd,MAAM,iBAAiB,CAAC;AACzB,yEAAyE;AACzE,8EAA8E;AAC9E,8EAA8E;AAC9E,yCAAyC;AACzC,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,6BAA6B,EAC7B,+BAA+B,EAC/B,uBAAuB,GACxB,MAAM,8BAA8B,CAAC;AAKtC,8EAA8E;AAC9E,4EAA4E;AAC5E,iFAAiF;AACjF,mCAAmC;AACnC,OAAO,EACL,4BAA4B,EAC5B,mBAAmB,GACpB,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAAE,gCAAgC,EAAE,MAAM,6CAA6C,CAAC"}
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ /**
3
+ * callChecks — a listing never makes a callable tool lose its checks.
4
+ *
5
+ * Pattern: the SDK's three per-tool call checks, carried by every tool the
6
+ * library hands out, plus one typed refusal.
7
+ * Role: The MCP SDK's `Client` runs three checks inside `callTool`, and it
8
+ * keeps them only for the tools of its most recent `tools/list`
9
+ * answer (1.32: `cacheToolMetadata` replaces them on every answer):
10
+ *
11
+ * 1. a tool with `execution.taskSupport: 'required'` is refused before a
12
+ * plain `tools/call` is sent;
13
+ * 2. a tool with an `outputSchema` must answer with `structuredContent`,
14
+ * unless the answer is an error;
15
+ * 3. that `structuredContent` must match the schema.
16
+ *
17
+ * So a tool that declares any of them carries them itself, as the listing it
18
+ * came from declared them. The library leaves them to the SDK only for a tool
19
+ * of a ONE-page list whose answer is still the latest, with no list request
20
+ * out (`listingGenerations.ts`), both when the call starts and when its answer
21
+ * comes in: the one-page normal case, which runs exactly as it always did.
22
+ * Every other call runs the tool's own checks, at the SDK's moments — the first
23
+ * before sending, the other two once the answer is in, before anything reads it
24
+ * — and raises the SDK's error: its own `McpError` where this arm loaded the
25
+ * SDK, otherwise an error with the same name, code and message. Those calls
26
+ * are to a tool of a paged list, a tool whose list a later answer replaced (a
27
+ * refresh that failed or succeeded; a reference held across it), and any call
28
+ * made while a list is being read.
29
+ *
30
+ * All of this covers the lists the library reads (`tools()`, `refresh()`); a
31
+ * listing it does not send — the SDK client's own automatic refresh, a
32
+ * `listTools` call of the caller's — is not seen (`listingGenerations.ts`).
33
+ *
34
+ * The output checks need a JSON Schema validator equal to the client's: the
35
+ * SDK's default (`AjvJsonSchemaValidator`, loaded with the SDK on the Node
36
+ * loader arm), or the caller's `jsonSchemaValidator`. Where neither exists — an
37
+ * `sdk` or `connection` arm given none — an answer under these rules is never
38
+ * returned unchecked (`McpOutputUncheckedError`):
39
+ *
40
+ * - a call under the tool's own checks from the start, to a tool that
41
+ * declares an `outputSchema`, is refused before it is sent (`sent: false`);
42
+ * - a call that started under the SDK's checks is refused once its answer is
43
+ * in, unread, when the library sent a `tools/list` request while the call
44
+ * was out (`sent: true`): the SDK may have checked it against another list.
45
+ *
46
+ * A schema is compiled when a tool's own checks first need it, never before:
47
+ * the one-page normal case compiles nothing twice.
48
+ */
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.McpOutputUncheckedError = exports.checkOutput = exports.refuseRequiredTask = exports.outputUnchecked = exports.checksFor = exports.validatorOption = exports.callCheckKit = void 0;
51
+ /** JSON-RPC's codes for the two errors the SDK raises here. */
52
+ const INVALID_REQUEST = -32600;
53
+ const INVALID_PARAMS = -32602;
54
+ /**
55
+ * The kit for one connection, named `server`, whose list requests `listings`
56
+ * counts (`listingsOf(connection)`, shared by every client over it). `given` is
57
+ * the caller's `jsonSchemaValidator`; `loaded` holds what the library loaded
58
+ * with the SDK on the Node loader arm — the SDK's default validator class and
59
+ * its `McpError` — each `undefined` where it is not a constructor (another arm,
60
+ * or an SDK without it).
61
+ */
62
+ function callCheckKit(server, listings, given, loaded = {}) {
63
+ const Default = typeof loaded.Validator === 'function' ? loaded.Validator : undefined;
64
+ let created;
65
+ return {
66
+ server,
67
+ validator: () => {
68
+ if (given !== undefined)
69
+ return given;
70
+ if (Default === undefined)
71
+ return undefined;
72
+ created ??= new Default();
73
+ return created;
74
+ },
75
+ ...(typeof loaded.McpError === 'function' && { McpError: loaded.McpError }),
76
+ listings,
77
+ };
78
+ }
79
+ exports.callCheckKit = callCheckKit;
80
+ /**
81
+ * `value` as a validator, or `undefined` when it was not given. Refused by name
82
+ * when it is something else, before anything connects.
83
+ */
84
+ function validatorOption(value, at) {
85
+ if (value === undefined)
86
+ return undefined;
87
+ const getValidator = value !== null && (typeof value === 'object' || typeof value === 'function')
88
+ ? value.getValidator
89
+ : undefined;
90
+ if (typeof getValidator === 'function')
91
+ return value;
92
+ throw new TypeError(`${at}: \`jsonSchemaValidator\` has no \`getValidator\` method. Pass a JSON Schema ` +
93
+ "validator as the MCP SDK's `Client` takes one: `new AjvJsonSchemaValidator()` from " +
94
+ "'@modelcontextprotocol/sdk/validation/ajv', or the one your client uses.");
95
+ }
96
+ exports.validatorOption = validatorOption;
97
+ /**
98
+ * The checks `listed` carries — `undefined` when it declares none. `generation`
99
+ * is the one answer its list was read in, when it was read in one
100
+ * (`toolList.ts` · `ToolListing`); a paged list's tools have none, so the SDK
101
+ * never holds all of them.
102
+ */
103
+ function checksFor(listed, kit, generation) {
104
+ const schema = ownObject(listed, 'outputSchema');
105
+ const taskRequired = ownValue(ownObject(listed, 'execution'), 'taskSupport') === 'required';
106
+ if (schema === undefined && !taskRequired)
107
+ return undefined;
108
+ let compiled;
109
+ return {
110
+ server: kit.server,
111
+ tool: listed.name,
112
+ taskRequired,
113
+ outputDeclared: schema !== undefined,
114
+ heldBySdk: () => generation !== undefined && kit.listings.isLatest(generation),
115
+ validate: () => {
116
+ if (schema !== undefined)
117
+ compiled ??= kit.validator()?.getValidator(schema);
118
+ return compiled;
119
+ },
120
+ fail: (code, message) => kit.McpError !== undefined
121
+ ? new kit.McpError(code, message)
122
+ : new McpErrorShape(code, message),
123
+ };
124
+ }
125
+ exports.checksFor = checksFor;
126
+ /**
127
+ * True when this call's output cannot be checked here: it runs under the
128
+ * tool's own checks, the tool declares an `outputSchema`, no validator is
129
+ * reachable, and it is not refused as a required task anyway (that refusal is
130
+ * the SDK's, and comes first). The call is then refused before it is sent.
131
+ */
132
+ function outputUnchecked(checks) {
133
+ return checks.outputDeclared && !checks.taskRequired && checks.validate() === undefined;
134
+ }
135
+ exports.outputUnchecked = outputUnchecked;
136
+ /** The SDK's task refusal, word for word, before anything is sent. */
137
+ function refuseRequiredTask(checks) {
138
+ if (!checks.taskRequired)
139
+ return;
140
+ throw checks.fail(INVALID_REQUEST, `Tool "${checks.tool}" requires task-based execution. ` +
141
+ 'Use client.experimental.tasks.callToolStream() instead.');
142
+ }
143
+ exports.refuseRequiredTask = refuseRequiredTask;
144
+ /**
145
+ * The SDK's two output checks, in its order and with its sentences, once the
146
+ * answer is in and before anything reads it. An answer no validator here can
147
+ * check is never returned: it is refused, unread, as sent.
148
+ */
149
+ function checkOutput(checks, result) {
150
+ if (!checks.outputDeclared)
151
+ return;
152
+ const validate = checks.validate();
153
+ if (validate === undefined)
154
+ throw new McpOutputUncheckedError(checks.server, checks.tool, true);
155
+ const structured = ownValue(result, 'structuredContent');
156
+ if (!structured && !ownValue(result, 'isError')) {
157
+ throw checks.fail(INVALID_REQUEST, `Tool ${checks.tool} has an output schema but did not return structured content`);
158
+ }
159
+ if (!structured)
160
+ return;
161
+ let mismatch;
162
+ try {
163
+ const verdict = validate(structured);
164
+ if (!verdict.valid) {
165
+ mismatch = `Structured content does not match the tool's output schema: ${verdict.errorMessage}`;
166
+ }
167
+ }
168
+ catch (error) {
169
+ throw checks.fail(INVALID_PARAMS, `Failed to validate structured content: ${error instanceof Error ? error.message : String(error)}`);
170
+ }
171
+ if (mismatch !== undefined)
172
+ throw checks.fail(INVALID_PARAMS, mismatch);
173
+ }
174
+ exports.checkOutput = checkOutput;
175
+ /**
176
+ * The SDK's `McpError`, for an arm that cannot reach the class itself (`sdk`,
177
+ * `connection`): the same name, `code`, `data` and message, so a caller that
178
+ * branches on those handles it as it handles the SDK's.
179
+ */
180
+ class McpErrorShape extends Error {
181
+ code;
182
+ data;
183
+ constructor(code, message) {
184
+ super(`MCP error ${code}: ${message}`);
185
+ this.code = code;
186
+ this.data = undefined;
187
+ this.name = 'McpError';
188
+ }
189
+ }
190
+ /** What the refusal says happened, refused before sending. */
191
+ const NOT_SENT = 'was not sent. The tool declares an outputSchema, the MCP SDK no longer holds the checks of ' +
192
+ 'the tool list it came from (that list was paged, a later list replaced it, or a list is ' +
193
+ 'being read)';
194
+ /** What the refusal says happened, refused once the answer was in. */
195
+ const SENT_UNREAD = 'was sent, and its answer is not returned. The tool declares an outputSchema, a tool list ' +
196
+ 'was read while the call was out (so the MCP SDK may not have checked the answer against ' +
197
+ "this tool's schema)";
198
+ /**
199
+ * A tool call whose output this connection cannot check, refused. The tool
200
+ * declares an `outputSchema`, the MCP SDK no longer holds the checks of the
201
+ * tool list it came from, and this connection has no JSON Schema validator to
202
+ * check it with — so its answer is never returned:
203
+ *
204
+ * - `sent: false` — refused before it was sent: nothing reached the server.
205
+ * The tool came from a paged list, from a list a later answer replaced, or
206
+ * the call was made while a list was being read.
207
+ * - `sent: true` — the call was sent, and a `tools/list` request went out
208
+ * before its answer came in: the SDK may have checked that answer against
209
+ * the other list, so it is refused unread. The tool may have run.
210
+ *
211
+ * Pass `jsonSchemaValidator` — the validator your client uses — to `mcpClient`.
212
+ * Names the server and the tool, never an argument or an answer.
213
+ */
214
+ class McpOutputUncheckedError extends Error {
215
+ server;
216
+ tool;
217
+ sent;
218
+ code = 'ERR_MCP_OUTPUT_UNCHECKED';
219
+ reason = 'output-unchecked';
220
+ constructor(server, tool,
221
+ /** Whether the request left the client: `false` when the refusal came before it. */
222
+ sent) {
223
+ super(`mcpClient[${server}]: the call to '${tool}' ${sent ? SENT_UNREAD : NOT_SENT}, and this ` +
224
+ 'connection has no JSON Schema validator to check its output with. Pass ' +
225
+ '`jsonSchemaValidator` to mcpClient: the validator your client uses.');
226
+ this.server = server;
227
+ this.tool = tool;
228
+ this.sent = sent;
229
+ this.name = 'McpOutputUncheckedError';
230
+ }
231
+ }
232
+ exports.McpOutputUncheckedError = McpOutputUncheckedError;
233
+ /** An own data property, never an accessor's result; `undefined` otherwise. */
234
+ function ownValue(value, key) {
235
+ if (value === null || typeof value !== 'object')
236
+ return undefined;
237
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
238
+ return descriptor !== undefined && 'value' in descriptor ? descriptor.value : undefined;
239
+ }
240
+ /** An own data property that is a plain object (not an array); `undefined` otherwise. */
241
+ function ownObject(value, key) {
242
+ const found = ownValue(value, key);
243
+ return found !== null && typeof found === 'object' && !Array.isArray(found)
244
+ ? found
245
+ : undefined;
246
+ }
247
+ //# sourceMappingURL=callChecks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callChecks.js","sourceRoot":"","sources":["../../src/mcp/callChecks.ts"],"names":[],"mappings":"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"}
package/dist/mcp/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.McpReplayUnavailableError = exports.retryingFetch = exports.GatewayAuthorizationRequiredError = exports.gatewayTransport = exports.mockMcpClient = exports.MCP_TOOL_EXTRAS_KEY = exports.mcpServe = exports.mcpClient = void 0;
3
+ exports.McpOutputUncheckedError = exports.McpToolListIncompleteError = exports.McpReplayUnavailableError = exports.retryingFetch = exports.GatewayAuthorizationRequiredError = exports.gatewayTransport = exports.mockMcpClient = exports.MCP_TOOL_EXTRAS_KEY = exports.mcpServe = exports.mcpClient = void 0;
4
4
  /**
5
5
  * MCP — Model Context Protocol, both directions. `mcpClient` connects to
6
6
  * someone else's MCP server and registers its tools on your Agent;
@@ -29,4 +29,13 @@ Object.defineProperty(exports, "retryingFetch", { enumerable: true, get: functio
29
29
  // before it is sent, with a typed reason.
30
30
  var replayUnavailable_js_1 = require("./replayUnavailable.js");
31
31
  Object.defineProperty(exports, "McpReplayUnavailableError", { enumerable: true, get: function () { return replayUnavailable_js_1.McpReplayUnavailableError; } });
32
+ // A tool list that could not be read to its end (past 100 requests, or a cursor
33
+ // sent twice) is refused with this error and a typed reason, never cut short.
34
+ var toolList_js_1 = require("./toolList.js");
35
+ Object.defineProperty(exports, "McpToolListIncompleteError", { enumerable: true, get: function () { return toolList_js_1.McpToolListIncompleteError; } });
36
+ // A tool whose list the SDK no longer holds, and whose output this connection
37
+ // cannot check — no JSON Schema validator reachable — is refused, never answered
38
+ // unchecked.
39
+ var callChecks_js_1 = require("./callChecks.js");
40
+ Object.defineProperty(exports, "McpOutputUncheckedError", { enumerable: true, get: function () { return callChecks_js_1.McpOutputUncheckedError; } });
32
41
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";;;AAAA;;;;;GAKG;AACH,+CAA2C;AAAlC,yGAAA,SAAS,OAAA;AAClB,6CAAyC;AAAhC,uGAAA,QAAQ,OAAA;AACjB,8EAA8E;AAC9E,0EAA0E;AAC1E,iDAA0E;AAAjE,oHAAA,mBAAmB,OAAA;AAC5B,uDAAgG;AAAvF,iHAAA,aAAa,OAAA;AACtB,6DAI+B;AAH7B,uHAAA,gBAAgB,OAAA;AAChB,wIAAA,iCAAiC,OAAA;AAGnC,6EAA6E;AAC7E,4EAA4E;AAC5E,sEAAsE;AACtE,uDAAmD;AAA1C,iHAAA,aAAa,OAAA;AAgBtB,2EAA2E;AAC3E,0CAA0C;AAC1C,+DAAoG;AAA3F,iIAAA,yBAAyB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";;;AAAA;;;;;GAKG;AACH,+CAA2C;AAAlC,yGAAA,SAAS,OAAA;AAClB,6CAAyC;AAAhC,uGAAA,QAAQ,OAAA;AACjB,8EAA8E;AAC9E,0EAA0E;AAC1E,iDAA0E;AAAjE,oHAAA,mBAAmB,OAAA;AAC5B,uDAAgG;AAAvF,iHAAA,aAAa,OAAA;AACtB,6DAI+B;AAH7B,uHAAA,gBAAgB,OAAA;AAChB,wIAAA,iCAAiC,OAAA;AAGnC,6EAA6E;AAC7E,4EAA4E;AAC5E,sEAAsE;AACtE,uDAAmD;AAA1C,iHAAA,aAAa,OAAA;AAgBtB,2EAA2E;AAC3E,0CAA0C;AAC1C,+DAAoG;AAA3F,iIAAA,yBAAyB,OAAA;AAClC,gFAAgF;AAChF,8EAA8E;AAC9E,6CAA6F;AAApF,yHAAA,0BAA0B,OAAA;AACnC,8EAA8E;AAC9E,iFAAiF;AACjF,aAAa;AACb,iDAA0D;AAAjD,wHAAA,uBAAuB,OAAA"}
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ /**
3
+ * listingGenerations — which `tools/list` answer the MCP SDK's `Client` holds
4
+ * its per-tool call checks for, as far as one connection can tell.
5
+ *
6
+ * Pattern: a count of the list requests one connection sent and settled.
7
+ * Role: The SDK's `Client` keeps its per-tool call checks (`callChecks.ts`)
8
+ * for the tools of the last `tools/list` answer it received: every
9
+ * answer replaces them. A request that FAILED can replace them too: the
10
+ * SDK caches an answer before it returns it, and caching can throw after
11
+ * the old checks are cleared (1.32: an `outputSchema` its validator
12
+ * cannot compile). The library cannot see inside the SDK, but it sends
13
+ * every list request it makes. So it counts them:
14
+ *
15
+ * - a request is OUT from the moment it is sent until it settles, because the
16
+ * SDK replaces its checks before the library sees the answer;
17
+ * - each request that settles, answered or failed, starts a new GENERATION.
18
+ *
19
+ * The SDK holds the checks of a listing's one answer only while the generation
20
+ * that answer settled at is the latest and no request is out. The SDK keeps one
21
+ * set of checks per client, so there is one count per connection object: every
22
+ * `mcpClient` over the same client shares it (`listingsOf`). A `tools/list`
23
+ * request the library did not send — your own code calling `listTools` on a
24
+ * client passed as `connection`, or that client's own automatic refresh
25
+ * (`listChanged.tools` with `autoRefresh`) — is not counted: the library cannot
26
+ * see it, and a tool it handed out goes on leaving its checks to an SDK that no
27
+ * longer holds them. That limit is documented, and pinned by
28
+ * `test/mcp/listingKeepsChecks.limits.test.ts`.
29
+ */
30
+ Object.defineProperty(exports, "__esModule", { value: true });
31
+ exports.listingsOf = exports.ListingGenerations = void 0;
32
+ /** The `tools/list` requests one connection has out, and the generations they settled at. */
33
+ class ListingGenerations {
34
+ settled = 0;
35
+ out = 0;
36
+ /**
37
+ * Send one `tools/list` request with `send`, counted as out from the moment it
38
+ * is sent until it settles. Settling starts a new generation, answered or not.
39
+ *
40
+ * @throws whatever `send` rejects with, as it rejected
41
+ */
42
+ async ask(send) {
43
+ this.out += 1;
44
+ try {
45
+ const answer = await send();
46
+ return { answer, generation: this.settle() };
47
+ }
48
+ catch (error) {
49
+ this.settle();
50
+ throw error;
51
+ }
52
+ }
53
+ /** True while `generation` is the latest settled one and no request is out. */
54
+ isLatest(generation) {
55
+ return this.out === 0 && this.settled === generation;
56
+ }
57
+ settle() {
58
+ this.out -= 1;
59
+ this.settled += 1;
60
+ return this.settled;
61
+ }
62
+ }
63
+ exports.ListingGenerations = ListingGenerations;
64
+ /** One count per connection object, held only as long as the connection is. */
65
+ const BY_CONNECTION = new WeakMap();
66
+ /**
67
+ * The count for `connection`: the same one for every `mcpClient` that adapts
68
+ * the same client, because the SDK keeps one set of checks per client and a
69
+ * listing by any of them replaces it.
70
+ */
71
+ function listingsOf(connection) {
72
+ let listings = BY_CONNECTION.get(connection);
73
+ if (listings === undefined) {
74
+ listings = new ListingGenerations();
75
+ BY_CONNECTION.set(connection, listings);
76
+ }
77
+ return listings;
78
+ }
79
+ exports.listingsOf = listingsOf;
80
+ //# sourceMappingURL=listingGenerations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listingGenerations.js","sourceRoot":"","sources":["../../src/mcp/listingGenerations.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;;;AAQH,6FAA6F;AAC7F,MAAa,kBAAkB;IACrB,OAAO,GAAG,CAAC,CAAC;IACZ,GAAG,GAAG,CAAC,CAAC;IAEhB;;;;;OAKG;IACH,KAAK,CAAC,GAAG,CAAI,IAAsB;QACjC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QACd,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,EAAE,CAAC;YAC5B,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QAC/C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,+EAA+E;IAC/E,QAAQ,CAAC,UAAkB;QACzB,OAAO,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,KAAK,UAAU,CAAC;IACvD,CAAC;IAEO,MAAM;QACZ,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QACd,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;CACF;AA/BD,gDA+BC;AAED,+EAA+E;AAC/E,MAAM,aAAa,GAAG,IAAI,OAAO,EAA8B,CAAC;AAEhE;;;;GAIG;AACH,SAAgB,UAAU,CAAC,UAAkB;IAC3C,IAAI,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC7C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,QAAQ,GAAG,IAAI,kBAAkB,EAAE,CAAC;QACpC,aAAa,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAPD,gCAOC"}
@@ -48,6 +48,9 @@ exports.mcpClient = void 0;
48
48
  const footprintjs_1 = require("footprintjs");
49
49
  const toolExtras_js_1 = require("./toolExtras.js");
50
50
  const toolResult_js_1 = require("./toolResult.js");
51
+ const toolList_js_1 = require("./toolList.js");
52
+ const listingGenerations_js_1 = require("./listingGenerations.js");
53
+ const callChecks_js_1 = require("./callChecks.js");
51
54
  const gatewayTransport_js_1 = require("./gatewayTransport.js");
52
55
  const throttleRetry_js_1 = require("./throttleRetry.js");
53
56
  const requestLedger_js_1 = require("./requestLedger.js");
@@ -97,7 +100,10 @@ async function mcpClient(opts) {
97
100
  // checked once, here, before anything connects (`replay.ts`).
98
101
  const replay = (0, replay_js_1.replayDeclarationOf)(opts.replay, `mcpClient[${name}]`);
99
102
  const strict = replay.policy === 'app-authorized';
100
- const { connection, ledger } = await openConnection(opts, replay, name);
103
+ // The validator a tool's own output checks use (`callChecks.ts`), refused by
104
+ // name now when it is not one.
105
+ const validator = (0, callChecks_js_1.validatorOption)(opts.jsonSchemaValidator, `mcpClient[${name}]`);
106
+ const { connection, ledger, kit } = await openConnection(opts, replay, name, validator);
101
107
  const unlisted = new Set();
102
108
  // Tool cache so consumers calling `.tools()` more than once don't
103
109
  // hammer the server. `.refresh()` invalidates it.
@@ -109,13 +115,24 @@ async function mcpClient(opts) {
109
115
  }
110
116
  };
111
117
  const buildTools = async () => {
112
- // `signal` rides in the SDK's THIRD argument (RequestOptions), not in
113
- // the request params — see `wrapMcpTool`.
114
- const listed = opts.signal
115
- ? await connection.listTools(undefined, { signal: opts.signal })
116
- : await connection.listTools();
117
- noteUnlisted(replay, listed.tools, name, unlisted);
118
- return listed.tools.map((t) => wrapMcpTool(name, connection, t, opts.signal, resultMode, ledger, strict));
118
+ // Every page of the server's list (`toolList.ts`). `signal` rides each
119
+ // page in the SDK's trailing options argument, never in the params — see
120
+ // `wrapMcpTool`.
121
+ // Each page request is counted in this connection's listings, whatever it
122
+ // settles to (`listingGenerations.ts`).
123
+ const listing = await (0, toolList_js_1.listEveryPage)(connection, opts.signal, kit.listings);
124
+ // A list that did not end cannot say which entries name nothing.
125
+ if (listing.incomplete === undefined)
126
+ noteUnlisted(replay, listing.tools, name, unlisted);
127
+ // Every tool carries the checks of the list it came from (`callChecks.ts`):
128
+ // the SDK's `Client` holds them only while a one-page list's answer is its
129
+ // latest, and only then are they left to it.
130
+ const tools = listing.tools.map((t) => wrapMcpTool(name, connection, t, opts.signal, resultMode, ledger, strict, (0, callChecks_js_1.checksFor)(t, kit, listing.generation)));
131
+ // Never a list cut short: what was listed travels on the refusal instead.
132
+ if (listing.incomplete !== undefined) {
133
+ throw new toolList_js_1.McpToolListIncompleteError(listing.incomplete, name, listing.pages, tools);
134
+ }
135
+ return tools;
119
136
  };
120
137
  return {
121
138
  name,
@@ -155,31 +172,79 @@ exports.mcpClient = mcpClient;
155
172
  * A call with neither `connection` nor `sdk` reaches `resolveClient` with two
156
173
  * `undefined` arguments and takes the identical path it always took.
157
174
  */
158
- async function openConnection(opts, replay, name) {
159
- if (opts.connection !== undefined)
160
- return { connection: opts.connection };
161
- if (opts._client !== undefined)
162
- return { connection: opts._client };
175
+ async function openConnection(opts, replay, name, validator) {
176
+ // A client built elsewhere: the library loads nothing, so only the caller's
177
+ // validator can check a tool's output itself, and errors take the SDK's shape.
178
+ // Its list requests are counted per client: every mcpClient over it shares one
179
+ // count, as they share the SDK's one set of checks (`listingGenerations.ts`).
180
+ if (opts.connection !== undefined) {
181
+ const kit = (0, callChecks_js_1.callCheckKit)(name, (0, listingGenerations_js_1.listingsOf)(opts.connection), validator);
182
+ return { connection: opts.connection, kit };
183
+ }
184
+ if (opts._client !== undefined) {
185
+ return {
186
+ connection: opts._client,
187
+ kit: (0, callChecks_js_1.callCheckKit)(name, (0, listingGenerations_js_1.listingsOf)(opts._client), validator),
188
+ };
189
+ }
163
190
  // `refuseConflictingOptions` has already refused the case where this is
164
191
  // absent, so the assertion documents that guarantee rather than assuming it.
165
192
  const transport = opts.transport;
166
- return resolveClient(transport, opts.clientInfo, opts.signal, opts.retryOnThrottle, opts.sdk, replay, name);
193
+ return resolveClient(transport, opts.clientInfo, opts.signal, opts.retryOnThrottle, opts.sdk, replay, name, validator);
167
194
  }
168
195
  // ─── SDK construction (lazy require, unless the caller supplied it) ─
169
- async function resolveClient(transport, clientInfo, signal, retryOnThrottle, sdk, replay, name = 'mcp') {
196
+ async function resolveClient(transport, clientInfo, signal, retryOnThrottle, sdk, replay, name = 'mcp', validator) {
170
197
  if (sdk !== undefined)
171
198
  assertSdk(sdk, transport.transport);
172
199
  const mod = sdk ?? loadClientModule();
173
- const client = new mod.Client(clientInfo ?? DEFAULT_CLIENT_INFO, {
174
- capabilities: {},
175
- });
200
+ // A caller's validator is the client's too, so one validator checks every
201
+ // page; without one, the options are exactly what they always were.
202
+ const options = validator === undefined
203
+ ? { capabilities: {} }
204
+ : { capabilities: {}, jsonSchemaValidator: validator };
205
+ const client = new mod.Client(clientInfo ?? DEFAULT_CLIENT_INFO, options);
176
206
  const built = await buildTransport(transport, retryOnThrottle, sdk, replay, name);
207
+ // With the SDK loaded here, so is what a tool's own checks run with: its
208
+ // default validator and its own error class. With `sdk`, the loader is never
209
+ // touched (`callChecks.ts`).
210
+ const kit = sdk === undefined
211
+ ? (0, callChecks_js_1.callCheckKit)(name, (0, listingGenerations_js_1.listingsOf)(client), validator, loadCheckModules(validator))
212
+ : (0, callChecks_js_1.callCheckKit)(name, (0, listingGenerations_js_1.listingsOf)(client), validator);
177
213
  // Same rule as callTool: options ride beside the transport, never inside it.
178
214
  if (signal)
179
215
  await client.connect(built.transport, { signal });
180
216
  else
181
217
  await client.connect(built.transport);
182
- return { connection: client, ...(built.ledger !== undefined && { ledger: built.ledger }) };
218
+ return {
219
+ connection: client,
220
+ kit,
221
+ ...(built.ledger !== undefined && { ledger: built.ledger }),
222
+ };
223
+ }
224
+ /**
225
+ * What the SDK's `Client` checks a tool's call with, loaded with the SDK on the
226
+ * Node loader path (the bundling law: when the client is built, never at a
227
+ * later tool call): its own `McpError`, and — unless the caller gave a
228
+ * validator — its default validator, `AjvJsonSchemaValidator`. Both modules are
229
+ * the ones `client/index.js` already loaded. An SDK without one of them (older
230
+ * than its public validator subpath) leaves that slot empty: errors then take
231
+ * the SDK's shape, and an output no validator here can check is never returned.
232
+ */
233
+ function loadCheckModules(validator) {
234
+ const McpError = loadOptional(() => (0, lazyRequire_js_1.lazyRequire)('@modelcontextprotocol/sdk/types.js'))?.McpError;
235
+ const Validator = validator === undefined
236
+ ? loadOptional(() => (0, lazyRequire_js_1.lazyRequire)('@modelcontextprotocol/sdk/validation/ajv'))?.AjvJsonSchemaValidator
237
+ : undefined;
238
+ return { McpError, Validator };
239
+ }
240
+ /** A module this SDK may not have: `undefined` when it cannot be loaded. */
241
+ function loadOptional(load) {
242
+ try {
243
+ return load();
244
+ }
245
+ catch {
246
+ return undefined;
247
+ }
183
248
  }
184
249
  /**
185
250
  * An injected `sdk` is checked before anything is built, so a wrong import (a
@@ -358,7 +423,7 @@ function noteUnlisted(replay, listed, server, noted) {
358
423
  }
359
424
  }
360
425
  // ─── Tool wrapping ─────────────────────────────────────────────────
361
- function wrapMcpTool(serverName, connection, mcp, signal, resultMode, ledger, strict = false) {
426
+ function wrapMcpTool(serverName, connection, mcp, signal, resultMode, ledger, strict = false, checks) {
362
427
  const tool = {
363
428
  schema: {
364
429
  name: mcp.name,
@@ -420,12 +485,32 @@ function wrapMcpTool(serverName, connection, mcp, signal, resultMode, ledger, st
420
485
  if (unavailable !== undefined) {
421
486
  throw new replayUnavailable_js_1.McpReplayUnavailableError(unavailable, serverName, mcp.name);
422
487
  }
488
+ // Whose checks this call runs under, decided before anything is sent
489
+ // (`callChecks.ts`): the SDK's `Client` holds them while this tool's
490
+ // one-page list is still its latest answer and no list request is out;
491
+ // otherwise the tool's own checks run. An output they cannot check is
492
+ // refused here, unsent.
493
+ const own = checks !== undefined && !checks.heldBySdk();
494
+ if (own && (0, callChecks_js_1.outputUnchecked)(checks)) {
495
+ throw new callChecks_js_1.McpOutputUncheckedError(serverName, mcp.name, false);
496
+ }
423
497
  const call = (0, callLifetime_js_1.openCall)(params, ctx, signal, ledger, serverName);
424
498
  let verdict = 'failed';
425
499
  try {
426
- const result = call.signal
427
- ? await connection.callTool(call.params, undefined, { signal: call.signal })
428
- : await connection.callTool(call.params);
500
+ const send = () => call.signal
501
+ ? connection.callTool(call.params, undefined, { signal: call.signal })
502
+ : connection.callTool(call.params);
503
+ // The tool's own checks run where the SDK's `callTool` runs them, so a
504
+ // call fails as it would with the SDK holding them: the task refusal
505
+ // before sending, the output checks once the answer is in.
506
+ if (own)
507
+ (0, callChecks_js_1.refuseRequiredTask)(checks);
508
+ const result = await send();
509
+ // A list request that went out while the call was out may have replaced
510
+ // the SDK's checks before it read the answer, so the tool's own checks
511
+ // read it too.
512
+ if (checks !== undefined && (own || !checks.heldBySdk()))
513
+ (0, callChecks_js_1.checkOutput)(checks, result);
429
514
  verdict = (0, toolResult_js_1.isToolError)(result) ? 'tool-error' : 'completed';
430
515
  return (0, toolResult_js_1.readToolResult)(result, mcp.name, serverName, resultMode);
431
516
  }