stitchkit 0.54.0 → 0.56.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.md +4 -1
  2. package/dist/browser/socket-io.d.ts +25 -7
  3. package/dist/browser/socket-io.d.ts.map +1 -1
  4. package/dist/cli.js +14 -9
  5. package/dist/contract/errors.d.ts +7 -0
  6. package/dist/contract/errors.d.ts.map +1 -1
  7. package/dist/contract/file-ref.d.ts +13 -0
  8. package/dist/contract/file-ref.d.ts.map +1 -0
  9. package/dist/contract/index.d.ts +1 -0
  10. package/dist/contract/index.d.ts.map +1 -1
  11. package/dist/contract/index.js +21 -17
  12. package/dist/files/boundary.d.ts +55 -0
  13. package/dist/files/boundary.d.ts.map +1 -0
  14. package/dist/files.d.ts +3 -0
  15. package/dist/files.d.ts.map +1 -0
  16. package/dist/files.js +16 -0
  17. package/dist/index-1bx83sw4.js +4 -0
  18. package/dist/{index-pfqjb5xy.js → index-28cqssm3.js} +30 -5
  19. package/dist/index-557by2db.js +34 -0
  20. package/dist/index-5r13htq1.js +283 -0
  21. package/dist/index-6k1937bx.js +20 -0
  22. package/dist/index-6y759j86.js +1031 -0
  23. package/dist/{index-bmnhqtya.js → index-7eq9va6e.js} +1 -4
  24. package/dist/index-bfcpjw20.js +363 -0
  25. package/dist/{index-yxpe3phd.js → index-cby4ar3v.js} +1 -18
  26. package/dist/{index-7vb44hy8.js → index-escqg10p.js} +193 -751
  27. package/dist/{index-173tvqcp.js → index-j3dem06f.js} +9 -114
  28. package/dist/{index-gvha32jh.js → index-jcc611vh.js} +2 -2
  29. package/dist/{index-z7761jk2.js → index-n4nfa7gh.js} +16 -22
  30. package/dist/{index-e1es808r.js → index-qh4xevf5.js} +1 -1
  31. package/dist/index-sbdmyz75.js +18 -0
  32. package/dist/{index-b15rtha1.js → index-sm2tjx06.js} +98 -3
  33. package/dist/{index-yez419px.js → index-xy8fmh6w.js} +99 -7
  34. package/dist/index.d.ts +2 -2
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +188 -40
  37. package/dist/node.js +31 -27
  38. package/dist/observability/index.js +28 -25
  39. package/dist/react.js +2 -2
  40. package/dist/realtime/contract.d.ts +9 -0
  41. package/dist/realtime/contract.d.ts.map +1 -1
  42. package/dist/realtime/index.d.ts +2 -1
  43. package/dist/realtime/index.d.ts.map +1 -1
  44. package/dist/realtime/request.d.ts +21 -0
  45. package/dist/realtime/request.d.ts.map +1 -0
  46. package/dist/realtime/socket.d.ts +3 -0
  47. package/dist/realtime/socket.d.ts.map +1 -1
  48. package/dist/remote.d.ts +9 -0
  49. package/dist/remote.d.ts.map +1 -0
  50. package/dist/remote.js +79 -0
  51. package/dist/server/context-contribution.d.ts +18 -0
  52. package/dist/server/context-contribution.d.ts.map +1 -0
  53. package/dist/server/context.d.ts.map +1 -1
  54. package/dist/server/index.d.ts +2 -1
  55. package/dist/server/index.d.ts.map +1 -1
  56. package/dist/server/index.js +136 -88
  57. package/dist/server/lifecycle.d.ts +4 -0
  58. package/dist/server/lifecycle.d.ts.map +1 -0
  59. package/dist/server/middleware/auth.d.ts +64 -9
  60. package/dist/server/middleware/auth.d.ts.map +1 -1
  61. package/dist/server/types.d.ts +1 -1
  62. package/dist/server/types.d.ts.map +1 -1
  63. package/dist/testing/surface-conformance.d.ts +180 -0
  64. package/dist/testing/surface-conformance.d.ts.map +1 -0
  65. package/dist/testing/surface-manifest.d.ts +255 -0
  66. package/dist/testing/surface-manifest.d.ts.map +1 -0
  67. package/dist/testing.d.ts +2 -0
  68. package/dist/testing.d.ts.map +1 -1
  69. package/dist/testing.js +714 -3
  70. package/dist/tools/agent.d.ts.map +1 -1
  71. package/dist/tools/async-operation.d.ts +360 -0
  72. package/dist/tools/async-operation.d.ts.map +1 -0
  73. package/dist/tools/cli-args.d.ts +1 -1
  74. package/dist/tools/cli-args.d.ts.map +1 -1
  75. package/dist/tools/cli-format.d.ts +2 -2
  76. package/dist/tools/cli-format.d.ts.map +1 -1
  77. package/dist/tools/cli-wait.d.ts +11 -2
  78. package/dist/tools/cli-wait.d.ts.map +1 -1
  79. package/dist/tools/cli.d.ts +2 -0
  80. package/dist/tools/cli.d.ts.map +1 -1
  81. package/dist/tools/define-download-tool.d.ts +6 -5
  82. package/dist/tools/define-download-tool.d.ts.map +1 -1
  83. package/dist/tools/define-upload-tool.d.ts +3 -1
  84. package/dist/tools/define-upload-tool.d.ts.map +1 -1
  85. package/dist/tools/define-wait-tool.d.ts +1 -1
  86. package/dist/tools/define-wait-tool.d.ts.map +1 -1
  87. package/dist/tools/download-core.d.ts +5 -10
  88. package/dist/tools/download-core.d.ts.map +1 -1
  89. package/dist/tools/internal/surface-projector.d.ts +101 -0
  90. package/dist/tools/internal/surface-projector.d.ts.map +1 -0
  91. package/dist/tools/lifecycle.d.ts +4 -0
  92. package/dist/tools/lifecycle.d.ts.map +1 -0
  93. package/dist/tools/managed-file-error.d.ts +6 -0
  94. package/dist/tools/managed-file-error.d.ts.map +1 -0
  95. package/dist/tools/mcp-prepare.d.ts +3 -21
  96. package/dist/tools/mcp-prepare.d.ts.map +1 -1
  97. package/dist/tools/mcp-round-policy.d.ts +9 -0
  98. package/dist/tools/mcp-round-policy.d.ts.map +1 -0
  99. package/dist/tools/mcp-round.d.ts +0 -4
  100. package/dist/tools/mcp-round.d.ts.map +1 -1
  101. package/dist/tools/mount-download.d.ts +5 -4
  102. package/dist/tools/mount-download.d.ts.map +1 -1
  103. package/dist/tools/mount-upload.d.ts +4 -2
  104. package/dist/tools/mount-upload.d.ts.map +1 -1
  105. package/dist/tools/mount.d.ts +5 -9
  106. package/dist/tools/mount.d.ts.map +1 -1
  107. package/dist/tools/runtime-tool.d.ts.map +1 -1
  108. package/dist/tools/surface.d.ts +7 -0
  109. package/dist/tools/surface.d.ts.map +1 -1
  110. package/dist/tools/upload-core.d.ts +3 -2
  111. package/dist/tools/upload-core.d.ts.map +1 -1
  112. package/dist/tools/view-file.d.ts +4 -3
  113. package/dist/tools/view-file.d.ts.map +1 -1
  114. package/dist/tools/wait-core.d.ts +6 -1
  115. package/dist/tools/wait-core.d.ts.map +1 -1
  116. package/dist/tools.d.ts +3 -2
  117. package/dist/tools.d.ts.map +1 -1
  118. package/dist/tools.js +705 -599
  119. package/llms-full.txt +727 -34
  120. package/package.json +10 -2
package/dist/tools.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  signJwt,
3
3
  verifyPkce
4
- } from "./index-yez419px.js";
4
+ } from "./index-xy8fmh6w.js";
5
5
  import {
6
6
  DEFAULT_CORS_ALLOW_HEADERS,
7
7
  DEFAULT_PROCESS_SIGNALS,
@@ -9,11 +9,9 @@ import {
9
9
  defaultSignalSource,
10
10
  guardSignalCallback,
11
11
  reportSignalError
12
- } from "./index-b15rtha1.js";
12
+ } from "./index-sm2tjx06.js";
13
13
  import {
14
- assertToolName,
15
- assertUniqueToolName,
16
- buildToolPresentationSchema,
14
+ WaitTimeoutError,
17
15
  coerceJsonArgs,
18
16
  collectToolSurface,
19
17
  collectTools,
@@ -25,40 +23,51 @@ import {
25
23
  executeToolMethod,
26
24
  fetchGuarded,
27
25
  fetchPinnedDocument,
28
- flattenToolJsonSchema,
29
26
  formatToolError,
30
- isObjectPresentationSchema,
31
- presentationMetadata,
32
27
  readCapped,
33
28
  runWaitOperation,
34
29
  toolErrorFromResult,
35
- toolResultFromError,
36
- writeDownload
37
- } from "./index-7vb44hy8.js";
30
+ toolResultFromError
31
+ } from "./index-escqg10p.js";
38
32
  import {
39
- isWithinDir,
40
- realPathWithinDir,
41
- toJsonSchema
42
- } from "./index-yxpe3phd.js";
33
+ ManagedFileError
34
+ } from "./index-bfcpjw20.js";
35
+ import"./index-sbdmyz75.js";
43
36
  import {
44
- ApiError,
45
- createClient
46
- } from "./index-e1es808r.js";
47
- import"./index-48ffdxgk.js";
37
+ PORTABLE_JSON_SCHEMA_FORMATS,
38
+ assertToolName,
39
+ assertUniqueToolName,
40
+ findNonPortableFormats,
41
+ findUntypedProperties,
42
+ flattenToolJsonSchema,
43
+ prepareProjectedMcpTools,
44
+ presentationMetadata,
45
+ validateMcpRoundPolicy
46
+ } from "./index-6y759j86.js";
47
+ import"./index-cby4ar3v.js";
48
48
  import {
49
49
  redact
50
- } from "./index-gvha32jh.js";
50
+ } from "./index-jcc611vh.js";
51
51
  import {
52
- AppError,
53
52
  getRequestContext,
54
53
  getTraceId,
55
- mergeMeta,
54
+ normalizeError,
56
55
  runWithRequestContext
57
- } from "./index-173tvqcp.js";
56
+ } from "./index-j3dem06f.js";
57
+ import {
58
+ AppError,
59
+ STITCH_ERROR_STATUS,
60
+ defineContract
61
+ } from "./index-5r13htq1.js";
62
+ import {
63
+ ManagedFilePathSchema,
64
+ ManagedFileRefSchema
65
+ } from "./index-6k1937bx.js";
58
66
  import {
59
67
  isRecord,
60
68
  resolvePropagationContext
61
- } from "./index-bmnhqtya.js";
69
+ } from "./index-7eq9va6e.js";
70
+ import"./index-1bx83sw4.js";
62
71
 
63
72
  // src/tools/agent.ts
64
73
  import { jsonSchema, tool } from "ai";
@@ -85,10 +94,10 @@ function mountAgent(services, config = {}) {
85
94
  const inputSchema = jsonSchema(mountable.presentationSchema, {
86
95
  validate: async (value) => isRecord(value) ? { success: true, value } : { success: false, error: new Error("Tool arguments must be an object") }
87
96
  });
88
- const execute = async (rawArgs) => {
97
+ const execute = async (rawArgs, options) => {
89
98
  const args = isRecord(rawArgs) ? rawArgs : {};
90
99
  try {
91
- const result = await runTool(mountable, args);
100
+ const result = await runTool(mountable, args, { signal: options.abortSignal });
92
101
  if (result.ok)
93
102
  return result.data;
94
103
  return formatToolError(result, mountable.name, config.errorHint);
@@ -128,9 +137,408 @@ function mountAgent(services, config = {}) {
128
137
  }
129
138
  return tools;
130
139
  }
131
- // src/tools/download-core.ts
132
- import { basename, extname, resolve } from "node:path";
140
+ // src/tools/async-operation.ts
133
141
  import { z } from "zod";
142
+
143
+ // src/tools/native-definition.ts
144
+ function managedNativeIdentity(identity, method) {
145
+ return { ...identity, method };
146
+ }
147
+
148
+ // src/tools/define-wait-tool.ts
149
+ function defineWaitTool(config) {
150
+ const renderedMcp = config.render ? {
151
+ mcp: (state) => {
152
+ const rendered = config.render?.(state, false);
153
+ return {
154
+ content: [
155
+ {
156
+ type: "text",
157
+ text: rendered?.text ?? JSON.stringify(state, null, 2)
158
+ }
159
+ ]
160
+ };
161
+ }
162
+ } : undefined;
163
+ const present = renderedMcp || config.present ? { ...renderedMcp, ...config.present } : undefined;
164
+ return defineRuntimeTool({
165
+ name: config.name ?? "wait",
166
+ description: config.description,
167
+ identity: managedNativeIdentity(config.identity, "GET"),
168
+ input: config.input,
169
+ output: config.state,
170
+ transports: config.transports,
171
+ annotations: config.annotations,
172
+ present,
173
+ handler: async (context) => {
174
+ let result;
175
+ try {
176
+ result = await runWaitOperation({
177
+ input: context.input,
178
+ poll: async (input, signal) => config.state.parse(await config.poll(input, { ...context, signal })),
179
+ done: config.done,
180
+ backoff: config.backoff,
181
+ timeoutSec: config.timeoutFromInput?.(context.input) ?? config.defaultTimeout,
182
+ signal: context.signal
183
+ });
184
+ } catch (error) {
185
+ if (error instanceof WaitTimeoutError) {
186
+ throw new AppError("WAIT_TIMEOUT", "Wait timed out before the first snapshot", 408);
187
+ }
188
+ throw error;
189
+ }
190
+ const rendered = config.render?.(result.state, result.timedOut);
191
+ if (result.timedOut) {
192
+ throw new AppError("WAIT_TIMEOUT", rendered?.text ?? "Wait timed out before the operation completed", 408);
193
+ }
194
+ if (rendered?.isError) {
195
+ throw new AppError("WAIT_FAILED", rendered.text, 409);
196
+ }
197
+ return result.state;
198
+ }
199
+ });
200
+ }
201
+
202
+ // src/tools/async-operation.ts
203
+ var AsyncOperationCancelResultSchema = z.discriminatedUnion("outcome", [
204
+ z.object({ outcome: z.literal("accepted") }),
205
+ z.object({ outcome: z.literal("already_terminal") }),
206
+ z.object({ outcome: z.literal("rejected"), reason: z.string().min(1) })
207
+ ]);
208
+ function createAsyncOperationSnapshotSchema(config) {
209
+ const progress = config.progress ? { progress: config.progress.optional() } : {};
210
+ return z.discriminatedUnion("phase", [
211
+ z.object({ phase: z.literal("pending"), ...progress }),
212
+ z.object({ phase: z.literal("running"), ...progress }),
213
+ z.object({ phase: z.literal("succeeded"), ...progress }),
214
+ z.object({ phase: z.literal("failed"), failure: config.failure, ...progress }),
215
+ z.object({ phase: z.literal("cancelled"), ...progress })
216
+ ]);
217
+ }
218
+ function terminal(snapshot) {
219
+ if (typeof snapshot !== "object" || snapshot === null || !("phase" in snapshot)) {
220
+ return false;
221
+ }
222
+ return snapshot.phase === "succeeded" || snapshot.phase === "failed" || snapshot.phase === "cancelled";
223
+ }
224
+ function succeeded(snapshot) {
225
+ return typeof snapshot === "object" && snapshot !== null && "phase" in snapshot && snapshot.phase === "succeeded";
226
+ }
227
+ function defineAsyncOperation(config) {
228
+ const capabilityName = (capability) => config.names?.[capability] ?? `${config.name}_${capability}`;
229
+ const description = (capability) => config.descriptions?.[capability] ?? `${config.description}: ${capability}`;
230
+ const identity = (capability, method) => ({
231
+ serviceName: config.identity.serviceName,
232
+ action: `${config.identity.action}.${capability}`,
233
+ scope: config.scopes?.[capability] ?? config.identity.scope,
234
+ meta: config.identity.meta,
235
+ method
236
+ });
237
+ const common = (capability) => ({
238
+ name: capabilityName(capability),
239
+ description: description(capability),
240
+ transports: config.transports?.[capability],
241
+ annotations: config.annotations?.[capability] ?? (capability === "start" || capability === "cancel" ? { destructiveHint: true } : undefined)
242
+ });
243
+ const inspect = async (capability, context) => {
244
+ await config.authorize(context.input, capability, context);
245
+ const state = config.state.parse(await config.inspect(context.input, context));
246
+ const snapshot = config.snapshot.parse(await config.classify(state, context));
247
+ return { state, snapshot };
248
+ };
249
+ const start = defineRuntimeTool({
250
+ ...common("start"),
251
+ identity: identity("start", "POST"),
252
+ input: config.startInput,
253
+ output: config.id,
254
+ handler: (context) => config.start(context.input, context)
255
+ });
256
+ const status = defineRuntimeTool({
257
+ ...common("status"),
258
+ identity: identity("status", "GET"),
259
+ input: config.id,
260
+ output: config.snapshot,
261
+ handler: async (context) => (await inspect("status", context)).snapshot
262
+ });
263
+ const wait = defineWaitTool({
264
+ ...common("wait"),
265
+ identity: identity("wait", "GET"),
266
+ input: config.id,
267
+ state: config.snapshot,
268
+ poll: async (_id, context) => (await inspect("wait", context)).snapshot,
269
+ done: terminal,
270
+ backoff: config.backoff,
271
+ defaultTimeout: config.defaultTimeout,
272
+ timeoutFromInput: config.timeoutFromId
273
+ });
274
+ const definitions = { start, status, wait };
275
+ const names = new Set;
276
+ if (config.cancel) {
277
+ const capability = config.cancel;
278
+ definitions.cancel = defineRuntimeTool({
279
+ ...common("cancel"),
280
+ identity: identity("cancel", "DELETE"),
281
+ input: config.id,
282
+ output: AsyncOperationCancelResultSchema,
283
+ handler: async (context) => {
284
+ const inspected = await inspect("cancel", context);
285
+ return capability.handler(inspected.state, context);
286
+ }
287
+ });
288
+ }
289
+ if (config.result) {
290
+ const capability = config.result;
291
+ definitions.result = defineRuntimeTool({
292
+ ...common("result"),
293
+ identity: identity("result", "GET"),
294
+ input: config.id,
295
+ output: capability.output,
296
+ handler: async (context) => {
297
+ const inspected = await inspect("result", context);
298
+ if (!succeeded(inspected.snapshot)) {
299
+ throw new AppError("OPERATION_NOT_SUCCEEDED", "Operation result is not available", 409);
300
+ }
301
+ return capability.handler(inspected.state, context);
302
+ }
303
+ });
304
+ }
305
+ if (config.artifacts) {
306
+ const capability = config.artifacts;
307
+ definitions.artifacts = defineRuntimeTool({
308
+ ...common("artifacts"),
309
+ identity: identity("artifacts", "GET"),
310
+ input: config.id,
311
+ output: capability.output,
312
+ handler: async (context) => {
313
+ const inspected = await inspect("artifacts", context);
314
+ if (!succeeded(inspected.snapshot)) {
315
+ throw new AppError("OPERATION_NOT_SUCCEEDED", "Operation artifacts are not available", 409);
316
+ }
317
+ return capability.handler(inspected.state, context);
318
+ }
319
+ });
320
+ }
321
+ for (const definition of Object.values(definitions)) {
322
+ assertUniqueToolName(definition.name, names.has(definition.name), "in-process tool name");
323
+ names.add(definition.name);
324
+ }
325
+ return {
326
+ ...definitions,
327
+ runtimeTools: Object.values(definitions),
328
+ schemas: {
329
+ id: config.id,
330
+ snapshot: config.snapshot,
331
+ cancelResult: AsyncOperationCancelResultSchema
332
+ }
333
+ };
334
+ }
335
+ function adapterResult(capability, target, schema, value) {
336
+ const parsed = schema.safeParse(value);
337
+ if (!parsed.success) {
338
+ throw new Error(`Async operation adapter for capability "${capability}" returned invalid ${target}`, { cause: parsed.error });
339
+ }
340
+ return parsed.data;
341
+ }
342
+ var NON_WIRE_STABLE_ZOD_TYPES = new Set([
343
+ "catch",
344
+ "default",
345
+ "lazy",
346
+ "pipe",
347
+ "prefault",
348
+ "readonly",
349
+ "success",
350
+ "transform"
351
+ ]);
352
+ function hasOverwriteCheck(value) {
353
+ return isRecord(value) && isRecord(value._zod) && isRecord(value._zod.def) && value._zod.def.check === "overwrite";
354
+ }
355
+ function assertWireStableIdSchema(schema, boundary) {
356
+ const visited = new Set;
357
+ const pending = [schema];
358
+ while (pending.length > 0) {
359
+ const value = pending.pop();
360
+ if (typeof value !== "object" || value === null || visited.has(value))
361
+ continue;
362
+ visited.add(value);
363
+ if (value instanceof z.ZodType) {
364
+ const definition = value._zod.def;
365
+ if (NON_WIRE_STABLE_ZOD_TYPES.has(definition.type) || "coerce" in definition && definition.coerce === true || "checks" in definition && Array.isArray(definition.checks) && definition.checks.some(hasOverwriteCheck)) {
366
+ throw new Error(`${boundary} must use a wire-stable ID schema without transforms, coercion, defaults or overwrites; use binding: "adapted" with explicit ID adapters`);
367
+ }
368
+ pending.push(...Object.values(definition));
369
+ continue;
370
+ }
371
+ if (Array.isArray(value)) {
372
+ pending.push(...value);
373
+ continue;
374
+ }
375
+ if (isRecord(value))
376
+ pending.push(...Object.values(value));
377
+ }
378
+ }
379
+ function defineAsyncOperationContract(config) {
380
+ assertWireStableIdSchema(config.id, "Async operation contract");
381
+ const hasStartOutput = config.startOutput !== undefined;
382
+ const hasIdFromStart = config.idFromStart !== undefined;
383
+ if (hasStartOutput !== hasIdFromStart) {
384
+ throw new Error("Async operation contract startOutput and idFromStart must be configured together");
385
+ }
386
+ const description = (capability) => config.descriptions?.[capability] ?? `${config.description}: ${capability}`;
387
+ const endpoint = (capability, method, path, input, output) => {
388
+ const scope = config.scopes?.[capability];
389
+ return {
390
+ method,
391
+ path,
392
+ desc: description(capability),
393
+ input,
394
+ output,
395
+ ...scope !== undefined && { scope }
396
+ };
397
+ };
398
+ const startOutput = config.startOutput ?? config.id;
399
+ const endpoints = {
400
+ start: endpoint("start", "POST", "/", config.startInput, startOutput),
401
+ status: endpoint("status", "POST", "/status", config.id, config.snapshot),
402
+ wait: endpoint("wait", "POST", "/wait", config.id, config.snapshot)
403
+ };
404
+ const capabilities = {
405
+ start: "start",
406
+ status: "status",
407
+ wait: "wait"
408
+ };
409
+ const schemas = {
410
+ startInput: config.startInput,
411
+ startOutput,
412
+ id: config.id,
413
+ snapshot: config.snapshot,
414
+ cancelResult: AsyncOperationCancelResultSchema
415
+ };
416
+ const inputFor = {
417
+ status: (id) => adapterResult("status", "input", config.id, id),
418
+ wait: (id) => adapterResult("wait", "input", config.id, id)
419
+ };
420
+ if (config.cancel) {
421
+ endpoints.cancel = endpoint("cancel", "POST", "/cancel", config.id, AsyncOperationCancelResultSchema);
422
+ capabilities.cancel = "cancel";
423
+ schemas.cancel = AsyncOperationCancelResultSchema;
424
+ inputFor.cancel = (id) => adapterResult("cancel", "input", config.id, id);
425
+ }
426
+ if (config.result) {
427
+ endpoints.result = endpoint("result", "POST", "/result", config.id, config.result);
428
+ capabilities.result = "result";
429
+ schemas.result = config.result;
430
+ inputFor.result = (id) => adapterResult("result", "input", config.id, id);
431
+ }
432
+ if (config.artifacts) {
433
+ endpoints.artifacts = endpoint("artifacts", "POST", "/artifacts", config.id, config.artifacts);
434
+ capabilities.artifacts = "artifacts";
435
+ schemas.artifacts = config.artifacts;
436
+ inputFor.artifacts = (id) => adapterResult("artifacts", "input", config.id, id);
437
+ }
438
+ const contract = config.scope ? defineContract({ prefix: config.prefix, scope: config.scope }, endpoints) : defineContract({ prefix: config.prefix }, endpoints);
439
+ const applicationStartOutput = config.startOutput;
440
+ const applicationIdFromStart = config.idFromStart;
441
+ const idFromStart = applicationStartOutput && applicationIdFromStart ? (output) => adapterResult("start", "id", config.id, applicationIdFromStart(output)) : (output) => adapterResult("start", "id", config.id, output);
442
+ return {
443
+ contract,
444
+ capabilities,
445
+ schemas,
446
+ adapters: { idFromStart, inputFor }
447
+ };
448
+ }
449
+ function bindContractAsyncOperation(config) {
450
+ const endpoint = (key) => {
451
+ const found = config.contract.endpoints[key];
452
+ if (!found)
453
+ throw new Error(`Contract async operation method "${key}" not found`);
454
+ return found;
455
+ };
456
+ const start = endpoint(config.capabilities.start);
457
+ const status = endpoint(config.capabilities.status);
458
+ const wait = endpoint(config.capabilities.wait);
459
+ if (!start.output || !status.output || !wait.output) {
460
+ throw new Error("Contract async operation requires outputs for start, status and wait");
461
+ }
462
+ const startOutput = start.output;
463
+ const statusOutput = status.output;
464
+ if (wait.output !== statusOutput) {
465
+ throw new Error('Contract async operation capability "wait" output must reuse the same schema instance as the status output');
466
+ }
467
+ const optionalCapabilities = [
468
+ "cancel",
469
+ "result",
470
+ "artifacts"
471
+ ];
472
+ if (config.binding !== "adapted") {
473
+ assertWireStableIdSchema(startOutput, "Direct contract async operation");
474
+ if (status.input !== startOutput || wait.input !== startOutput) {
475
+ const capability = status.input !== startOutput ? "status" : "wait";
476
+ throw new Error(`Contract async operation capability "${capability}" input must reuse the same schema instance as the start output`);
477
+ }
478
+ const inputFor2 = {
479
+ status: (id) => adapterResult("status", "input", startOutput, id),
480
+ wait: (id) => adapterResult("wait", "input", startOutput, id)
481
+ };
482
+ for (const capability of optionalCapabilities) {
483
+ const key = config.capabilities[capability];
484
+ if (!key)
485
+ continue;
486
+ if (endpoint(key).input !== startOutput) {
487
+ throw new Error(`Contract async operation capability "${capability}" input must reuse the same schema instance as the start output`);
488
+ }
489
+ inputFor2[capability] = (id) => adapterResult(capability, "input", startOutput, id);
490
+ }
491
+ return {
492
+ contract: config.contract,
493
+ handlers: config.handlers,
494
+ capabilities: config.capabilities,
495
+ schemas: { id: startOutput, snapshot: statusOutput },
496
+ adapters: {
497
+ idFromStart: (output) => adapterResult("start", "id", startOutput, output),
498
+ inputFor: inputFor2
499
+ }
500
+ };
501
+ }
502
+ const adapters = config.adapters;
503
+ const idSchema = config.id;
504
+ const adaptedInput = (capability, key, builder) => {
505
+ const input = endpoint(key).input;
506
+ if (!input) {
507
+ throw new Error(`Contract async operation capability "${capability}" requires an input schema`);
508
+ }
509
+ return (id) => adapterResult(capability, "input", input, builder(id));
510
+ };
511
+ const inputFor = {
512
+ status: adaptedInput("status", config.capabilities.status, adapters.inputFor.status),
513
+ wait: adaptedInput("wait", config.capabilities.wait, adapters.inputFor.wait)
514
+ };
515
+ const optionalInput = (capability, key, builder) => {
516
+ if (!key)
517
+ return;
518
+ if (!builder) {
519
+ throw new Error(`Contract async operation capability "${capability}" requires an input adapter`);
520
+ }
521
+ inputFor[capability] = adaptedInput(capability, key, builder);
522
+ };
523
+ optionalInput("cancel", config.capabilities.cancel, "cancel" in adapters.inputFor ? adapters.inputFor.cancel : undefined);
524
+ optionalInput("result", config.capabilities.result, "result" in adapters.inputFor ? adapters.inputFor.result : undefined);
525
+ optionalInput("artifacts", config.capabilities.artifacts, "artifacts" in adapters.inputFor ? adapters.inputFor.artifacts : undefined);
526
+ if (!idSchema) {
527
+ throw new Error("Contract adapted async operation requires an id schema");
528
+ }
529
+ return {
530
+ contract: config.contract,
531
+ handlers: config.handlers,
532
+ capabilities: config.capabilities,
533
+ schemas: { id: idSchema, snapshot: statusOutput },
534
+ adapters: {
535
+ idFromStart: (output) => adapterResult("start", "id", idSchema, adapters.idFromStart(output)),
536
+ inputFor
537
+ }
538
+ };
539
+ }
540
+ // src/tools/download-core.ts
541
+ import { basename, extname } from "node:path";
134
542
  var DEFAULT_MAX_BYTES = 100 * 1024 * 1024;
135
543
  var MIME_EXT = {
136
544
  "image/png": ".png",
@@ -143,11 +551,6 @@ var MIME_EXT = {
143
551
  "audio/wav": ".wav",
144
552
  "audio/ogg": ".ogg"
145
553
  };
146
- var DownloadResultSchema = z.object({
147
- path: z.string(),
148
- size: z.number().int().nonnegative(),
149
- mimeType: z.string()
150
- });
151
554
 
152
555
  class DownloadOperationError extends Error {
153
556
  code;
@@ -192,19 +595,42 @@ async function runDownloadOperation(config) {
192
595
  await res.body?.cancel();
193
596
  throw new DownloadOperationError("DOWNLOAD_TOO_LARGE", `file exceeds the ${max}-byte cap`, 413);
194
597
  }
195
- const buffer = await readCapped(res, max);
196
- if (!buffer) {
197
- throw new DownloadOperationError("DOWNLOAD_TOO_LARGE", `file exceeds the ${max}-byte cap`, 413);
198
- }
199
- const dir = resolve(config.dir);
200
- const filePath = resolve(dir, `${baseNameFor(config.url)}${extensionFor(config.url, mimeType)}`);
201
- await writeDownload(dir, filePath, buffer);
202
- return { path: filePath, size: buffer.length, mimeType };
598
+ const path = config.path ?? `${baseNameFor(config.url)}${extensionFor(config.url, mimeType)}`;
599
+ return config.files.write(path, res.body ?? new Uint8Array, {
600
+ maxBytes: max,
601
+ mediaType: mimeType,
602
+ signal: config.signal
603
+ });
203
604
  }
204
605
 
205
- // src/tools/native-definition.ts
206
- function managedNativeIdentity(identity, method) {
207
- return { ...identity, method };
606
+ // src/tools/managed-file-error.ts
607
+ var SAFE_MANAGED_FILE_MESSAGES = {
608
+ FILE_INVALID_PATH: "Invalid managed-file path",
609
+ FILE_OUTSIDE_ROOT: "Managed-file path escapes its boundary",
610
+ FILE_NOT_FOUND: "Managed file not found",
611
+ FILE_NOT_REGULAR: "Managed path is not a regular file",
612
+ FILE_INSPECTION_REJECTED: "Managed file rejected by inspection",
613
+ FILE_TOO_LARGE: "Managed file exceeds the configured size limit",
614
+ FILE_EXISTS: "Managed file already exists"
615
+ };
616
+ function managedFileAppError(error) {
617
+ if (!(error instanceof ManagedFileError))
618
+ return null;
619
+ switch (error.code) {
620
+ case "FILE_INVALID_PATH":
621
+ case "FILE_OUTSIDE_ROOT":
622
+ case "FILE_NOT_FOUND":
623
+ case "FILE_NOT_REGULAR":
624
+ case "FILE_INSPECTION_REJECTED":
625
+ case "FILE_TOO_LARGE":
626
+ case "FILE_EXISTS":
627
+ return new AppError(error.code, SAFE_MANAGED_FILE_MESSAGES[error.code], STITCH_ERROR_STATUS[error.code]);
628
+ case "FILE_IO_ERROR":
629
+ return null;
630
+ }
631
+ }
632
+ function normalizeFileToolError(error) {
633
+ return managedFileAppError(error) ?? normalizeError(error);
208
634
  }
209
635
 
210
636
  // src/tools/define-download-tool.ts
@@ -214,7 +640,7 @@ function defineDownloadTool(config) {
214
640
  description: config.description,
215
641
  identity: managedNativeIdentity(config.identity, "POST"),
216
642
  input: config.input,
217
- output: DownloadResultSchema,
643
+ output: ManagedFileRefSchema,
218
644
  transports: config.transports,
219
645
  annotations: config.annotations,
220
646
  present: config.present,
@@ -226,7 +652,8 @@ function defineDownloadTool(config) {
226
652
  try {
227
653
  return await runDownloadOperation({
228
654
  url,
229
- dir: config.dirFromInput?.(context.input) ?? config.defaultDir,
655
+ files: config.files,
656
+ path: config.pathFromInput?.(context.input),
230
657
  allowPrivateHosts: config.allowPrivateHosts,
231
658
  maxBytes: config.maxBytes,
232
659
  timeoutMs: config.timeoutMs,
@@ -236,6 +663,9 @@ function defineDownloadTool(config) {
236
663
  if (error instanceof DownloadOperationError) {
237
664
  throw new AppError(error.code, `Download failed: ${error.message}`, error.status);
238
665
  }
666
+ const managedError = managedFileAppError(error);
667
+ if (managedError)
668
+ throw managedError;
239
669
  throw error;
240
670
  }
241
671
  }
@@ -245,14 +675,14 @@ function defineDownloadTool(config) {
245
675
  import { z as z2 } from "zod";
246
676
 
247
677
  // src/tools/upload-core.ts
248
- function runUploadOperation(path, upload, signal) {
678
+ function runUploadOperation(files, path, upload, signal) {
249
679
  signal?.throwIfAborted();
250
- return upload(path, signal);
680
+ return files.read(path, { signal }).then((source) => upload(source, signal));
251
681
  }
252
682
 
253
683
  // src/tools/define-upload-tool.ts
254
684
  var UploadToolInputSchema = z2.object({
255
- path: z2.string().min(1).describe("Path to a local file on this machine")
685
+ path: ManagedFilePathSchema.describe("Path relative to the configured file boundary")
256
686
  });
257
687
  function defineUploadTool(config) {
258
688
  return defineRuntimeTool({
@@ -264,12 +694,20 @@ function defineUploadTool(config) {
264
694
  transports: config.transports,
265
695
  annotations: config.annotations,
266
696
  present: config.present,
267
- handler: async (context) => runUploadOperation(context.input.path, (path) => config.upload(path, context), context.signal)
697
+ handler: async (context) => {
698
+ try {
699
+ return await runUploadOperation(config.files, context.input.path, (source) => config.upload(source, context), context.signal);
700
+ } catch (error) {
701
+ const managedError = managedFileAppError(error);
702
+ if (managedError)
703
+ throw managedError;
704
+ throw error;
705
+ }
706
+ }
268
707
  });
269
708
  }
270
709
  // src/tools/view-file.ts
271
- import { readFile, stat } from "node:fs/promises";
272
- import { extname as extname2, resolve as resolve2 } from "node:path";
710
+ import { extname as extname2 } from "node:path";
273
711
  import { z as z3 } from "zod";
274
712
  var MAX_INLINE_BYTES = 20 * 1024 * 1024;
275
713
  var MAX_VIEW_FILES = 20;
@@ -323,7 +761,7 @@ async function fetchSource(pathOrUrl, options, maxBytes) {
323
761
  signal: options.signal
324
762
  });
325
763
  if (!res.ok)
326
- throw new Error(`HTTP ${res.status}`);
764
+ throw new AppError("VIEW_HTTP_ERROR", `HTTP ${res.status}`, 502);
327
765
  const headerMime = (res.headers.get("content-type") ?? "").split(";")[0]?.trim() ?? "";
328
766
  const mimeType = headerMime || extMime || "application/octet-stream";
329
767
  if (mimeType.startsWith("video/")) {
@@ -335,32 +773,33 @@ async function fetchSource(pathOrUrl, options, maxBytes) {
335
773
  await res.body?.cancel();
336
774
  return { tooLarge: true, mimeType, bytesRead: 0 };
337
775
  }
338
- const buffer2 = await readCapped(res, maxBytes);
339
- return buffer2 ? { buffer: buffer2, mimeType, bytesRead: buffer2.length } : { tooLarge: true, mimeType, bytesRead: maxBytes };
776
+ const buffer = await readCapped(res, maxBytes);
777
+ return buffer ? { buffer, mimeType, bytesRead: buffer.length } : { tooLarge: true, mimeType, bytesRead: maxBytes };
340
778
  }
341
- if (!options.baseDir) {
342
- throw new Error("local file paths are disabled — set baseDir to allow them");
779
+ if (!options.files) {
780
+ throw new AppError("FILE_INVALID_PATH", "local file paths are disabled — set files to allow them", 400);
343
781
  }
344
782
  options.signal?.throwIfAborted();
345
783
  if (!extMime) {
346
- throw new Error("refusing to read a non-media file");
347
- }
348
- const root = resolve2(options.baseDir);
349
- const target = resolve2(root, pathOrUrl);
350
- if (!isWithinDir(root, target)) {
351
- throw new Error("path escapes the allowed directory");
784
+ throw new AppError("FILE_INSPECTION_REJECTED", "refusing to read a non-media file", 422);
352
785
  }
353
- const realTarget = await realPathWithinDir(root, target);
354
- if (realTarget === null) {
355
- throw new Error("path escapes the allowed directory");
786
+ try {
787
+ const source = await options.files.read(pathOrUrl, {
788
+ maxBytes,
789
+ signal: options.signal
790
+ });
791
+ const buffer = Buffer.from(source.bytes);
792
+ return {
793
+ buffer,
794
+ mimeType: source.ref.mediaType ?? extMime,
795
+ bytesRead: buffer.length
796
+ };
797
+ } catch (error) {
798
+ if (error instanceof ManagedFileError && error.code === "FILE_TOO_LARGE") {
799
+ return { tooLarge: true, mimeType: extMime, bytesRead: maxBytes };
800
+ }
801
+ throw error;
356
802
  }
357
- const info = await stat(realTarget).catch(() => null);
358
- if (!info?.isFile())
359
- throw new Error("file not found");
360
- if (info.size > maxBytes)
361
- return { tooLarge: true, mimeType: extMime, bytesRead: 0 };
362
- const buffer = await readFile(realTarget, { signal: options.signal });
363
- return { buffer, mimeType: extMime, bytesRead: buffer.length };
364
803
  }
365
804
  async function resolveMediaWithinBudget(pathOrUrl, options, maxBytes) {
366
805
  if (maxBytes <= 0) {
@@ -448,7 +887,7 @@ async function runViewFileOperation(paths, options = {}, signal) {
448
887
  } catch (error) {
449
888
  if (signal?.aborted)
450
889
  throw signal.reason ?? error;
451
- const message = error instanceof Error ? error.message : String(error);
890
+ const message = normalizeFileToolError(error).message;
452
891
  errors.push({ path: pathOrUrl, message });
453
892
  content.push({ type: "text", text: `[${pathOrUrl}] Error: ${message}` });
454
893
  }
@@ -497,57 +936,12 @@ function defineViewFileTool(config) {
497
936
  },
498
937
  present: { ...defaults, ...config.present },
499
938
  handler: (context) => runViewFileOperation(context.input.paths, {
500
- baseDir: config.baseDir,
939
+ files: config.files,
501
940
  allowPrivateHosts: config.allowPrivateHosts,
502
941
  timeoutMs: config.timeoutMs
503
942
  }, context.signal)
504
943
  });
505
944
  }
506
- // src/tools/define-wait-tool.ts
507
- function defineWaitTool(config) {
508
- const renderedMcp = config.render ? {
509
- mcp: (state) => {
510
- const rendered = config.render?.(state, false);
511
- return {
512
- content: [
513
- {
514
- type: "text",
515
- text: rendered?.text ?? JSON.stringify(state, null, 2)
516
- }
517
- ]
518
- };
519
- }
520
- } : undefined;
521
- const present = renderedMcp || config.present ? { ...renderedMcp, ...config.present } : undefined;
522
- return defineRuntimeTool({
523
- name: config.name ?? "wait",
524
- description: config.description,
525
- identity: managedNativeIdentity(config.identity, "GET"),
526
- input: config.input,
527
- output: config.state,
528
- transports: config.transports,
529
- annotations: config.annotations,
530
- present,
531
- handler: async (context) => {
532
- const result = await runWaitOperation({
533
- input: context.input,
534
- poll: async (input) => config.state.parse(await config.poll(input, context)),
535
- done: config.done,
536
- backoff: config.backoff,
537
- timeoutSec: config.timeoutFromInput?.(context.input) ?? config.defaultTimeout,
538
- signal: context.signal
539
- });
540
- const rendered = config.render?.(result.state, result.timedOut);
541
- if (result.timedOut) {
542
- throw new AppError("WAIT_TIMEOUT", rendered?.text ?? "Wait timed out before the operation completed", 408);
543
- }
544
- if (rendered?.isError) {
545
- throw new AppError("WAIT_FAILED", rendered.text, 409);
546
- }
547
- return result.state;
548
- }
549
- });
550
- }
551
945
  // src/tools/invoker.ts
552
946
  function createToolInvoker(services, config) {
553
947
  const serviceList = Array.isArray(services) ? services : [services];
@@ -595,6 +989,24 @@ function createToolInvoker(services, config) {
595
989
  }
596
990
  });
597
991
  }
992
+ // src/tools/lifecycle.ts
993
+ function composeToolLifecycle(...hooks) {
994
+ return {
995
+ beforeHandle: async (ctx, endpoint) => {
996
+ for (const hook of hooks)
997
+ await hook?.beforeHandle?.(ctx, endpoint);
998
+ },
999
+ afterHandle: async (ctx, result, endpoint) => {
1000
+ let current = result;
1001
+ for (const hook of hooks) {
1002
+ const transformed = await hook?.afterHandle?.(ctx, current, endpoint);
1003
+ if (transformed !== undefined)
1004
+ current = transformed;
1005
+ }
1006
+ return current;
1007
+ }
1008
+ };
1009
+ }
598
1010
  // src/tools/list-names.ts
599
1011
  var TOOL_TRANSPORTS = ["MCP", "AGENT", "CLI"];
600
1012
  function listToolNames(surface) {
@@ -667,6 +1079,95 @@ function inlineMcpAppBundle(html) {
667
1079
  return html.replace(EXT_APPS_BUNDLE_PLACEHOLDER, () => bundle);
668
1080
  }
669
1081
 
1082
+ // src/tools/mcp-prepare.ts
1083
+ function textBlock(text) {
1084
+ return [{ type: "text", text }];
1085
+ }
1086
+ function formatMcpResult(result, mode, toolName, errorHint) {
1087
+ if (result.ok) {
1088
+ if (mode === "none")
1089
+ return { content: [] };
1090
+ let serialized;
1091
+ try {
1092
+ serialized = JSON.stringify(result.data, null, 2);
1093
+ } catch {
1094
+ serialized = undefined;
1095
+ }
1096
+ if (serialized === undefined) {
1097
+ const safe = redact(result.data, { sensitiveKeys: /(?!)/ });
1098
+ return { content: textBlock(JSON.stringify(safe, null, 2)), structuredContent: safe };
1099
+ }
1100
+ return { content: textBlock(serialized), structuredContent: result.data };
1101
+ }
1102
+ return {
1103
+ content: textBlock(JSON.stringify(formatToolError(result, toolName, errorHint), null, 2)),
1104
+ isError: true
1105
+ };
1106
+ }
1107
+ function prepareMcpSurface(services, config = {}) {
1108
+ const serviceList = Array.isArray(services) ? services : [services];
1109
+ const tools = serviceList.flatMap((service) => collectTools(service, "MCP", config));
1110
+ return prepareMcpTools(tools, config);
1111
+ }
1112
+ function prepareMcpTools(tools, config = {}) {
1113
+ const prepared = prepareProjectedMcpTools(tools.map((mountable) => ({
1114
+ tool: mountable,
1115
+ name: mountable.name,
1116
+ ...mountable.method.paramsSchema !== undefined && {
1117
+ paramsSchema: mountable.method.paramsSchema
1118
+ },
1119
+ ...mountable.method.inputSchema !== undefined && {
1120
+ inputSchema: mountable.method.inputSchema
1121
+ },
1122
+ ...mountable.method.outputSchema !== undefined && {
1123
+ outputSchema: mountable.method.outputSchema
1124
+ },
1125
+ shouldExtend: mountable.shouldExtend,
1126
+ ...mountable.method.mcp !== undefined && { mcp: mountable.method.mcp }
1127
+ })), config).map((entry) => {
1128
+ Object.freeze(entry.tool);
1129
+ return Object.freeze({
1130
+ mountable: entry.tool,
1131
+ inputSchema: entry.inputSchema,
1132
+ ...entry.outputSchema !== undefined && { outputSchema: entry.outputSchema },
1133
+ outputMode: entry.outputSchema === undefined ? "none" : "direct"
1134
+ });
1135
+ });
1136
+ return Object.freeze(prepared);
1137
+ }
1138
+ function prepareMcpServerSurface(surface, config = {}) {
1139
+ const contractMountables = [];
1140
+ const definitions = [];
1141
+ const runtimeMountables = [];
1142
+ for (const entry of collectToolSurface({
1143
+ surface,
1144
+ transport: "MCP",
1145
+ extend: config.extend,
1146
+ flattenUnionInput: config.flattenUnionInput
1147
+ })) {
1148
+ if (entry.kind === "contract") {
1149
+ contractMountables.push(entry.mountable);
1150
+ } else {
1151
+ definitions.push(entry.definition);
1152
+ runtimeMountables.push(entry.mountable);
1153
+ }
1154
+ }
1155
+ const contractTools = prepareMcpTools(contractMountables, config);
1156
+ const runtimeDescriptors = prepareMcpTools(runtimeMountables, {
1157
+ schemaValidation: config.schemaValidation,
1158
+ logger: config.logger,
1159
+ multiRound: config.multiRound
1160
+ });
1161
+ const descriptorsByName = new Map(runtimeDescriptors.map((descriptor) => [descriptor.mountable.name, descriptor]));
1162
+ const runtimeTools = [];
1163
+ for (const definition of definitions) {
1164
+ const descriptor = descriptorsByName.get(definition.name);
1165
+ if (descriptor)
1166
+ runtimeTools.push(Object.freeze({ definition, descriptor }));
1167
+ }
1168
+ return Object.freeze({ contractTools, runtimeTools: Object.freeze(runtimeTools) });
1169
+ }
1170
+
670
1171
  // src/tools/mcp-round.ts
671
1172
  import {
672
1173
  acceptedContent,
@@ -746,29 +1247,7 @@ async function runRoundFailure(tool2, rawArgs, runTool, context, code, message)
746
1247
  throw new AppError(code, message, 400);
747
1248
  }
748
1249
  }
749
- }, rawArgs, context);
750
- }
751
- function validateMcpRoundPolicy(tool2, policy, runtime) {
752
- if (!runtime?.stateConfigured) {
753
- throw new Error(`[stitchkit] MCP tool "${tool2.name}" declares inputRequired but no multiRound.state key is configured`);
754
- }
755
- const { maxRounds } = runtime;
756
- if (!Number.isInteger(maxRounds) || maxRounds < 1) {
757
- throw new Error("[stitchkit] multiRound.serving.maxRounds must be a positive integer");
758
- }
759
- if (policy.inputRequired.length === 0) {
760
- throw new Error(`[stitchkit] MCP tool "${tool2.name}" must declare at least one input round`);
761
- }
762
- if (policy.inputRequired.length > maxRounds) {
763
- throw new Error(`[stitchkit] MCP tool "${tool2.name}" declares ${policy.inputRequired.length} input rounds, exceeding maxRounds ${maxRounds}`);
764
- }
765
- const keys = new Set;
766
- for (const request of policy.inputRequired) {
767
- if (keys.has(request.key)) {
768
- throw new Error(`[stitchkit] MCP tool "${tool2.name}" declares duplicate input key "${request.key}"`);
769
- }
770
- keys.add(request.key);
771
- }
1250
+ }, rawArgs, context);
772
1251
  }
773
1252
  async function failedResolution(options) {
774
1253
  const result = await runRoundFailure(options.tool, options.rawArgs, options.runTool, options.context, options.code, options.message);
@@ -902,329 +1381,6 @@ async function resolveMcpRound(options) {
902
1381
  };
903
1382
  }
904
1383
 
905
- // src/tools/portable-formats.ts
906
- var PORTABLE_JSON_SCHEMA_FORMATS = new Set([
907
- "date",
908
- "time",
909
- "date-time",
910
- "duration",
911
- "uri",
912
- "uri-reference",
913
- "uri-template",
914
- "email",
915
- "hostname",
916
- "ipv4",
917
- "ipv6",
918
- "regex",
919
- "uuid",
920
- "json-pointer",
921
- "relative-json-pointer"
922
- ]);
923
- function propertyPath(prefix, property) {
924
- return prefix ? `${prefix}.${property}` : property;
925
- }
926
- function keywordPath(prefix, keyword) {
927
- if (prefix && keyword.startsWith("["))
928
- return `${prefix}${keyword}`;
929
- return prefix ? `${prefix}.${keyword}` : keyword;
930
- }
931
- function findNonPortableFormats(schema, allowFormats = []) {
932
- const allowed = new Set([...PORTABLE_JSON_SCHEMA_FORMATS, ...allowFormats]);
933
- const found = [];
934
- const pending = [{ value: schema, path: "" }];
935
- while (pending.length > 0) {
936
- const current = pending.pop();
937
- if (!current || !isRecord(current.value))
938
- continue;
939
- const format = current.value.format;
940
- if (typeof format === "string" && !allowed.has(format)) {
941
- found.push({ path: current.path || "(root)", format });
942
- }
943
- const properties = current.value.properties;
944
- if (isRecord(properties)) {
945
- for (const [key, value] of Object.entries(properties)) {
946
- pending.push({ value, path: propertyPath(current.path, key) });
947
- }
948
- }
949
- for (const mapKey of ["$defs", "definitions", "patternProperties", "dependentSchemas"]) {
950
- const entries = current.value[mapKey];
951
- if (!isRecord(entries))
952
- continue;
953
- for (const [key, value] of Object.entries(entries)) {
954
- pending.push({ value, path: keywordPath(current.path, `${mapKey}.${key}`) });
955
- }
956
- }
957
- const directChildren = [
958
- ["items", "[]"],
959
- ["contains", "contains"],
960
- ["additionalProperties", "*"],
961
- ["unevaluatedProperties", "*"],
962
- ["propertyNames", "propertyNames"],
963
- ["not", "not"],
964
- ["if", "if"],
965
- ["then", "then"],
966
- ["else", "else"]
967
- ];
968
- for (const [keyword, segment] of directChildren) {
969
- const child = current.value[keyword];
970
- if (isRecord(child)) {
971
- pending.push({ value: child, path: keywordPath(current.path, segment) });
972
- }
973
- if (Array.isArray(child)) {
974
- child.forEach((value, index) => {
975
- pending.push({ value, path: keywordPath(current.path, `${segment}[${index}]`) });
976
- });
977
- }
978
- }
979
- for (const keyword of ["prefixItems", "allOf", "anyOf", "oneOf"]) {
980
- const branches = current.value[keyword];
981
- if (!Array.isArray(branches))
982
- continue;
983
- branches.forEach((value, index) => {
984
- pending.push({ value, path: keywordPath(current.path, `${keyword}[${index}]`) });
985
- });
986
- }
987
- }
988
- return found.sort((left, right) => left.path.localeCompare(right.path) || left.format.localeCompare(right.format));
989
- }
990
-
991
- // src/tools/untyped-properties.ts
992
- var TYPE_KEYWORDS = ["type", "enum", "const", "anyOf", "oneOf", "allOf", "$ref", "not"];
993
- function saysWhatItIs(schema) {
994
- return TYPE_KEYWORDS.some((keyword) => schema[keyword] !== undefined);
995
- }
996
- function findUntypedProperties(schema, prefix = "") {
997
- if (!isRecord(schema))
998
- return [];
999
- const found = [];
1000
- const properties = schema.properties;
1001
- if (isRecord(properties)) {
1002
- for (const [key, value] of Object.entries(properties)) {
1003
- const path = prefix ? `${prefix}.${key}` : key;
1004
- if (isRecord(value)) {
1005
- if (!saysWhatItIs(value)) {
1006
- const description = value.description;
1007
- found.push({
1008
- path,
1009
- ...typeof description === "string" && { description }
1010
- });
1011
- }
1012
- found.push(...findUntypedProperties(value, path));
1013
- }
1014
- }
1015
- }
1016
- for (const key of ["items", "additionalProperties"]) {
1017
- const child = schema[key];
1018
- if (isRecord(child))
1019
- found.push(...findUntypedProperties(child, prefix));
1020
- if (Array.isArray(child)) {
1021
- for (const entry of child) {
1022
- if (isRecord(entry))
1023
- found.push(...findUntypedProperties(entry, prefix));
1024
- }
1025
- }
1026
- }
1027
- for (const key of ["prefixItems"]) {
1028
- const child = schema[key];
1029
- if (Array.isArray(child)) {
1030
- for (const entry of child) {
1031
- if (isRecord(entry))
1032
- found.push(...findUntypedProperties(entry, prefix));
1033
- }
1034
- }
1035
- }
1036
- const patterned = schema.patternProperties;
1037
- if (isRecord(patterned)) {
1038
- for (const value of Object.values(patterned)) {
1039
- if (isRecord(value))
1040
- found.push(...findUntypedProperties(value, prefix));
1041
- }
1042
- }
1043
- for (const key of ["$defs", "definitions"]) {
1044
- const container = schema[key];
1045
- if (!isRecord(container))
1046
- continue;
1047
- for (const definition of Object.values(container)) {
1048
- if (isRecord(definition))
1049
- found.push(...findUntypedProperties(definition, prefix));
1050
- }
1051
- }
1052
- for (const key of ["allOf", "anyOf", "oneOf"]) {
1053
- const branches = schema[key];
1054
- if (Array.isArray(branches)) {
1055
- for (const branch of branches)
1056
- found.push(...findUntypedProperties(branch, prefix));
1057
- }
1058
- }
1059
- return found;
1060
- }
1061
-
1062
- // src/tools/mcp-prepare.ts
1063
- function textBlock(text) {
1064
- return [{ type: "text", text }];
1065
- }
1066
- function formatMcpResult(result, mode, toolName, errorHint) {
1067
- if (result.ok) {
1068
- if (mode === "none")
1069
- return { content: [] };
1070
- let serialized;
1071
- try {
1072
- serialized = JSON.stringify(result.data, null, 2);
1073
- } catch {
1074
- serialized = undefined;
1075
- }
1076
- if (serialized === undefined) {
1077
- const safe = redact(result.data, { sensitiveKeys: /(?!)/ });
1078
- return { content: textBlock(JSON.stringify(safe, null, 2)), structuredContent: safe };
1079
- }
1080
- return { content: textBlock(serialized), structuredContent: result.data };
1081
- }
1082
- return {
1083
- content: textBlock(JSON.stringify(formatToolError(result, toolName, errorHint), null, 2)),
1084
- isError: true
1085
- };
1086
- }
1087
- function probeSchema(schema, io) {
1088
- try {
1089
- toJsonSchema(schema, io);
1090
- return null;
1091
- } catch (err) {
1092
- return err instanceof Error ? err.message : String(err);
1093
- }
1094
- }
1095
- function resolveOutputSchema(outputSchema) {
1096
- if (!outputSchema)
1097
- return null;
1098
- return { schema: outputSchema, mode: "direct" };
1099
- }
1100
- function reportIncompatible(message, policy, logger, failures) {
1101
- if (policy === "throw") {
1102
- failures.push(message);
1103
- } else if (policy === "warn") {
1104
- if (logger)
1105
- logger.warn(`[stitchkit] ${message}`);
1106
- else
1107
- console.warn(`[stitchkit] ${message}`);
1108
- }
1109
- }
1110
- function throwIfFailures(failures) {
1111
- if (failures.length > 0) {
1112
- throw new Error(`[stitchkit] ${failures.length} problem(s) with MCP tool schemas:
1113
- - ${failures.join(`
1114
- - `)}`);
1115
- }
1116
- }
1117
- function prepareMcpTool(mountable, config, validation, logger, failures, seen) {
1118
- const policy = validation.policy ?? "throw";
1119
- assertUniqueToolName(mountable.name, seen.has(mountable.name), "MCP tool name");
1120
- seen.add(mountable.name);
1121
- if (mountable.method.mcp) {
1122
- validateMcpRoundPolicy(mountable, mountable.method.mcp, config.multiRound);
1123
- }
1124
- let inputJsonSchema;
1125
- try {
1126
- inputJsonSchema = buildToolPresentationSchema({
1127
- paramsSchema: mountable.method.paramsSchema,
1128
- inputSchema: mountable.method.inputSchema,
1129
- extendSchema: mountable.shouldExtend && config.extend ? config.extend.schema : undefined,
1130
- flattenUnionInput: config.flattenUnionInput,
1131
- unrepresentable: "throw"
1132
- });
1133
- } catch (err) {
1134
- reportIncompatible(`MCP tool "${mountable.name}" — input schema is not JSON Schema-compatible: ${err instanceof Error ? err.message : String(err)}`, policy, logger, failures);
1135
- return null;
1136
- }
1137
- if (!isObjectPresentationSchema(inputJsonSchema)) {
1138
- reportIncompatible(`MCP tool "${mountable.name}" — input must be an object schema; a union, discriminated union or scalar cannot be an MCP tool input (flatten it in the contract, or drop MCP from \`expose\`)`, policy, logger, failures);
1139
- return null;
1140
- }
1141
- if (validation.requireTypedProperties) {
1142
- const allowed = new Set(validation.allowUntyped ?? []);
1143
- for (const untyped of findUntypedProperties(inputJsonSchema)) {
1144
- const path = `${mountable.name}.${untyped.path}`;
1145
- if (allowed.has(path))
1146
- continue;
1147
- const clue = untyped.description ? ` (only a description: "${untyped.description}")` : "";
1148
- reportIncompatible(`MCP tool "${mountable.name}" — input property "${untyped.path}" carries no type, enum or $ref${clue}. ` + "A model is given no way to know what to send. Use `z.json()` for an arbitrary JSON value; use `allowUntyped` only when the presentation value is genuinely not representable as JSON Schema.", policy === "skip" ? "warn" : policy, logger, failures);
1149
- }
1150
- }
1151
- if (validation.requirePortableFormats) {
1152
- for (const finding of findNonPortableFormats(inputJsonSchema, validation.allowFormats)) {
1153
- reportIncompatible(`MCP tool "${mountable.name}" — input property "${finding.path}" uses non-portable JSON Schema format "${finding.format}". ` + "Use a portable pattern/schema, or list the format in `allowFormats` only when every MCP client supports it.", policy === "skip" ? "warn" : policy, logger, failures);
1154
- }
1155
- }
1156
- const resolved = resolveOutputSchema(mountable.method.outputSchema);
1157
- if (!resolved)
1158
- return { mountable, inputSchema: inputJsonSchema, outputMode: "none" };
1159
- const outputError = probeSchema(resolved.schema, "output");
1160
- if (outputError) {
1161
- reportIncompatible(`MCP tool "${mountable.name}" — output schema is not JSON Schema-compatible: ${outputError}`, policy, logger, failures);
1162
- return { mountable, inputSchema: inputJsonSchema, outputMode: "none" };
1163
- }
1164
- if (validation.requirePortableFormats) {
1165
- for (const finding of findNonPortableFormats(toJsonSchema(resolved.schema, "output"), validation.allowFormats)) {
1166
- reportIncompatible(`MCP tool "${mountable.name}" — output property "${finding.path}" uses non-portable JSON Schema format "${finding.format}". ` + "Use a portable pattern/schema, or list the format in `allowFormats` only when every MCP client supports it.", policy === "skip" ? "warn" : policy, logger, failures);
1167
- }
1168
- }
1169
- return {
1170
- mountable,
1171
- inputSchema: inputJsonSchema,
1172
- outputSchema: resolved.schema,
1173
- outputMode: resolved.mode
1174
- };
1175
- }
1176
- function prepareMcpSurface(services, config = {}) {
1177
- const serviceList = Array.isArray(services) ? services : [services];
1178
- const tools = serviceList.flatMap((service) => collectTools(service, "MCP", config));
1179
- return prepareMcpTools(tools, config);
1180
- }
1181
- function prepareMcpTools(tools, config = {}) {
1182
- const seen = new Set;
1183
- const failures = [];
1184
- const prepared = [];
1185
- for (const mountable of tools) {
1186
- const tool2 = prepareMcpTool(mountable, config, config.schemaValidation ?? {}, config.logger, failures, seen);
1187
- if (tool2) {
1188
- Object.freeze(tool2.mountable);
1189
- prepared.push(Object.freeze(tool2));
1190
- }
1191
- }
1192
- throwIfFailures(failures);
1193
- return Object.freeze(prepared);
1194
- }
1195
- function prepareMcpServerSurface(surface, config = {}) {
1196
- const contractMountables = [];
1197
- const definitions = [];
1198
- const runtimeMountables = [];
1199
- for (const entry of collectToolSurface({
1200
- surface,
1201
- transport: "MCP",
1202
- extend: config.extend,
1203
- flattenUnionInput: config.flattenUnionInput
1204
- })) {
1205
- if (entry.kind === "contract") {
1206
- contractMountables.push(entry.mountable);
1207
- } else {
1208
- definitions.push(entry.definition);
1209
- runtimeMountables.push(entry.mountable);
1210
- }
1211
- }
1212
- const contractTools = prepareMcpTools(contractMountables, config);
1213
- const runtimeDescriptors = prepareMcpTools(runtimeMountables, {
1214
- schemaValidation: config.schemaValidation,
1215
- logger: config.logger,
1216
- multiRound: config.multiRound
1217
- });
1218
- const descriptorsByName = new Map(runtimeDescriptors.map((descriptor) => [descriptor.mountable.name, descriptor]));
1219
- const runtimeTools = [];
1220
- for (const definition of definitions) {
1221
- const descriptor = descriptorsByName.get(definition.name);
1222
- if (descriptor)
1223
- runtimeTools.push(Object.freeze({ definition, descriptor }));
1224
- }
1225
- return Object.freeze({ contractTools, runtimeTools: Object.freeze(runtimeTools) });
1226
- }
1227
-
1228
1384
  // src/tools/mcp-trace.ts
1229
1385
  import {
1230
1386
  BAGGAGE_META_KEY,
@@ -1610,8 +1766,8 @@ function createMcpHandler(config) {
1610
1766
  const authByCarrier = new WeakMap;
1611
1767
  let closed = false;
1612
1768
  let resolveClosed;
1613
- const closedSignal = new Promise((resolve3) => {
1614
- resolveClosed = resolve3;
1769
+ const closedSignal = new Promise((resolve) => {
1770
+ resolveClosed = resolve;
1615
1771
  });
1616
1772
  const sdkHandler = createSdkMcpHandler(({ authInfo }) => {
1617
1773
  const resolved = authInfo ? authByCarrier.get(authInfo) : undefined;
@@ -1714,8 +1870,8 @@ function bindStdioProcessSignals(handle, options = {}) {
1714
1870
  let rejectChain = () => {
1715
1871
  return;
1716
1872
  };
1717
- const promise = new Promise((resolve3, reject) => {
1718
- resolveChain = resolve3;
1873
+ const promise = new Promise((resolve, reject) => {
1874
+ resolveChain = resolve;
1719
1875
  rejectChain = reject;
1720
1876
  });
1721
1877
  promise.catch(() => {
@@ -1811,44 +1967,51 @@ function mountDownload(server, config) {
1811
1967
  return textResult("Nothing to download.", true);
1812
1968
  const result = await runDownloadOperation({
1813
1969
  url,
1814
- dir: config.dirFromArgs?.(args) ?? config.defaultDir,
1970
+ files: config.files,
1971
+ path: config.pathFromArgs?.(args),
1815
1972
  allowPrivateHosts: config.allowPrivateHosts,
1816
1973
  maxBytes: config.maxBytes,
1817
1974
  timeoutMs: config.timeoutMs
1818
1975
  });
1819
1976
  return textResult(JSON.stringify(result, null, 2));
1820
- } catch (err) {
1821
- return textResult(`Download failed: ${err instanceof Error ? err.message : String(err)}`, true);
1977
+ } catch (error) {
1978
+ if (error instanceof DownloadOperationError) {
1979
+ return textResult(`Download failed [${error.code}]: ${error.message}`, true);
1980
+ }
1981
+ const normalized = normalizeFileToolError(error);
1982
+ return textResult(`Download failed [${normalized.code}]: ${normalized.message}`, true);
1822
1983
  }
1823
1984
  });
1824
1985
  }
1825
1986
  // src/tools/mount-upload.ts
1826
- import { z as z7 } from "zod";
1827
1987
  function mountUpload(server, config) {
1828
1988
  const name = config.name ?? "upload";
1829
1989
  assertToolName(name, "<native>", "upload");
1830
1990
  server.registerTool(name, {
1831
1991
  description: config.description,
1832
- inputSchema: { path: z7.string().describe("Path to a local file on this machine") }
1992
+ inputSchema: {
1993
+ path: ManagedFilePathSchema.describe("Path relative to the configured file boundary")
1994
+ }
1833
1995
  }, async (rawArgs) => {
1834
1996
  const args = isRecord(rawArgs) ? rawArgs : {};
1835
1997
  const path = typeof args.path === "string" ? args.path : "";
1836
1998
  if (!path)
1837
1999
  return textResult("Provide `path`.", true);
1838
2000
  try {
1839
- const uploaded = await runUploadOperation(path, config.upload);
2001
+ const uploaded = await runUploadOperation(config.files, path, config.upload);
1840
2002
  return textResult(JSON.stringify(uploaded ?? null, null, 2));
1841
- } catch (err) {
1842
- return textResult(`Upload failed: ${err instanceof Error ? err.message : String(err)}`, true);
2003
+ } catch (error) {
2004
+ const normalized = normalizeFileToolError(error);
2005
+ return textResult(`Upload failed [${normalized.code}]: ${normalized.message}`, true);
1843
2006
  }
1844
2007
  });
1845
2008
  }
1846
2009
  // src/tools/mount-wait.ts
1847
- import { z as z8 } from "zod";
2010
+ import { z as z7 } from "zod";
1848
2011
  function mountWait(server, config) {
1849
2012
  const name = config.name ?? "wait";
1850
2013
  assertToolName(name, "<native>", "wait");
1851
- server.registerTool(name, { description: config.description, inputSchema: z8.object(config.inputSchema) }, async (rawArgs) => {
2014
+ server.registerTool(name, { description: config.description, inputSchema: z7.object(config.inputSchema) }, async (rawArgs) => {
1852
2015
  const args = isRecord(rawArgs) ? rawArgs : {};
1853
2016
  try {
1854
2017
  const { state, timedOut } = await runWaitOperation({
@@ -1867,19 +2030,19 @@ function mountWait(server, config) {
1867
2030
  }
1868
2031
  // src/tools/oauth-provider.ts
1869
2032
  import { randomUUID } from "node:crypto";
1870
- import { z as z9 } from "zod";
2033
+ import { z as z8 } from "zod";
1871
2034
  function isPlainDisplayCharacter(character) {
1872
2035
  const codePoint = character.codePointAt(0);
1873
2036
  return codePoint !== undefined && codePoint >= 32 && codePoint !== 127 && character !== "<" && character !== ">";
1874
2037
  }
1875
- var CimdClientMetadataSchema = z9.object({
1876
- client_id: z9.url(),
1877
- redirect_uris: z9.array(z9.url()).min(1),
1878
- client_name: z9.string().trim().min(1).max(200).refine((value) => Array.from(value).every(isPlainDisplayCharacter), {
2038
+ var CimdClientMetadataSchema = z8.object({
2039
+ client_id: z8.url(),
2040
+ redirect_uris: z8.array(z8.url()).min(1),
2041
+ client_name: z8.string().trim().min(1).max(200).refine((value) => Array.from(value).every(isPlainDisplayCharacter), {
1879
2042
  message: "client_name must be plain display text"
1880
2043
  }),
1881
- token_endpoint_auth_method: z9.literal("none"),
1882
- application_type: z9.enum(["native", "web"]).optional()
2044
+ token_endpoint_auth_method: z8.literal("none"),
2045
+ application_type: z8.enum(["native", "web"]).optional()
1883
2046
  });
1884
2047
  function createSecureClientMetadataFetcher(policy = {}) {
1885
2048
  assertPositiveInteger("cimd.maxBytes", policy.maxBytes ?? DEFAULT_CIMD_MAX_BYTES);
@@ -2512,67 +2675,6 @@ function mountOAuthProvider(config) {
2512
2675
  };
2513
2676
  return dcrEnabled ? [metadataRoute, registerRoute, authorizeRoute, tokenRoute] : [metadataRoute, authorizeRoute, tokenRoute];
2514
2677
  }
2515
- // src/tools/remote.ts
2516
- var HTTP_ONLY = Object.freeze(["HTTP"]);
2517
- function toArgs(ctx) {
2518
- const { params, input } = ctx;
2519
- return {
2520
- ...isRecord(params) ? params : {},
2521
- ...isRecord(input) ? input : {}
2522
- };
2523
- }
2524
- function implementRemote(contract, http, options) {
2525
- const client = createClient(contract, http);
2526
- const groupScope = contract.meta.scope ?? "public";
2527
- const methods = {};
2528
- for (const [key, endpoint] of Object.entries(contract.endpoints)) {
2529
- methods[key] = {
2530
- method: endpoint.method,
2531
- path: endpoint.path,
2532
- desc: endpoint.desc,
2533
- serviceName: contract.meta.prefix,
2534
- key,
2535
- toolName: "toolName" in endpoint ? endpoint.toolName : undefined,
2536
- expose: endpoint.rawResponse || endpoint.rawBody || endpoint.responseMeta ? HTTP_ONLY : endpoint.expose,
2537
- ui: "ui" in endpoint ? endpoint.ui : undefined,
2538
- annotations: "annotations" in endpoint ? endpoint.annotations : undefined,
2539
- meta: mergeMeta(contract.meta.meta, endpoint.meta),
2540
- scope: endpoint.scope ?? groupScope,
2541
- paramsSchema: endpoint.params,
2542
- inputSchema: endpoint.input,
2543
- outputSchema: endpoint.output,
2544
- multipart: endpoint.multipart,
2545
- maxJsonBodyBytes: endpoint.maxJsonBodyBytes,
2546
- idempotent: endpoint.idempotent,
2547
- rawResponse: endpoint.rawResponse,
2548
- rawBody: endpoint.rawBody,
2549
- responseMeta: endpoint.responseMeta,
2550
- contentType: "contentType" in endpoint ? endpoint.contentType : undefined,
2551
- handler: async (ctx) => {
2552
- const call = client[key];
2553
- if (!call) {
2554
- throw new Error(`implementRemote: endpoint "${key}" is not exposed over HTTP`);
2555
- }
2556
- const args = toArgs(ctx);
2557
- try {
2558
- const finalArgs = options?.transformArgs ? await options.transformArgs(key, args) : args;
2559
- return await call(finalArgs);
2560
- } catch (err) {
2561
- if (ApiError.is(err)) {
2562
- throw new AppError(err.code, err.message, err.status, isRecord(err.details) ? err.details : undefined, err.hint, err.traceId);
2563
- }
2564
- throw err;
2565
- }
2566
- }
2567
- };
2568
- }
2569
- return {
2570
- name: contract.meta.prefix,
2571
- prefix: contract.meta.prefix,
2572
- scope: groupScope,
2573
- methods
2574
- };
2575
- }
2576
2678
  // src/tools/tool-logger.ts
2577
2679
  function createToolLogger(config = {}) {
2578
2680
  const log = config.log ?? ((line) => console.error(line));
@@ -2673,56 +2775,60 @@ function summarizeTransports(surface) {
2673
2775
  };
2674
2776
  }
2675
2777
  export {
2676
- wwwAuthenticateHeader,
2677
- validateMcpSchemas,
2678
- summarizeTransports,
2679
- resolveMedia,
2680
- protectedResourceMetadataUrl,
2681
- oauthProtectedResourceRoute,
2682
- mountWait,
2683
- mountViewFile,
2684
- mountUpload,
2685
- mountOAuthProvider,
2686
- mountMcpResource,
2687
- mountMcp,
2688
- mountDownload,
2689
- mountAgent,
2690
- listToolNames,
2691
- listContractToolNames,
2692
- inlineMcpAppBundle,
2693
- implementRemote,
2694
- flattenToolJsonSchema,
2695
- findUntypedProperties,
2696
- findNonPortableFormats,
2697
- defineWaitTool,
2698
- defineViewFileTool,
2699
- defineUploadTool,
2700
- defineRuntimeTool,
2701
- defineDownloadTool,
2702
- defineCliCommand,
2703
- createToolkit,
2704
- createToolLogger,
2705
- createToolInvoker,
2706
- createStdioMcpServer,
2707
- createSecureClientMetadataFetcher,
2708
- createRuntimeToolFactory,
2709
- createMcpHttpRoute,
2710
- createMcpHandler,
2711
- createCli,
2712
- collectTools,
2713
- coerceJsonArgs,
2714
- buildToolManifest,
2715
- buildMcpServer,
2716
- bindStdioProcessSignals,
2717
- ViewFileOutputSchema,
2718
- ViewFileInputSchema,
2719
- ViewFileErrorSchema,
2720
- UploadToolInputSchema,
2721
- RESOURCE_MIME_TYPE,
2722
- PROTECTED_RESOURCE_PATH,
2723
- PORTABLE_JSON_SCHEMA_FORMATS,
2724
- McpMediaContentSchema,
2725
- McpAnnotationsSchema,
2778
+ AsyncOperationCancelResultSchema,
2726
2779
  EXT_APPS_BUNDLE_PLACEHOLDER,
2727
- DownloadResultSchema
2780
+ McpAnnotationsSchema,
2781
+ McpMediaContentSchema,
2782
+ PORTABLE_JSON_SCHEMA_FORMATS,
2783
+ PROTECTED_RESOURCE_PATH,
2784
+ RESOURCE_MIME_TYPE,
2785
+ UploadToolInputSchema,
2786
+ ViewFileErrorSchema,
2787
+ ViewFileInputSchema,
2788
+ ViewFileOutputSchema,
2789
+ bindContractAsyncOperation,
2790
+ bindStdioProcessSignals,
2791
+ buildMcpServer,
2792
+ buildToolManifest,
2793
+ coerceJsonArgs,
2794
+ collectTools,
2795
+ composeToolLifecycle,
2796
+ createAsyncOperationSnapshotSchema,
2797
+ createCli,
2798
+ createMcpHandler,
2799
+ createMcpHttpRoute,
2800
+ createRuntimeToolFactory,
2801
+ createSecureClientMetadataFetcher,
2802
+ createStdioMcpServer,
2803
+ createToolInvoker,
2804
+ createToolLogger,
2805
+ createToolkit,
2806
+ defineAsyncOperation,
2807
+ defineAsyncOperationContract,
2808
+ defineCliCommand,
2809
+ defineDownloadTool,
2810
+ defineRuntimeTool,
2811
+ defineUploadTool,
2812
+ defineViewFileTool,
2813
+ defineWaitTool,
2814
+ findNonPortableFormats,
2815
+ findUntypedProperties,
2816
+ flattenToolJsonSchema,
2817
+ inlineMcpAppBundle,
2818
+ listContractToolNames,
2819
+ listToolNames,
2820
+ mountAgent,
2821
+ mountDownload,
2822
+ mountMcp,
2823
+ mountMcpResource,
2824
+ mountOAuthProvider,
2825
+ mountUpload,
2826
+ mountViewFile,
2827
+ mountWait,
2828
+ oauthProtectedResourceRoute,
2829
+ protectedResourceMetadataUrl,
2830
+ resolveMedia,
2831
+ summarizeTransports,
2832
+ validateMcpSchemas,
2833
+ wwwAuthenticateHeader
2728
2834
  };