@nylorun/runtime 0.25.0-beta → 0.27.0-beta
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/CHANGELOG.md +169 -0
- package/README.md +26 -16
- package/dist/adapters/execution/restate.d.ts +15 -18
- package/dist/adapters/execution/restate.js +31 -27
- package/dist/adapters/replication/pgoutput.d.ts +1 -1
- package/dist/adapters/replication/pgoutput.js +3 -2
- package/dist/adapters/sandbox/local.js +8 -5
- package/dist/adapters/sandbox/virtual.js +2 -2
- package/dist/adapters/streams/s2.d.ts +1 -1
- package/dist/adapters/streams/s2.js +18 -14
- package/dist/api/a2a/card.js +1 -1
- package/dist/api/a2a/endpoint.js +3 -4
- package/dist/api/a2a/protocol.js +4 -0
- package/dist/api/a2a/routes.d.ts +2 -6
- package/dist/api/a2a/routes.js +12 -18
- package/dist/api/ag-ui/endpoint.js +2 -3
- package/dist/api/ag-ui/routes.js +4 -4
- package/dist/api/components.d.ts +326 -45
- package/dist/api/components.js +5 -1
- package/dist/api/gate/keys.js +7 -4
- package/dist/api/gate/routes.d.ts +6 -2
- package/dist/api/gate/routes.js +15 -11
- package/dist/api/http/app.d.ts +2 -2
- package/dist/api/http/app.js +2 -2
- package/dist/api/http/body.d.ts +15 -3
- package/dist/api/http/body.js +15 -7
- package/dist/api/http/define.d.ts +1 -1
- package/dist/api/http/respond.js +7 -3
- package/dist/api/http/routes/access.js +5 -4
- package/dist/api/http/routes/artifacts.js +19 -7
- package/dist/api/http/routes/coverage.js +1 -1
- package/dist/api/http/routes/environment-manifests.js +1 -1
- package/dist/api/http/routes/environments.js +6 -6
- package/dist/api/http/routes/files.d.ts +2 -1
- package/dist/api/http/routes/files.js +31 -5
- package/dist/api/http/routes/mcp.js +1 -1
- package/dist/api/http/routes/reads.d.ts +35 -0
- package/dist/api/http/routes/reads.js +50 -22
- package/dist/api/http/routes/sessions.js +12 -11
- package/dist/api/http/routes/tenant.d.ts +2 -2
- package/dist/api/http/routes/tenant.js +69 -16
- package/dist/api/inbound-files.d.ts +3 -9
- package/dist/api/inbound-files.js +18 -24
- package/dist/api/openapi.js +6 -2
- package/dist/artifacts/disk.d.ts +0 -4
- package/dist/artifacts/disk.js +77 -73
- package/dist/artifacts/files.d.ts +13 -4
- package/dist/artifacts/files.js +27 -9
- package/dist/artifacts/folder-upload.d.ts +75 -0
- package/dist/artifacts/folder-upload.js +263 -0
- package/dist/artifacts/folders.d.ts +14 -8
- package/dist/artifacts/folders.js +19 -11
- package/dist/artifacts/links.js +7 -6
- package/dist/artifacts/parts.d.ts +9 -14
- package/dist/artifacts/parts.js +11 -9
- package/dist/artifacts/pipe.d.ts +15 -0
- package/dist/artifacts/pipe.js +71 -0
- package/dist/artifacts/quota.d.ts +36 -0
- package/dist/artifacts/quota.js +44 -0
- package/dist/artifacts/service.d.ts +25 -1
- package/dist/artifacts/service.js +70 -25
- package/dist/blob/fs.js +2 -1
- package/dist/blob/s3.d.ts +6 -2
- package/dist/blob/s3.js +9 -7
- package/dist/blob/types.d.ts +1 -1
- package/dist/blob/types.js +1 -1
- package/dist/core/limits.d.ts +11 -35
- package/dist/core/limits.js +5 -73
- package/dist/core/sandbox-routes.js +0 -1
- package/dist/core/turn-manifest.d.ts +1 -2
- package/dist/core/turn-manifest.js +4 -28
- package/dist/execution/memory.d.ts +8 -5
- package/dist/execution/memory.js +8 -6
- package/dist/execution/types.d.ts +1 -6
- package/dist/execution/types.js +0 -5
- package/dist/gates/contract.d.ts +3 -2
- package/dist/gates/contract.js +1 -1
- package/dist/gates/egress-credentials.js +4 -3
- package/dist/gates/egress.d.ts +9 -9
- package/dist/gates/egress.js +19 -19
- package/dist/gates/fixture-model.js +13 -2
- package/dist/gates/handler.d.ts +1 -3
- package/dist/gates/handler.js +4 -3
- package/dist/gates/http-client.d.ts +4 -3
- package/dist/gates/http-client.js +5 -7
- package/dist/gates/http-tool.d.ts +24 -4
- package/dist/gates/http-tool.js +53 -17
- package/dist/gates/in-process.d.ts +2 -1
- package/dist/gates/in-process.js +17 -9
- package/dist/gates/inflight.d.ts +2 -2
- package/dist/gates/inflight.js +3 -2
- package/dist/gates/journal.js +5 -7
- package/dist/gates/mcp-handler.d.ts +4 -3
- package/dist/gates/mcp-handler.js +15 -5
- package/dist/gates/model-gate.d.ts +11 -2
- package/dist/gates/policy.d.ts +28 -5
- package/dist/gates/policy.js +60 -17
- package/dist/gates/provider-gate.d.ts +22 -0
- package/dist/gates/provider-gate.js +30 -0
- package/dist/gates/tool-calls.d.ts +1 -3
- package/dist/gates/tool-calls.js +2 -3
- package/dist/gates/tool-client.d.ts +1 -1
- package/dist/gates/tool-client.js +5 -6
- package/dist/gates/tool-contract.d.ts +8 -5
- package/dist/gates/tool-contract.js +1 -1
- package/dist/gates/tool-gate.d.ts +18 -7
- package/dist/gates/tool-gate.js +49 -7
- package/dist/harness/calls.d.ts +27 -25
- package/dist/harness/calls.js +40 -46
- package/dist/harness/executors.d.ts +10 -3
- package/dist/harness/executors.js +53 -23
- package/dist/harness/main.js +2 -2
- package/dist/harness/pod.d.ts +0 -2
- package/dist/harness/pod.js +7 -6
- package/dist/harness/run-tokens.d.ts +4 -1
- package/dist/harness/run-tokens.js +6 -2
- package/dist/harness/service.d.ts +17 -14
- package/dist/harness/service.js +21 -35
- package/dist/harness/setup.d.ts +0 -2
- package/dist/harness/setup.js +4 -9
- package/dist/harness/ws-client.d.ts +2 -2
- package/dist/harness/ws-client.js +7 -5
- package/dist/harness/ws-port.d.ts +3 -4
- package/dist/harness/ws-port.js +3 -4
- package/dist/harness-api/artifacts.d.ts +20 -0
- package/dist/harness-api/artifacts.js +478 -0
- package/dist/harness-api/in-process.d.ts +2 -2
- package/dist/harness-api/in-process.js +18 -16
- package/dist/harness-api/record.d.ts +10 -9
- package/dist/harness-api/record.js +102 -157
- package/dist/harness-api/renew.d.ts +1 -1
- package/dist/harness-api/renew.js +2 -2
- package/dist/harness-api/server.d.ts +1 -16
- package/dist/harness-api/server.js +82 -73
- package/dist/harness-api/start.d.ts +20 -2
- package/dist/harness-api/start.js +57 -18
- package/dist/harness-api/tool-journal.d.ts +15 -10
- package/dist/harness-api/tool-journal.js +26 -24
- package/dist/harness-api/workspace.d.ts +0 -4
- package/dist/harness-api/workspace.js +2 -45
- package/dist/harness-api/ws-server.js +10 -10
- package/dist/host/create-host.js +2 -3
- package/dist/host/execution.js +2 -3
- package/dist/host/gates.d.ts +2 -9
- package/dist/host/gates.js +15 -12
- package/dist/host/http.d.ts +0 -2
- package/dist/host/http.js +0 -27
- package/dist/host/main.js +10 -3
- package/dist/host/operate.js +12 -3
- package/dist/host/stack-config.d.ts +11 -0
- package/dist/host/stack-config.js +21 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/infra/index.js +2 -1
- package/dist/infra/readiness.d.ts +0 -2
- package/dist/infra/readiness.js +4 -3
- package/dist/keys/client.d.ts +1 -0
- package/dist/keys/client.js +2 -3
- package/dist/keys/contract.d.ts +1 -3
- package/dist/keys/contract.js +1 -3
- package/dist/keys/egress-ca.d.ts +0 -2
- package/dist/keys/egress-ca.js +2 -3
- package/dist/keys/keys.js +6 -1
- package/dist/keys/x509.d.ts +8 -4
- package/dist/keys/x509.js +14 -5
- package/dist/management-openapi.json +1961 -994
- package/dist/mcp/connect.d.ts +35 -26
- package/dist/mcp/connect.js +112 -141
- package/dist/mcp/pool.d.ts +27 -22
- package/dist/mcp/pool.js +34 -48
- package/dist/mcp/preview.d.ts +13 -6
- package/dist/mcp/preview.js +37 -20
- package/dist/mcp/snapshot.d.ts +35 -86
- package/dist/mcp/snapshot.js +93 -135
- package/dist/model/auth-store.js +5 -3
- package/dist/model/capabilities.d.ts +1 -1
- package/dist/model/catalog.d.ts +9 -36
- package/dist/model/catalog.js +6 -33
- package/dist/model/descriptor.d.ts +51 -0
- package/dist/model/descriptor.js +83 -0
- package/dist/model/models.d.ts +0 -3
- package/dist/model/models.js +3 -5
- package/dist/model/native-blocks.js +1 -1
- package/dist/model/pi-model.d.ts +16 -5
- package/dist/model/pi-model.js +86 -105
- package/dist/openapi.json +866 -1218
- package/dist/reads/cursor.js +3 -1
- package/dist/record/append.d.ts +34 -2
- package/dist/record/append.js +50 -1
- package/dist/record/environment.d.ts +2 -0
- package/dist/record/environment.js +2 -0
- package/dist/record/index.d.ts +1 -1
- package/dist/record/index.js +1 -1
- package/dist/redact.d.ts +1 -1
- package/dist/redact.js +3 -2
- package/dist/sandbox/egress-token.d.ts +3 -3
- package/dist/sandbox/egress-token.js +3 -3
- package/dist/sandbox/join.js +3 -2
- package/dist/sandbox/manager.d.ts +41 -41
- package/dist/sandbox/manager.js +112 -44
- package/dist/sandbox/placement.d.ts +1 -1
- package/dist/sandbox/placement.js +5 -8
- package/dist/sandbox/pods/client.d.ts +1 -10
- package/dist/sandbox/pods/client.js +8 -4
- package/dist/sandbox/pods/lifecycle.d.ts +19 -11
- package/dist/sandbox/pods/lifecycle.js +45 -23
- package/dist/sandbox/pods/network-gate.d.ts +4 -5
- package/dist/sandbox/pods/network-gate.js +7 -7
- package/dist/sandbox/pods/reconcile.js +4 -1
- package/dist/sandbox/pods/spec.js +8 -5
- package/dist/sandbox/policy.d.ts +7 -1
- package/dist/sandbox/policy.js +10 -3
- package/dist/sandbox/resolve.d.ts +8 -3
- package/dist/sandbox/resolve.js +12 -2
- package/dist/sandbox/setup.d.ts +3 -1
- package/dist/sandbox/setup.js +34 -11
- package/dist/sandbox/share.d.ts +5 -14
- package/dist/sandbox/share.js +5 -34
- package/dist/sandbox/skills.d.ts +7 -13
- package/dist/sandbox/skills.js +16 -31
- package/dist/sandbox/tenant-config.d.ts +2 -2
- package/dist/sandbox/tenant-config.js +4 -3
- package/dist/sandbox/tools.d.ts +7 -1
- package/dist/sandbox/tools.js +26 -26
- package/dist/sandbox/types.d.ts +10 -3
- package/dist/sandbox/types.js +9 -0
- package/dist/store/postgres/connect.d.ts +6 -5
- package/dist/store/postgres/connect.js +14 -11
- package/dist/store/postgres/control.js +13 -13
- package/dist/store/postgres/drizzle/0028_artifact_write_keys.sql +2 -0
- package/dist/store/postgres/drizzle/0029_protocol_15.sql +11 -0
- package/dist/store/postgres/drizzle/0030_protocol_15_runtime_block.sql +11 -0
- package/dist/store/postgres/drizzle/0031_protocol_15_mcp_snapshot.sql +8 -0
- package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
- package/dist/store/postgres/migrate.js +1 -1
- package/dist/store/postgres/reads.js +10 -2
- package/dist/store/postgres/schema.d.ts +19 -0
- package/dist/store/postgres/schema.js +7 -0
- package/dist/store/postgres/store.js +55 -26
- package/dist/store/types.d.ts +17 -7
- package/dist/streams/relay/core.d.ts +3 -3
- package/dist/streams/relay/core.js +9 -10
- package/dist/tenant/advance.js +21 -31
- package/dist/tenant/commands.js +9 -16
- package/dist/tenant/context.d.ts +14 -17
- package/dist/tenant/definition-files.d.ts +25 -4
- package/dist/tenant/definition-files.js +85 -47
- package/dist/tenant/effects.js +9 -36
- package/dist/tenant/environment-manifests.js +9 -1
- package/dist/tenant/environments.d.ts +0 -2
- package/dist/tenant/environments.js +54 -30
- package/dist/tenant/ephemeral.d.ts +1 -1
- package/dist/tenant/ephemeral.js +2 -1
- package/dist/tenant/history.d.ts +1 -1
- package/dist/tenant/history.js +1 -1
- package/dist/tenant/host-token.d.ts +3 -3
- package/dist/tenant/host-token.js +12 -11
- package/dist/tenant/http.d.ts +5 -8
- package/dist/tenant/http.js +9 -0
- package/dist/tenant/identity-file.d.ts +24 -1
- package/dist/tenant/identity-file.js +35 -7
- package/dist/tenant/issuers.d.ts +5 -5
- package/dist/tenant/issuers.js +31 -23
- package/dist/tenant/jwt.d.ts +8 -2
- package/dist/tenant/jwt.js +10 -2
- package/dist/tenant/limits.d.ts +90 -0
- package/dist/tenant/limits.js +175 -0
- package/dist/tenant/module.d.ts +4 -1
- package/dist/tenant/module.js +9 -8
- package/dist/tenant/outbound.d.ts +17 -19
- package/dist/tenant/outbound.js +18 -21
- package/dist/tenant/run-grants.js +3 -2
- package/dist/tenant/run-token.d.ts +0 -4
- package/dist/tenant/run-token.js +6 -11
- package/dist/tenant/runtime-limits.d.ts +24 -0
- package/dist/tenant/runtime-limits.js +27 -0
- package/dist/tenant/runtime.js +30 -43
- package/dist/tenant/session-streams.js +7 -6
- package/dist/tenant/session.d.ts +1 -1
- package/dist/tenant/session.js +1 -1
- package/dist/tenant/sessions.js +32 -29
- package/dist/tenant/signing-keys.d.ts +1 -1
- package/dist/tenant/signing-keys.js +5 -6
- package/dist/tenant/status.js +4 -3
- package/dist/tenant/streams.js +18 -20
- package/dist/tenant/sweep.d.ts +1 -21
- package/dist/tenant/sweep.js +39 -48
- package/dist/tenant/transcript.d.ts +70 -23
- package/dist/tenant/transcript.js +28 -22
- package/dist/tenant/types.d.ts +11 -16
- package/dist/tenant/worker.d.ts +2 -10
- package/dist/tenant/worker.js +5 -14
- package/dist/traces/ids.js +5 -1
- package/dist/traces/map.d.ts +9 -6
- package/dist/traces/map.js +9 -11
- package/dist/traces/otlp.d.ts +3 -3
- package/dist/traces/otlp.js +5 -3
- package/dist/traces/projector.d.ts +9 -4
- package/dist/traces/projector.js +13 -7
- package/dist/traces/read.js +6 -1
- package/dist/tuning.d.ts +383 -0
- package/dist/tuning.js +658 -0
- package/dist/vault/headers.js +4 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/dist/artifacts/export.d.ts +0 -8
- package/dist/artifacts/export.js +0 -204
- package/dist/artifacts/media-types.d.ts +0 -30
- package/dist/artifacts/media-types.js +0 -137
- package/dist/artifacts/workspace.d.ts +0 -60
- package/dist/artifacts/workspace.js +0 -45
- package/dist/media.d.ts +0 -10
- package/dist/media.js +0 -58
- package/dist/sandbox/runtime-tools.d.ts +0 -9
- package/dist/sandbox/runtime-tools.js +0 -16
- package/dist/sandbox/session-sandbox.d.ts +0 -16
- package/dist/sandbox/session-sandbox.js +0 -77
- package/dist/tenant/artifact-tool.d.ts +0 -13
- package/dist/tenant/artifact-tool.js +0 -243
- package/dist/tenant/skill-tool.d.ts +0 -20
- package/dist/tenant/skill-tool.js +0 -67
- package/dist/tenant/tool-results.d.ts +0 -57
- package/dist/tenant/tool-results.js +0 -298
- package/dist/tenant/tool-search.d.ts +0 -39
- package/dist/tenant/tool-search.js +0 -103
package/dist/mcp/connect.d.ts
CHANGED
|
@@ -2,9 +2,7 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
2
2
|
import type { McpServerManifest } from "@nylorun/core/define";
|
|
3
3
|
import { type OutboundPolicy } from "../tenant/outbound.js";
|
|
4
4
|
import type { AuthorizeResult } from "../vault/service.js";
|
|
5
|
-
import {
|
|
6
|
-
/** How much of a failed answer's body the model sees. */
|
|
7
|
-
export declare const MCP_ERROR_BODY_CHARS = 2000;
|
|
5
|
+
import type { McpDiagnostic, McpToolRecord } from "./snapshot.js";
|
|
8
6
|
/** Options of one MCP request. */
|
|
9
7
|
export interface McpRequestOptions {
|
|
10
8
|
readonly signal?: AbortSignal;
|
|
@@ -22,6 +20,17 @@ export interface McpCallOptions extends McpRequestOptions {
|
|
|
22
20
|
* it from the call's journal row.
|
|
23
21
|
*/
|
|
24
22
|
readonly traceparent?: string;
|
|
23
|
+
/**
|
|
24
|
+
* How much of a failed call's answer its error quotes: the Tenant's Runtime limit
|
|
25
|
+
* `errorExcerptChars`, read for the call. Default the connection's (`openMcpServer`).
|
|
26
|
+
*/
|
|
27
|
+
readonly errorExcerptChars?: number;
|
|
28
|
+
/**
|
|
29
|
+
* The largest answer kept, in bytes: the Tenant's Runtime limit `toolAnswerBytes` (R2b C11).
|
|
30
|
+
* A larger one is `mcp.too-large`. Required where this process holds the connection; a client
|
|
31
|
+
* of the gates service leaves it out, since the gate reads the limit itself.
|
|
32
|
+
*/
|
|
33
|
+
readonly maxResultBytes?: number;
|
|
25
34
|
}
|
|
26
35
|
/** A page of `tools/list`, as the MCP SDK returns it. */
|
|
27
36
|
export interface McpToolPage {
|
|
@@ -63,7 +72,7 @@ export interface McpClient {
|
|
|
63
72
|
}
|
|
64
73
|
export interface LiveConnection {
|
|
65
74
|
readonly client: McpClient;
|
|
66
|
-
/** The server's instructions from `initialize`,
|
|
75
|
+
/** The server's instructions from `initialize`, scrubbed (`serverInstructions`); absent when none. */
|
|
67
76
|
readonly instructions?: string;
|
|
68
77
|
/**
|
|
69
78
|
* The server's `serverInfo` from `initialize` (name, version, title), without a credential
|
|
@@ -100,15 +109,10 @@ export type McpToolOutcome = {
|
|
|
100
109
|
* - `mcp.status`: the server answered another HTTP error status, without a JSON-RPC error.
|
|
101
110
|
* - `mcp.lost`: sent, and the answer was lost, so the tool may have run. The model sees it, and
|
|
102
111
|
* decides whether to call again (R5; it replaced R2b's read-only and idempotent exception).
|
|
103
|
-
* - `mcp.too-large`: the answer passed `
|
|
104
|
-
* answer is `http.too-large`. The tool ran; its answer was not kept.
|
|
112
|
+
* - `mcp.too-large`: the answer passed the Tenant's Runtime limit `toolAnswerBytes` (R2b C11),
|
|
113
|
+
* as an HTTP tool's answer is `http.too-large`. The tool ran; its answer was not kept.
|
|
105
114
|
*/
|
|
106
115
|
export type McpFailureCode = "mcp.unreachable" | "credential_rejected" | "mcp.forbidden" | "mcp.error" | "mcp.status" | "mcp.lost" | "mcp.too-large";
|
|
107
|
-
/**
|
|
108
|
-
* The largest `tools/call` answer the gate holds (R2b C11): 8 MiB, an HTTP tool's cap. Core
|
|
109
|
-
* stores what does not fit a tool result as an artifact (`tenant/tool-results.ts`).
|
|
110
|
-
*/
|
|
111
|
-
export declare const MCP_RESULT_MAX_BYTES: number;
|
|
112
116
|
export interface McpCallFailure {
|
|
113
117
|
readonly code: McpFailureCode;
|
|
114
118
|
readonly message: string;
|
|
@@ -164,8 +168,12 @@ export declare class CredentialRejected extends McpCallFailed {
|
|
|
164
168
|
export declare class McpConnectionStale extends McpCallFailed {
|
|
165
169
|
constructor(server: string, reason: string);
|
|
166
170
|
}
|
|
167
|
-
/**
|
|
168
|
-
|
|
171
|
+
/**
|
|
172
|
+
* A tool call that never reached its server: its connection could not be opened. Where the
|
|
173
|
+
* opening reached the server, `chars` is how much of what it said the error quotes (the Tenant's
|
|
174
|
+
* `errorExcerptChars`); without it, the message is this process's own and stays whole.
|
|
175
|
+
*/
|
|
176
|
+
export declare function unreachable(server: string, error: unknown, chars?: number): McpCallFailed;
|
|
169
177
|
/**
|
|
170
178
|
* Where a `tools/call` carries its key (R5.5): MCP has no standard key field, and `_meta` is the
|
|
171
179
|
* spec's place for extra request data. The key is the call's effect id, the same on every send,
|
|
@@ -177,7 +185,7 @@ export declare const IDEMPOTENCY_META_KEY = "nylorun/idempotency-key";
|
|
|
177
185
|
* with them (R2b C8), and a failed call is an `McpCallFailed` (C7). A keyed call sends its key in
|
|
178
186
|
* `_meta` (R5.5).
|
|
179
187
|
*/
|
|
180
|
-
export declare function sdkClient(client: Client, serverName: string): McpClient;
|
|
188
|
+
export declare function sdkClient(client: Client, serverName: string, errorChars: number): McpClient;
|
|
181
189
|
export declare function openMcpServer(input: {
|
|
182
190
|
server: McpServerManifest;
|
|
183
191
|
authorize: (url: string) => Promise<AuthorizeResult>;
|
|
@@ -189,31 +197,32 @@ export declare function openMcpServer(input: {
|
|
|
189
197
|
policy?: OutboundPolicy;
|
|
190
198
|
/** Ends `initialize` (a tool preview's timeout, R2b C12). */
|
|
191
199
|
signal?: AbortSignal;
|
|
200
|
+
/**
|
|
201
|
+
* How much of a failed answer an error quotes: the Tenant's Runtime limit `errorExcerptChars`
|
|
202
|
+
* when the connection opens; a call may pass the one in force then.
|
|
203
|
+
*/
|
|
204
|
+
errorExcerptChars: number;
|
|
192
205
|
}): Promise<LiveConnection>;
|
|
193
206
|
/**
|
|
194
|
-
* What a server's `initialize` said to do with its tools,
|
|
195
|
-
* (
|
|
207
|
+
* What a server's `initialize` said to do with its tools, as it said it, without a credential
|
|
208
|
+
* value sent (C8). The harness decides how much of it the model reads.
|
|
196
209
|
*/
|
|
197
210
|
export declare function serverInstructions(text: unknown, secrets: readonly string[]): string | undefined;
|
|
198
211
|
/**
|
|
199
|
-
* Lists a server's tools
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
212
|
+
* Lists a server's tools as it names them (protocol 15): every tool whose input schema is a JSON
|
|
213
|
+
* object, the ones its `tools` settings disable included, since a turn's manifest decides
|
|
214
|
+
* `enabled` (`mcpFactsOf`). The harness names them for the model. A tool with no input schema
|
|
215
|
+
* object is in `omitted`; `disabled` counts the tools the declaration's settings disable, and
|
|
216
|
+
* `unknownTools` the settings' keys that name no listed tool.
|
|
203
217
|
*/
|
|
204
218
|
export declare function listMcpTools(client: McpClient, input: {
|
|
205
219
|
serverName: string;
|
|
206
|
-
taken: Set<string>;
|
|
207
220
|
signal?: AbortSignal;
|
|
208
|
-
/**
|
|
209
|
-
* The server's declaration: a tool its `tools` settings disable is left out before it takes
|
|
210
|
-
* a name (R2b C9), and a key that names no listed tool is in `unknownTools`.
|
|
211
|
-
*/
|
|
221
|
+
/** The server's declaration, whose `tools` settings the diagnostics count against. */
|
|
212
222
|
server?: McpServerManifest;
|
|
213
223
|
}): Promise<{
|
|
214
224
|
tools: McpToolRecord[];
|
|
215
225
|
omitted: string[];
|
|
216
|
-
renamed: McpRenamedTool[];
|
|
217
226
|
disabled: number;
|
|
218
227
|
unknownTools: string[];
|
|
219
228
|
}>;
|
package/dist/mcp/connect.js
CHANGED
|
@@ -3,18 +3,11 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
3
3
|
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
4
4
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
5
5
|
import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
-
import { MCP_TOOL_DEFAULTS,
|
|
6
|
+
import { MCP_TOOL_DEFAULTS, mcpToolSettings } from "@nylorun/core/define";
|
|
7
7
|
import { scrub, scrubValues } from "../redact.js";
|
|
8
|
-
import { guardedFetch,
|
|
8
|
+
import { guardedFetch, OutboundFailed, OutboundRefused, } from "../tenant/outbound.js";
|
|
9
9
|
import { credentialSecrets } from "../vault/headers.js";
|
|
10
|
-
import {
|
|
11
|
-
/** How much of a failed answer's body the model sees. */
|
|
12
|
-
export const MCP_ERROR_BODY_CHARS = 2_000;
|
|
13
|
-
/**
|
|
14
|
-
* The largest `tools/call` answer the gate holds (R2b C11): 8 MiB, an HTTP tool's cap. Core
|
|
15
|
-
* stores what does not fit a tool result as an artifact (`tenant/tool-results.ts`).
|
|
16
|
-
*/
|
|
17
|
-
export const MCP_RESULT_MAX_BYTES = MAX_RESPONSE_BYTES;
|
|
10
|
+
import { hostTuning } from "../tuning.js";
|
|
18
11
|
/** A tool call that failed with a code the model can act on (R2b C7). */
|
|
19
12
|
export class McpCallFailed extends Error {
|
|
20
13
|
failure;
|
|
@@ -96,21 +89,29 @@ export class McpConnectionStale extends McpCallFailed {
|
|
|
96
89
|
this.name = "McpConnectionStale";
|
|
97
90
|
}
|
|
98
91
|
}
|
|
99
|
-
/**
|
|
100
|
-
|
|
92
|
+
/**
|
|
93
|
+
* A tool call that never reached its server: its connection could not be opened. Where the
|
|
94
|
+
* opening reached the server, `chars` is how much of what it said the error quotes (the Tenant's
|
|
95
|
+
* `errorExcerptChars`); without it, the message is this process's own and stays whole.
|
|
96
|
+
*/
|
|
97
|
+
export function unreachable(server, error, chars) {
|
|
101
98
|
if (error instanceof McpCallFailed)
|
|
102
99
|
return error;
|
|
103
|
-
|
|
100
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
101
|
+
return unreachableFailure(server, detail, chars ?? detail.length);
|
|
104
102
|
}
|
|
105
103
|
/** The operation a request of the transport's fetch belongs to. */
|
|
106
104
|
const tracking = new AsyncLocalStorage();
|
|
107
|
-
function tracked(call, run,
|
|
105
|
+
function tracked(call, run, options) {
|
|
106
|
+
const { traceparent, maxResultBytes, errorChars } = options;
|
|
108
107
|
const tracker = {
|
|
109
108
|
secrets: new Set(),
|
|
110
109
|
sent: false,
|
|
111
110
|
settled: false,
|
|
111
|
+
errorChars,
|
|
112
112
|
...(call ? { calling: new AbortController() } : {}),
|
|
113
113
|
...(traceparent ? { traceparent } : {}),
|
|
114
|
+
...(maxResultBytes === undefined ? {} : { maxResultBytes }),
|
|
114
115
|
};
|
|
115
116
|
return tracking.run(tracker, () => run(tracker));
|
|
116
117
|
}
|
|
@@ -125,7 +126,7 @@ export const IDEMPOTENCY_META_KEY = "nylorun/idempotency-key";
|
|
|
125
126
|
* with them (R2b C8), and a failed call is an `McpCallFailed` (C7). A keyed call sends its key in
|
|
126
127
|
* `_meta` (R5.5).
|
|
127
128
|
*/
|
|
128
|
-
export function sdkClient(client, serverName) {
|
|
129
|
+
export function sdkClient(client, serverName, errorChars) {
|
|
129
130
|
return {
|
|
130
131
|
listTools: (params, options) => tracked(false, async (tracker) => {
|
|
131
132
|
try {
|
|
@@ -135,35 +136,41 @@ export function sdkClient(client, serverName) {
|
|
|
135
136
|
catch (error) {
|
|
136
137
|
throw scrubError(error, tracker);
|
|
137
138
|
}
|
|
138
|
-
}),
|
|
139
|
-
callTool: (params, options) =>
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
139
|
+
}, { errorChars }),
|
|
140
|
+
callTool: (params, options) => {
|
|
141
|
+
const maxResultBytes = options?.maxResultBytes;
|
|
142
|
+
if (maxResultBytes === undefined)
|
|
143
|
+
return Promise.reject(new Error("A tool call on a connection this process holds needs maxResultBytes"));
|
|
144
|
+
return tracked(true, async (tracker) => {
|
|
145
|
+
const lost = tracker.calling.signal;
|
|
146
|
+
try {
|
|
147
|
+
const keyed = options?.key === undefined
|
|
148
|
+
? params
|
|
149
|
+
: { ...params, _meta: { [IDEMPOTENCY_META_KEY]: options.key } };
|
|
150
|
+
const result = (await client.callTool(keyed, undefined, {
|
|
151
|
+
signal: options?.signal ? AbortSignal.any([options.signal, lost]) : lost,
|
|
152
|
+
}));
|
|
153
|
+
// An answer the stream did not count (an `sse` server answers on its event stream).
|
|
154
|
+
if (Buffer.byteLength(JSON.stringify(result)) > maxResultBytes)
|
|
155
|
+
throw tooLargeFailure(serverName, maxResultBytes);
|
|
156
|
+
const answer = scrubValues(result, [...tracker.secrets]);
|
|
157
|
+
return { result: answer.value, ...(answer.redacted > 0 ? { redacted: answer.redacted } : {}) };
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
// A cancel or a shutdown: the caller decides what it means.
|
|
161
|
+
if (options?.signal?.aborted)
|
|
162
|
+
throw error;
|
|
163
|
+
throw scrubError(failureOf(serverName, error, tracker), tracker);
|
|
164
|
+
}
|
|
165
|
+
finally {
|
|
166
|
+
tracker.settled = true;
|
|
167
|
+
}
|
|
168
|
+
}, { traceparent: options?.traceparent, maxResultBytes, errorChars: options?.errorExcerptChars ?? errorChars });
|
|
169
|
+
},
|
|
164
170
|
};
|
|
165
171
|
}
|
|
166
172
|
export async function openMcpServer(input) {
|
|
173
|
+
const errorChars = input.errorExcerptChars;
|
|
167
174
|
const authorize = input.authorize;
|
|
168
175
|
const initial = await authorize(input.server.url);
|
|
169
176
|
if (initial.status === "refused") {
|
|
@@ -175,7 +182,7 @@ export async function openMcpServer(input) {
|
|
|
175
182
|
// A credential with `via` sends the server's requests there (R2b C2); the manifest's URL still
|
|
176
183
|
// names the server for its credential, its tools and its diagnostics.
|
|
177
184
|
const url = new URL(endpointOf(input.server, initial));
|
|
178
|
-
const fetchImpl = authorizedFetch(input.server, url, authorize, input.policy ?? {});
|
|
185
|
+
const fetchImpl = authorizedFetch(input.server, url, authorize, input.policy ?? {}, errorChars);
|
|
179
186
|
const transport = input.server.type === "sse"
|
|
180
187
|
? new SSEClientTransport(url, { fetch: fetchImpl })
|
|
181
188
|
: new StreamableHTTPClientTransport(url, { fetch: fetchImpl });
|
|
@@ -194,33 +201,35 @@ export async function openMcpServer(input) {
|
|
|
194
201
|
instructions: serverInstructions(client.getInstructions(), [...tracker.secrets]),
|
|
195
202
|
serverInfo: info ? scrubbed({ ...info }, tracker) : undefined,
|
|
196
203
|
};
|
|
197
|
-
});
|
|
204
|
+
}, { errorChars });
|
|
198
205
|
return {
|
|
199
|
-
client: sdkClient(client, input.server.name),
|
|
206
|
+
client: sdkClient(client, input.server.name, errorChars),
|
|
200
207
|
...(instructions === undefined ? {} : { instructions }),
|
|
201
208
|
...(serverInfo === undefined ? {} : { serverInfo }),
|
|
202
209
|
close: () => client.close(),
|
|
203
210
|
};
|
|
204
211
|
}
|
|
205
212
|
/**
|
|
206
|
-
* What a server's `initialize` said to do with its tools,
|
|
207
|
-
* (
|
|
213
|
+
* What a server's `initialize` said to do with its tools, as it said it, without a credential
|
|
214
|
+
* value sent (C8). The harness decides how much of it the model reads.
|
|
208
215
|
*/
|
|
209
216
|
export function serverInstructions(text, secrets) {
|
|
210
217
|
if (typeof text !== "string" || text.trim().length === 0)
|
|
211
218
|
return undefined;
|
|
212
|
-
return scrubValues(text.trim()
|
|
219
|
+
return scrubValues(text.trim(), secrets).value;
|
|
213
220
|
}
|
|
214
221
|
/**
|
|
215
|
-
* Lists a server's tools
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
222
|
+
* Lists a server's tools as it names them (protocol 15): every tool whose input schema is a JSON
|
|
223
|
+
* object, the ones its `tools` settings disable included, since a turn's manifest decides
|
|
224
|
+
* `enabled` (`mcpFactsOf`). The harness names them for the model. A tool with no input schema
|
|
225
|
+
* object is in `omitted`; `disabled` counts the tools the declaration's settings disable, and
|
|
226
|
+
* `unknownTools` the settings' keys that name no listed tool.
|
|
219
227
|
*/
|
|
220
228
|
export async function listMcpTools(client, input) {
|
|
221
229
|
const listed = [];
|
|
222
230
|
let cursor;
|
|
223
|
-
|
|
231
|
+
const pages = hostTuning().harness.mcpListPages;
|
|
232
|
+
for (let page = 0; page < pages; page += 1) {
|
|
224
233
|
const answer = await client.listTools(cursor ? { cursor } : undefined, input.signal ? { signal: input.signal } : undefined);
|
|
225
234
|
listed.push(...answer.tools);
|
|
226
235
|
cursor = answer.nextCursor;
|
|
@@ -233,69 +242,32 @@ export async function listMcpTools(client, input) {
|
|
|
233
242
|
const unknownTools = Object.keys(server?.tools ?? {}).filter((key) => key !== MCP_TOOL_DEFAULTS && !listedNames.has(key));
|
|
234
243
|
let disabled = 0;
|
|
235
244
|
const omitted = [];
|
|
236
|
-
const
|
|
245
|
+
const tools = [];
|
|
246
|
+
const seen = new Set();
|
|
237
247
|
for (const tool of listed) {
|
|
238
|
-
if (typeof tool.name !== "string" || tool.name.length === 0)
|
|
248
|
+
if (typeof tool.name !== "string" || tool.name.length === 0 || seen.has(tool.name))
|
|
239
249
|
continue;
|
|
240
|
-
|
|
250
|
+
seen.add(tool.name);
|
|
251
|
+
if (server && !mcpToolSettings(server, tool.name).enabled)
|
|
241
252
|
disabled += 1;
|
|
253
|
+
const inputSchema = jsonObjectOf(tool.inputSchema);
|
|
254
|
+
if (!inputSchema) {
|
|
255
|
+
omitted.push(tool.name);
|
|
242
256
|
continue;
|
|
243
257
|
}
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
candidates.push({ tool, inputSchema });
|
|
247
|
-
else
|
|
248
|
-
omitted.push(normalizeToolName(input.serverName, tool.name));
|
|
249
|
-
}
|
|
250
|
-
// Names that need no change first, so a renamed tool never takes the name of another of the
|
|
251
|
-
// server's tools.
|
|
252
|
-
const names = new Map();
|
|
253
|
-
const renamed = [];
|
|
254
|
-
for (const candidate of candidates) {
|
|
255
|
-
const name = normalizeToolName(input.serverName, candidate.tool.name);
|
|
256
|
-
if (name !== `${input.serverName}__${candidate.tool.name}`)
|
|
257
|
-
continue;
|
|
258
|
-
if (input.taken.has(name))
|
|
259
|
-
omitted.push(name);
|
|
260
|
-
else {
|
|
261
|
-
input.taken.add(name);
|
|
262
|
-
names.set(candidate, name);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
for (const candidate of candidates) {
|
|
266
|
-
const raw = `${input.serverName}__${candidate.tool.name}`;
|
|
267
|
-
let name = normalizeToolName(input.serverName, candidate.tool.name);
|
|
268
|
-
if (name === raw)
|
|
269
|
-
continue;
|
|
270
|
-
if (input.taken.has(name))
|
|
271
|
-
name = normalizeToolName(input.serverName, candidate.tool.name, { suffixed: true });
|
|
272
|
-
if (input.taken.has(name)) {
|
|
273
|
-
omitted.push(name);
|
|
274
|
-
continue;
|
|
275
|
-
}
|
|
276
|
-
input.taken.add(name);
|
|
277
|
-
names.set(candidate, name);
|
|
278
|
-
renamed.push({ serverToolName: candidate.tool.name, name });
|
|
279
|
-
}
|
|
280
|
-
const tools = [];
|
|
281
|
-
for (const candidate of candidates) {
|
|
282
|
-
const name = names.get(candidate);
|
|
283
|
-
if (name === undefined)
|
|
284
|
-
continue;
|
|
285
|
-
const { tool, inputSchema } = candidate;
|
|
286
|
-
const outputSchema = tool.outputSchema ? usableSchema(tool.outputSchema) : undefined;
|
|
258
|
+
const outputSchema = jsonObjectOf(tool.outputSchema);
|
|
259
|
+
const annotations = jsonObjectOf(tool.annotations);
|
|
287
260
|
tools.push({
|
|
288
|
-
|
|
289
|
-
serverToolName: tool.name,
|
|
290
|
-
name,
|
|
261
|
+
name: tool.name,
|
|
291
262
|
...(typeof tool.description === "string" && tool.description.length > 0
|
|
292
263
|
? { description: tool.description }
|
|
293
264
|
: {}),
|
|
294
265
|
inputSchema,
|
|
295
266
|
...(outputSchema === undefined ? {} : { outputSchema }),
|
|
267
|
+
...(annotations === undefined || Object.keys(annotations).length === 0 ? {} : { annotations }),
|
|
296
268
|
});
|
|
297
269
|
}
|
|
298
|
-
return { tools, omitted,
|
|
270
|
+
return { tools, omitted, disabled, unknownTools };
|
|
299
271
|
}
|
|
300
272
|
/**
|
|
301
273
|
* One tool call and what it came to. Rejects with `McpConnectionStale` when the connection must
|
|
@@ -314,6 +286,7 @@ export async function callMcpTool(client, serverToolName, args, options = {}) {
|
|
|
314
286
|
...(options.key === undefined ? {} : { key: options.key }),
|
|
315
287
|
...(options.signal ? { signal: options.signal } : {}),
|
|
316
288
|
...(options.traceparent === undefined ? {} : { traceparent: options.traceparent }),
|
|
289
|
+
...(options.maxResultBytes === undefined ? {} : { maxResultBytes: options.maxResultBytes }),
|
|
317
290
|
});
|
|
318
291
|
}
|
|
319
292
|
catch (error) {
|
|
@@ -337,8 +310,8 @@ export async function callMcpTool(client, serverToolName, args, options = {}) {
|
|
|
337
310
|
}
|
|
338
311
|
if ("structuredContent" in result && result.structuredContent !== undefined)
|
|
339
312
|
return { kind: "completed", output: result.structuredContent, ...redacted };
|
|
340
|
-
// Text alone is the text. With an image, audio or a resource in it, every part stays, and
|
|
341
|
-
// stores the bytes as artifacts before
|
|
313
|
+
// Text alone is the text. With an image, audio or a resource in it, every part stays, and the
|
|
314
|
+
// harness stores the bytes as artifacts before it reports the outcome (R2b C11).
|
|
342
315
|
const text = "content" in result && onlyText(result.content) ? textContent(result.content) : undefined;
|
|
343
316
|
if (text !== undefined)
|
|
344
317
|
return { kind: "completed", output: text, ...redacted };
|
|
@@ -377,7 +350,7 @@ export function diagnosticFromError(serverName, error) {
|
|
|
377
350
|
message: unauthorized
|
|
378
351
|
? "MCP server returned 401"
|
|
379
352
|
: error instanceof Error
|
|
380
|
-
? error.message.slice(0,
|
|
353
|
+
? error.message.slice(0, hostTuning().harness.mcpDiagnosticChars)
|
|
381
354
|
: "MCP server connection failed",
|
|
382
355
|
};
|
|
383
356
|
}
|
|
@@ -394,7 +367,7 @@ function endpointOf(server, result) {
|
|
|
394
367
|
* `McpCallFailed`. Under a tracked operation it records the credential values it sends, whether
|
|
395
368
|
* a POST was sent, and a tool call's answer lost after it was.
|
|
396
369
|
*/
|
|
397
|
-
function authorizedFetch(server, endpoint, authorize, policy) {
|
|
370
|
+
function authorizedFetch(server, endpoint, authorize, policy, errorChars) {
|
|
398
371
|
const send = guardedFetch(policy, { stream: true });
|
|
399
372
|
return async (url, init) => {
|
|
400
373
|
const target = typeof url === "string" ? url : url.href;
|
|
@@ -448,19 +421,21 @@ function authorizedFetch(server, endpoint, authorize, policy) {
|
|
|
448
421
|
throw new McpConnectionStale(server.name, "ended the connection's session (HTTP 404)");
|
|
449
422
|
}
|
|
450
423
|
// A GET's error status (405: no event stream) is the SDK's to read.
|
|
451
|
-
if (post && !response.ok)
|
|
452
|
-
|
|
424
|
+
if (post && !response.ok) {
|
|
425
|
+
const chars = tracker?.errorChars ?? errorChars;
|
|
426
|
+
throw statusFailure(server.name, response.status, await bodyText(response, chars), chars);
|
|
427
|
+
}
|
|
453
428
|
return tracker?.calling && post && response.body ? watched(response, tracker) : response;
|
|
454
429
|
};
|
|
455
430
|
}
|
|
456
431
|
/** Error statuses after which the same request may work. */
|
|
457
432
|
const RETRYABLE_STATUSES = new Set([408, 429, 502, 503, 504]);
|
|
458
433
|
/** A POST the server answered with an error status: its JSON-RPC error, or the status. */
|
|
459
|
-
function statusFailure(server, status, body) {
|
|
434
|
+
function statusFailure(server, status, body, chars) {
|
|
460
435
|
const rpc = rpcErrorOf(body);
|
|
461
436
|
if (status !== 403 && rpc)
|
|
462
|
-
return rpcFailure(server, `MCP error ${rpc.code}: ${rpc.message}
|
|
463
|
-
const text = body.length >
|
|
437
|
+
return rpcFailure(server, `MCP error ${rpc.code}: ${rpc.message}`, chars);
|
|
438
|
+
const text = body.length > chars ? `${body.slice(0, chars)}…` : body;
|
|
464
439
|
return new McpCallFailed({
|
|
465
440
|
code: status === 403 ? "mcp.forbidden" : "mcp.status",
|
|
466
441
|
message: `The MCP server '${server}' answered HTTP ${status}${text ? `: ${text}` : ""}`,
|
|
@@ -468,34 +443,34 @@ function statusFailure(server, status, body) {
|
|
|
468
443
|
retryable: RETRYABLE_STATUSES.has(status),
|
|
469
444
|
});
|
|
470
445
|
}
|
|
471
|
-
function rpcFailure(server, message) {
|
|
446
|
+
function rpcFailure(server, message, chars) {
|
|
472
447
|
return new McpCallFailed({
|
|
473
448
|
code: "mcp.error",
|
|
474
|
-
message: `The MCP server '${server}' answered an error: ${message.slice(0,
|
|
449
|
+
message: `The MCP server '${server}' answered an error: ${message.slice(0, chars)}`,
|
|
475
450
|
sent: true,
|
|
476
451
|
retryable: false,
|
|
477
452
|
});
|
|
478
453
|
}
|
|
479
|
-
function unreachableFailure(server, detail) {
|
|
454
|
+
function unreachableFailure(server, detail, chars) {
|
|
480
455
|
return new McpCallFailed({
|
|
481
456
|
code: "mcp.unreachable",
|
|
482
|
-
message: `The MCP server '${server}' could not be reached, and the call was not sent: ${detail.slice(0,
|
|
457
|
+
message: `The MCP server '${server}' could not be reached, and the call was not sent: ${detail.slice(0, chars)}`,
|
|
483
458
|
sent: false,
|
|
484
459
|
retryable: true,
|
|
485
460
|
});
|
|
486
461
|
}
|
|
487
|
-
function lostFailure(server, detail) {
|
|
462
|
+
function lostFailure(server, detail, chars) {
|
|
488
463
|
return new McpCallFailed({
|
|
489
464
|
code: "mcp.lost",
|
|
490
|
-
message: `The connection to the MCP server '${server}' was lost after the call was sent, so it may have run: ${detail.slice(0,
|
|
465
|
+
message: `The connection to the MCP server '${server}' was lost after the call was sent, so it may have run: ${detail.slice(0, chars)}`,
|
|
491
466
|
sent: true,
|
|
492
467
|
retryable: true,
|
|
493
468
|
});
|
|
494
469
|
}
|
|
495
|
-
function tooLargeFailure(server) {
|
|
470
|
+
function tooLargeFailure(server, maxResultBytes) {
|
|
496
471
|
return new McpCallFailed({
|
|
497
472
|
code: "mcp.too-large",
|
|
498
|
-
message: `The MCP server '${server}' answered more than ${
|
|
473
|
+
message: `The MCP server '${server}' answered more than ${maxResultBytes} bytes, the most a tool result may hold (the Runtime limit toolAnswerBytes), so the answer was not kept`,
|
|
499
474
|
sent: true,
|
|
500
475
|
retryable: false,
|
|
501
476
|
});
|
|
@@ -505,24 +480,25 @@ function failureOf(server, error, tracker) {
|
|
|
505
480
|
if (error instanceof McpCallFailed)
|
|
506
481
|
return error;
|
|
507
482
|
if (tracker.tooLarge)
|
|
508
|
-
return tooLargeFailure(server);
|
|
483
|
+
return tooLargeFailure(server, tracker.maxResultBytes ?? 0);
|
|
509
484
|
const detail = error instanceof Error ? error.message : String(error);
|
|
485
|
+
const chars = tracker.errorChars;
|
|
510
486
|
if (tracker.lost !== undefined)
|
|
511
|
-
return lostFailure(server, tracker.lost);
|
|
487
|
+
return lostFailure(server, tracker.lost, chars);
|
|
512
488
|
if (error instanceof OutboundRefused)
|
|
513
|
-
return unreachableFailure(server, detail);
|
|
489
|
+
return unreachableFailure(server, detail, chars);
|
|
514
490
|
if (error instanceof OutboundFailed)
|
|
515
|
-
return error.sent ? lostFailure(server, detail) : unreachableFailure(server, detail);
|
|
491
|
+
return error.sent ? lostFailure(server, detail, chars) : unreachableFailure(server, detail, chars);
|
|
516
492
|
if (error instanceof McpError) {
|
|
517
493
|
// Sent, and no answer came in time.
|
|
518
494
|
if (error.code === ErrorCode.RequestTimeout)
|
|
519
|
-
return lostFailure(server, detail);
|
|
495
|
+
return lostFailure(server, detail, chars);
|
|
520
496
|
if (error.code === ErrorCode.ConnectionClosed)
|
|
521
|
-
return tracker.sent ? lostFailure(server, detail) : unreachableFailure(server, detail);
|
|
522
|
-
return rpcFailure(server, detail);
|
|
497
|
+
return tracker.sent ? lostFailure(server, detail, chars) : unreachableFailure(server, detail, chars);
|
|
498
|
+
return rpcFailure(server, detail, chars);
|
|
523
499
|
}
|
|
524
500
|
// An answer that could not be read leaves the call's fate unknown, as a lost one does.
|
|
525
|
-
return tracker.sent ? lostFailure(server, detail) : unreachableFailure(server, detail);
|
|
501
|
+
return tracker.sent ? lostFailure(server, detail, chars) : unreachableFailure(server, detail, chars);
|
|
526
502
|
}
|
|
527
503
|
/** `value` with the operation's credential values replaced (R2b C8). */
|
|
528
504
|
function scrubbed(value, tracker) {
|
|
@@ -549,7 +525,7 @@ function scrubError(error, tracker) {
|
|
|
549
525
|
/**
|
|
550
526
|
* A tool call's answer whose body reports its loss (R2b C7): a stream that breaks after the
|
|
551
527
|
* request was sent ends the call as lost, now, instead of at the SDK's request timeout. One that
|
|
552
|
-
* passes `
|
|
528
|
+
* passes the call's `maxResultBytes` ends it as `mcp.too-large` (C11), before more of it is read.
|
|
553
529
|
*/
|
|
554
530
|
function watched(response, tracker) {
|
|
555
531
|
const source = response.body.getReader();
|
|
@@ -571,9 +547,9 @@ function watched(response, tracker) {
|
|
|
571
547
|
if (next.done)
|
|
572
548
|
return controller.close();
|
|
573
549
|
received += next.value.byteLength;
|
|
574
|
-
if (received >
|
|
550
|
+
if (tracker.maxResultBytes !== undefined && received > tracker.maxResultBytes) {
|
|
575
551
|
tracker.tooLarge = true;
|
|
576
|
-
const error = new Error(`The answer passed ${
|
|
552
|
+
const error = new Error(`The answer passed ${tracker.maxResultBytes} bytes`);
|
|
577
553
|
tracker.calling?.abort(error);
|
|
578
554
|
controller.error(error);
|
|
579
555
|
void source.cancel(error).catch(() => undefined);
|
|
@@ -590,14 +566,15 @@ function watched(response, tracker) {
|
|
|
590
566
|
});
|
|
591
567
|
}
|
|
592
568
|
/** The start of an error answer's body. */
|
|
593
|
-
async function bodyText(response) {
|
|
569
|
+
async function bodyText(response, chars) {
|
|
594
570
|
if (!response.body)
|
|
595
571
|
return "";
|
|
596
572
|
const reader = response.body.getReader();
|
|
597
573
|
const chunks = [];
|
|
598
574
|
let size = 0;
|
|
599
575
|
try {
|
|
600
|
-
|
|
576
|
+
// A character takes at most 4 bytes of UTF-8: enough bytes for `chars` of them.
|
|
577
|
+
while (size < 4 * chars) {
|
|
601
578
|
const next = await reader.read();
|
|
602
579
|
if (next.done)
|
|
603
580
|
break;
|
|
@@ -634,17 +611,11 @@ function sameOrigin(requestUrl, serverUrl) {
|
|
|
634
611
|
return false;
|
|
635
612
|
}
|
|
636
613
|
}
|
|
637
|
-
|
|
614
|
+
/** `value` as a JSON object (a copy), or undefined when it is not one. */
|
|
615
|
+
function jsonObjectOf(value) {
|
|
638
616
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
639
617
|
return undefined;
|
|
640
|
-
|
|
641
|
-
try {
|
|
642
|
-
schemaFromJSON(schema);
|
|
643
|
-
}
|
|
644
|
-
catch {
|
|
645
|
-
return undefined;
|
|
646
|
-
}
|
|
647
|
-
return schema;
|
|
618
|
+
return JSON.parse(JSON.stringify(value));
|
|
648
619
|
}
|
|
649
620
|
function textContent(content) {
|
|
650
621
|
if (!Array.isArray(content))
|