@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.
Files changed (326) hide show
  1. package/CHANGELOG.md +169 -0
  2. package/README.md +26 -16
  3. package/dist/adapters/execution/restate.d.ts +15 -18
  4. package/dist/adapters/execution/restate.js +31 -27
  5. package/dist/adapters/replication/pgoutput.d.ts +1 -1
  6. package/dist/adapters/replication/pgoutput.js +3 -2
  7. package/dist/adapters/sandbox/local.js +8 -5
  8. package/dist/adapters/sandbox/virtual.js +2 -2
  9. package/dist/adapters/streams/s2.d.ts +1 -1
  10. package/dist/adapters/streams/s2.js +18 -14
  11. package/dist/api/a2a/card.js +1 -1
  12. package/dist/api/a2a/endpoint.js +3 -4
  13. package/dist/api/a2a/protocol.js +4 -0
  14. package/dist/api/a2a/routes.d.ts +2 -6
  15. package/dist/api/a2a/routes.js +12 -18
  16. package/dist/api/ag-ui/endpoint.js +2 -3
  17. package/dist/api/ag-ui/routes.js +4 -4
  18. package/dist/api/components.d.ts +326 -45
  19. package/dist/api/components.js +5 -1
  20. package/dist/api/gate/keys.js +7 -4
  21. package/dist/api/gate/routes.d.ts +6 -2
  22. package/dist/api/gate/routes.js +15 -11
  23. package/dist/api/http/app.d.ts +2 -2
  24. package/dist/api/http/app.js +2 -2
  25. package/dist/api/http/body.d.ts +15 -3
  26. package/dist/api/http/body.js +15 -7
  27. package/dist/api/http/define.d.ts +1 -1
  28. package/dist/api/http/respond.js +7 -3
  29. package/dist/api/http/routes/access.js +5 -4
  30. package/dist/api/http/routes/artifacts.js +19 -7
  31. package/dist/api/http/routes/coverage.js +1 -1
  32. package/dist/api/http/routes/environment-manifests.js +1 -1
  33. package/dist/api/http/routes/environments.js +6 -6
  34. package/dist/api/http/routes/files.d.ts +2 -1
  35. package/dist/api/http/routes/files.js +31 -5
  36. package/dist/api/http/routes/mcp.js +1 -1
  37. package/dist/api/http/routes/reads.d.ts +35 -0
  38. package/dist/api/http/routes/reads.js +50 -22
  39. package/dist/api/http/routes/sessions.js +12 -11
  40. package/dist/api/http/routes/tenant.d.ts +2 -2
  41. package/dist/api/http/routes/tenant.js +69 -16
  42. package/dist/api/inbound-files.d.ts +3 -9
  43. package/dist/api/inbound-files.js +18 -24
  44. package/dist/api/openapi.js +6 -2
  45. package/dist/artifacts/disk.d.ts +0 -4
  46. package/dist/artifacts/disk.js +77 -73
  47. package/dist/artifacts/files.d.ts +13 -4
  48. package/dist/artifacts/files.js +27 -9
  49. package/dist/artifacts/folder-upload.d.ts +75 -0
  50. package/dist/artifacts/folder-upload.js +263 -0
  51. package/dist/artifacts/folders.d.ts +14 -8
  52. package/dist/artifacts/folders.js +19 -11
  53. package/dist/artifacts/links.js +7 -6
  54. package/dist/artifacts/parts.d.ts +9 -14
  55. package/dist/artifacts/parts.js +11 -9
  56. package/dist/artifacts/pipe.d.ts +15 -0
  57. package/dist/artifacts/pipe.js +71 -0
  58. package/dist/artifacts/quota.d.ts +36 -0
  59. package/dist/artifacts/quota.js +44 -0
  60. package/dist/artifacts/service.d.ts +25 -1
  61. package/dist/artifacts/service.js +70 -25
  62. package/dist/blob/fs.js +2 -1
  63. package/dist/blob/s3.d.ts +6 -2
  64. package/dist/blob/s3.js +9 -7
  65. package/dist/blob/types.d.ts +1 -1
  66. package/dist/blob/types.js +1 -1
  67. package/dist/core/limits.d.ts +11 -35
  68. package/dist/core/limits.js +5 -73
  69. package/dist/core/sandbox-routes.js +0 -1
  70. package/dist/core/turn-manifest.d.ts +1 -2
  71. package/dist/core/turn-manifest.js +4 -28
  72. package/dist/execution/memory.d.ts +8 -5
  73. package/dist/execution/memory.js +8 -6
  74. package/dist/execution/types.d.ts +1 -6
  75. package/dist/execution/types.js +0 -5
  76. package/dist/gates/contract.d.ts +3 -2
  77. package/dist/gates/contract.js +1 -1
  78. package/dist/gates/egress-credentials.js +4 -3
  79. package/dist/gates/egress.d.ts +9 -9
  80. package/dist/gates/egress.js +19 -19
  81. package/dist/gates/fixture-model.js +13 -2
  82. package/dist/gates/handler.d.ts +1 -3
  83. package/dist/gates/handler.js +4 -3
  84. package/dist/gates/http-client.d.ts +4 -3
  85. package/dist/gates/http-client.js +5 -7
  86. package/dist/gates/http-tool.d.ts +24 -4
  87. package/dist/gates/http-tool.js +53 -17
  88. package/dist/gates/in-process.d.ts +2 -1
  89. package/dist/gates/in-process.js +17 -9
  90. package/dist/gates/inflight.d.ts +2 -2
  91. package/dist/gates/inflight.js +3 -2
  92. package/dist/gates/journal.js +5 -7
  93. package/dist/gates/mcp-handler.d.ts +4 -3
  94. package/dist/gates/mcp-handler.js +15 -5
  95. package/dist/gates/model-gate.d.ts +11 -2
  96. package/dist/gates/policy.d.ts +28 -5
  97. package/dist/gates/policy.js +60 -17
  98. package/dist/gates/provider-gate.d.ts +22 -0
  99. package/dist/gates/provider-gate.js +30 -0
  100. package/dist/gates/tool-calls.d.ts +1 -3
  101. package/dist/gates/tool-calls.js +2 -3
  102. package/dist/gates/tool-client.d.ts +1 -1
  103. package/dist/gates/tool-client.js +5 -6
  104. package/dist/gates/tool-contract.d.ts +8 -5
  105. package/dist/gates/tool-contract.js +1 -1
  106. package/dist/gates/tool-gate.d.ts +18 -7
  107. package/dist/gates/tool-gate.js +49 -7
  108. package/dist/harness/calls.d.ts +27 -25
  109. package/dist/harness/calls.js +40 -46
  110. package/dist/harness/executors.d.ts +10 -3
  111. package/dist/harness/executors.js +53 -23
  112. package/dist/harness/main.js +2 -2
  113. package/dist/harness/pod.d.ts +0 -2
  114. package/dist/harness/pod.js +7 -6
  115. package/dist/harness/run-tokens.d.ts +4 -1
  116. package/dist/harness/run-tokens.js +6 -2
  117. package/dist/harness/service.d.ts +17 -14
  118. package/dist/harness/service.js +21 -35
  119. package/dist/harness/setup.d.ts +0 -2
  120. package/dist/harness/setup.js +4 -9
  121. package/dist/harness/ws-client.d.ts +2 -2
  122. package/dist/harness/ws-client.js +7 -5
  123. package/dist/harness/ws-port.d.ts +3 -4
  124. package/dist/harness/ws-port.js +3 -4
  125. package/dist/harness-api/artifacts.d.ts +20 -0
  126. package/dist/harness-api/artifacts.js +478 -0
  127. package/dist/harness-api/in-process.d.ts +2 -2
  128. package/dist/harness-api/in-process.js +18 -16
  129. package/dist/harness-api/record.d.ts +10 -9
  130. package/dist/harness-api/record.js +102 -157
  131. package/dist/harness-api/renew.d.ts +1 -1
  132. package/dist/harness-api/renew.js +2 -2
  133. package/dist/harness-api/server.d.ts +1 -16
  134. package/dist/harness-api/server.js +82 -73
  135. package/dist/harness-api/start.d.ts +20 -2
  136. package/dist/harness-api/start.js +57 -18
  137. package/dist/harness-api/tool-journal.d.ts +15 -10
  138. package/dist/harness-api/tool-journal.js +26 -24
  139. package/dist/harness-api/workspace.d.ts +0 -4
  140. package/dist/harness-api/workspace.js +2 -45
  141. package/dist/harness-api/ws-server.js +10 -10
  142. package/dist/host/create-host.js +2 -3
  143. package/dist/host/execution.js +2 -3
  144. package/dist/host/gates.d.ts +2 -9
  145. package/dist/host/gates.js +15 -12
  146. package/dist/host/http.d.ts +0 -2
  147. package/dist/host/http.js +0 -27
  148. package/dist/host/main.js +10 -3
  149. package/dist/host/operate.js +12 -3
  150. package/dist/host/stack-config.d.ts +11 -0
  151. package/dist/host/stack-config.js +21 -1
  152. package/dist/index.d.ts +0 -1
  153. package/dist/index.js +0 -1
  154. package/dist/infra/index.js +2 -1
  155. package/dist/infra/readiness.d.ts +0 -2
  156. package/dist/infra/readiness.js +4 -3
  157. package/dist/keys/client.d.ts +1 -0
  158. package/dist/keys/client.js +2 -3
  159. package/dist/keys/contract.d.ts +1 -3
  160. package/dist/keys/contract.js +1 -3
  161. package/dist/keys/egress-ca.d.ts +0 -2
  162. package/dist/keys/egress-ca.js +2 -3
  163. package/dist/keys/keys.js +6 -1
  164. package/dist/keys/x509.d.ts +8 -4
  165. package/dist/keys/x509.js +14 -5
  166. package/dist/management-openapi.json +1961 -994
  167. package/dist/mcp/connect.d.ts +35 -26
  168. package/dist/mcp/connect.js +112 -141
  169. package/dist/mcp/pool.d.ts +27 -22
  170. package/dist/mcp/pool.js +34 -48
  171. package/dist/mcp/preview.d.ts +13 -6
  172. package/dist/mcp/preview.js +37 -20
  173. package/dist/mcp/snapshot.d.ts +35 -86
  174. package/dist/mcp/snapshot.js +93 -135
  175. package/dist/model/auth-store.js +5 -3
  176. package/dist/model/capabilities.d.ts +1 -1
  177. package/dist/model/catalog.d.ts +9 -36
  178. package/dist/model/catalog.js +6 -33
  179. package/dist/model/descriptor.d.ts +51 -0
  180. package/dist/model/descriptor.js +83 -0
  181. package/dist/model/models.d.ts +0 -3
  182. package/dist/model/models.js +3 -5
  183. package/dist/model/native-blocks.js +1 -1
  184. package/dist/model/pi-model.d.ts +16 -5
  185. package/dist/model/pi-model.js +86 -105
  186. package/dist/openapi.json +866 -1218
  187. package/dist/reads/cursor.js +3 -1
  188. package/dist/record/append.d.ts +34 -2
  189. package/dist/record/append.js +50 -1
  190. package/dist/record/environment.d.ts +2 -0
  191. package/dist/record/environment.js +2 -0
  192. package/dist/record/index.d.ts +1 -1
  193. package/dist/record/index.js +1 -1
  194. package/dist/redact.d.ts +1 -1
  195. package/dist/redact.js +3 -2
  196. package/dist/sandbox/egress-token.d.ts +3 -3
  197. package/dist/sandbox/egress-token.js +3 -3
  198. package/dist/sandbox/join.js +3 -2
  199. package/dist/sandbox/manager.d.ts +41 -41
  200. package/dist/sandbox/manager.js +112 -44
  201. package/dist/sandbox/placement.d.ts +1 -1
  202. package/dist/sandbox/placement.js +5 -8
  203. package/dist/sandbox/pods/client.d.ts +1 -10
  204. package/dist/sandbox/pods/client.js +8 -4
  205. package/dist/sandbox/pods/lifecycle.d.ts +19 -11
  206. package/dist/sandbox/pods/lifecycle.js +45 -23
  207. package/dist/sandbox/pods/network-gate.d.ts +4 -5
  208. package/dist/sandbox/pods/network-gate.js +7 -7
  209. package/dist/sandbox/pods/reconcile.js +4 -1
  210. package/dist/sandbox/pods/spec.js +8 -5
  211. package/dist/sandbox/policy.d.ts +7 -1
  212. package/dist/sandbox/policy.js +10 -3
  213. package/dist/sandbox/resolve.d.ts +8 -3
  214. package/dist/sandbox/resolve.js +12 -2
  215. package/dist/sandbox/setup.d.ts +3 -1
  216. package/dist/sandbox/setup.js +34 -11
  217. package/dist/sandbox/share.d.ts +5 -14
  218. package/dist/sandbox/share.js +5 -34
  219. package/dist/sandbox/skills.d.ts +7 -13
  220. package/dist/sandbox/skills.js +16 -31
  221. package/dist/sandbox/tenant-config.d.ts +2 -2
  222. package/dist/sandbox/tenant-config.js +4 -3
  223. package/dist/sandbox/tools.d.ts +7 -1
  224. package/dist/sandbox/tools.js +26 -26
  225. package/dist/sandbox/types.d.ts +10 -3
  226. package/dist/sandbox/types.js +9 -0
  227. package/dist/store/postgres/connect.d.ts +6 -5
  228. package/dist/store/postgres/connect.js +14 -11
  229. package/dist/store/postgres/control.js +13 -13
  230. package/dist/store/postgres/drizzle/0028_artifact_write_keys.sql +2 -0
  231. package/dist/store/postgres/drizzle/0029_protocol_15.sql +11 -0
  232. package/dist/store/postgres/drizzle/0030_protocol_15_runtime_block.sql +11 -0
  233. package/dist/store/postgres/drizzle/0031_protocol_15_mcp_snapshot.sql +8 -0
  234. package/dist/store/postgres/drizzle/meta/_journal.json +28 -0
  235. package/dist/store/postgres/migrate.js +1 -1
  236. package/dist/store/postgres/reads.js +10 -2
  237. package/dist/store/postgres/schema.d.ts +19 -0
  238. package/dist/store/postgres/schema.js +7 -0
  239. package/dist/store/postgres/store.js +55 -26
  240. package/dist/store/types.d.ts +17 -7
  241. package/dist/streams/relay/core.d.ts +3 -3
  242. package/dist/streams/relay/core.js +9 -10
  243. package/dist/tenant/advance.js +21 -31
  244. package/dist/tenant/commands.js +9 -16
  245. package/dist/tenant/context.d.ts +14 -17
  246. package/dist/tenant/definition-files.d.ts +25 -4
  247. package/dist/tenant/definition-files.js +85 -47
  248. package/dist/tenant/effects.js +9 -36
  249. package/dist/tenant/environment-manifests.js +9 -1
  250. package/dist/tenant/environments.d.ts +0 -2
  251. package/dist/tenant/environments.js +54 -30
  252. package/dist/tenant/ephemeral.d.ts +1 -1
  253. package/dist/tenant/ephemeral.js +2 -1
  254. package/dist/tenant/history.d.ts +1 -1
  255. package/dist/tenant/history.js +1 -1
  256. package/dist/tenant/host-token.d.ts +3 -3
  257. package/dist/tenant/host-token.js +12 -11
  258. package/dist/tenant/http.d.ts +5 -8
  259. package/dist/tenant/http.js +9 -0
  260. package/dist/tenant/identity-file.d.ts +24 -1
  261. package/dist/tenant/identity-file.js +35 -7
  262. package/dist/tenant/issuers.d.ts +5 -5
  263. package/dist/tenant/issuers.js +31 -23
  264. package/dist/tenant/jwt.d.ts +8 -2
  265. package/dist/tenant/jwt.js +10 -2
  266. package/dist/tenant/limits.d.ts +90 -0
  267. package/dist/tenant/limits.js +175 -0
  268. package/dist/tenant/module.d.ts +4 -1
  269. package/dist/tenant/module.js +9 -8
  270. package/dist/tenant/outbound.d.ts +17 -19
  271. package/dist/tenant/outbound.js +18 -21
  272. package/dist/tenant/run-grants.js +3 -2
  273. package/dist/tenant/run-token.d.ts +0 -4
  274. package/dist/tenant/run-token.js +6 -11
  275. package/dist/tenant/runtime-limits.d.ts +24 -0
  276. package/dist/tenant/runtime-limits.js +27 -0
  277. package/dist/tenant/runtime.js +30 -43
  278. package/dist/tenant/session-streams.js +7 -6
  279. package/dist/tenant/session.d.ts +1 -1
  280. package/dist/tenant/session.js +1 -1
  281. package/dist/tenant/sessions.js +32 -29
  282. package/dist/tenant/signing-keys.d.ts +1 -1
  283. package/dist/tenant/signing-keys.js +5 -6
  284. package/dist/tenant/status.js +4 -3
  285. package/dist/tenant/streams.js +18 -20
  286. package/dist/tenant/sweep.d.ts +1 -21
  287. package/dist/tenant/sweep.js +39 -48
  288. package/dist/tenant/transcript.d.ts +70 -23
  289. package/dist/tenant/transcript.js +28 -22
  290. package/dist/tenant/types.d.ts +11 -16
  291. package/dist/tenant/worker.d.ts +2 -10
  292. package/dist/tenant/worker.js +5 -14
  293. package/dist/traces/ids.js +5 -1
  294. package/dist/traces/map.d.ts +9 -6
  295. package/dist/traces/map.js +9 -11
  296. package/dist/traces/otlp.d.ts +3 -3
  297. package/dist/traces/otlp.js +5 -3
  298. package/dist/traces/projector.d.ts +9 -4
  299. package/dist/traces/projector.js +13 -7
  300. package/dist/traces/read.js +6 -1
  301. package/dist/tuning.d.ts +383 -0
  302. package/dist/tuning.js +658 -0
  303. package/dist/vault/headers.js +4 -2
  304. package/dist/version.d.ts +1 -1
  305. package/dist/version.js +1 -1
  306. package/package.json +3 -3
  307. package/dist/artifacts/export.d.ts +0 -8
  308. package/dist/artifacts/export.js +0 -204
  309. package/dist/artifacts/media-types.d.ts +0 -30
  310. package/dist/artifacts/media-types.js +0 -137
  311. package/dist/artifacts/workspace.d.ts +0 -60
  312. package/dist/artifacts/workspace.js +0 -45
  313. package/dist/media.d.ts +0 -10
  314. package/dist/media.js +0 -58
  315. package/dist/sandbox/runtime-tools.d.ts +0 -9
  316. package/dist/sandbox/runtime-tools.js +0 -16
  317. package/dist/sandbox/session-sandbox.d.ts +0 -16
  318. package/dist/sandbox/session-sandbox.js +0 -77
  319. package/dist/tenant/artifact-tool.d.ts +0 -13
  320. package/dist/tenant/artifact-tool.js +0 -243
  321. package/dist/tenant/skill-tool.d.ts +0 -20
  322. package/dist/tenant/skill-tool.js +0 -67
  323. package/dist/tenant/tool-results.d.ts +0 -57
  324. package/dist/tenant/tool-results.js +0 -298
  325. package/dist/tenant/tool-search.d.ts +0 -39
  326. package/dist/tenant/tool-search.js +0 -103
@@ -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 { type McpDiagnostic, type McpRenamedTool, type McpToolRecord } from "./snapshot.js";
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`, cut (`serverInstructions`); absent when none. */
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 `MCP_RESULT_MAX_BYTES` (R2b C11), as an HTTP tool's
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
- /** A tool call that never reached its server: its connection could not be opened. */
168
- export declare function unreachable(server: string, error: unknown): McpCallFailed;
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, for the note on its deferred tools
195
- * (R2b C10): cut to `SERVER_INSTRUCTIONS_MAX_CHARS`, without a credential value sent (C8).
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 under the names the model knows them by (R2b C6): `server__tool` where
200
- * that is a name every provider accepts, else `normalizeToolName`'s, listed in `renamed`. A name
201
- * taken by a declared tool or an earlier server's tool omits the tool; a renamed tool that
202
- * collides gets the hash suffix first.
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
  }>;
@@ -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, SERVER_INSTRUCTIONS_MAX_CHARS, mcpToolSettings, schemaFromJSON, } from "@nylorun/core/define";
6
+ import { MCP_TOOL_DEFAULTS, mcpToolSettings } from "@nylorun/core/define";
7
7
  import { scrub, scrubValues } from "../redact.js";
8
- import { guardedFetch, MAX_RESPONSE_BYTES, OutboundFailed, OutboundRefused, } from "../tenant/outbound.js";
8
+ import { guardedFetch, OutboundFailed, OutboundRefused, } from "../tenant/outbound.js";
9
9
  import { credentialSecrets } from "../vault/headers.js";
10
- import { normalizeToolName, } from "./snapshot.js";
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
- /** A tool call that never reached its server: its connection could not be opened. */
100
- export function unreachable(server, error) {
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
- return unreachableFailure(server, error instanceof Error ? error.message : String(error));
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, traceparent) {
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) => tracked(true, async (tracker) => {
140
- const lost = tracker.calling.signal;
141
- try {
142
- const keyed = options?.key === undefined
143
- ? params
144
- : { ...params, _meta: { [IDEMPOTENCY_META_KEY]: options.key } };
145
- const result = (await client.callTool(keyed, undefined, {
146
- signal: options?.signal ? AbortSignal.any([options.signal, lost]) : lost,
147
- }));
148
- // An answer the stream did not count (an `sse` server answers on its event stream).
149
- if (Buffer.byteLength(JSON.stringify(result)) > MCP_RESULT_MAX_BYTES)
150
- throw tooLargeFailure(serverName);
151
- const answer = scrubValues(result, [...tracker.secrets]);
152
- return { result: answer.value, ...(answer.redacted > 0 ? { redacted: answer.redacted } : {}) };
153
- }
154
- catch (error) {
155
- // A cancel or a shutdown: the caller decides what it means.
156
- if (options?.signal?.aborted)
157
- throw error;
158
- throw scrubError(failureOf(serverName, error, tracker), tracker);
159
- }
160
- finally {
161
- tracker.settled = true;
162
- }
163
- }, options?.traceparent),
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, for the note on its deferred tools
207
- * (R2b C10): cut to `SERVER_INSTRUCTIONS_MAX_CHARS`, without a credential value sent (C8).
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().slice(0, SERVER_INSTRUCTIONS_MAX_CHARS), secrets).value;
219
+ return scrubValues(text.trim(), secrets).value;
213
220
  }
214
221
  /**
215
- * Lists a server's tools under the names the model knows them by (R2b C6): `server__tool` where
216
- * that is a name every provider accepts, else `normalizeToolName`'s, listed in `renamed`. A name
217
- * taken by a declared tool or an earlier server's tool omits the tool; a renamed tool that
218
- * collides gets the hash suffix first.
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
- for (let page = 0; page < 100; page += 1) {
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 candidates = [];
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
- if (server && !mcpToolSettings(server, tool.name).enabled) {
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 inputSchema = usableSchema(tool.inputSchema);
245
- if (inputSchema)
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
- serverName: input.serverName,
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, renamed, disabled, unknownTools };
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 core
341
- // stores the bytes as artifacts before anything records them (R2b C11, `tenant/tool-results.ts`).
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, 500)
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
- throw statusFailure(server.name, response.status, await bodyText(response));
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 > MCP_ERROR_BODY_CHARS ? `${body.slice(0, MCP_ERROR_BODY_CHARS)}…` : body;
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, MCP_ERROR_BODY_CHARS)}`,
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, MCP_ERROR_BODY_CHARS)}`,
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, MCP_ERROR_BODY_CHARS)}`,
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 ${MCP_RESULT_MAX_BYTES} bytes, the most a tool result may hold, so the answer was not kept`,
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 `MCP_RESULT_MAX_BYTES` ends it as `mcp.too-large` (C11), before more of it is read.
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 > MCP_RESULT_MAX_BYTES) {
550
+ if (tracker.maxResultBytes !== undefined && received > tracker.maxResultBytes) {
575
551
  tracker.tooLarge = true;
576
- const error = new Error(`The answer passed ${MCP_RESULT_MAX_BYTES} bytes`);
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
- while (size < 4 * MCP_ERROR_BODY_CHARS) {
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
- function usableSchema(value) {
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
- const schema = JSON.parse(JSON.stringify(value));
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))