stitchkit 0.53.2 → 0.54.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 (57) hide show
  1. package/dist/cli.d.ts +2 -1
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +3 -1
  4. package/dist/contract/define.d.ts +27 -0
  5. package/dist/contract/define.d.ts.map +1 -1
  6. package/dist/contract/index.d.ts +1 -1
  7. package/dist/contract/index.d.ts.map +1 -1
  8. package/dist/{index-c78w2y09.js → index-7vb44hy8.js} +394 -70
  9. package/dist/{index-yydqk4fh.js → index-b15rtha1.js} +30 -1
  10. package/dist/{index-pdv1mjjr.js → index-z7761jk2.js} +15 -38
  11. package/dist/node.js +2 -2
  12. package/dist/observability/event.d.ts +2 -16
  13. package/dist/observability/event.d.ts.map +1 -1
  14. package/dist/server/index.js +2 -2
  15. package/dist/server/process-signal-common.d.ts +6 -0
  16. package/dist/server/process-signal-common.d.ts.map +1 -0
  17. package/dist/server/process-signals.d.ts.map +1 -1
  18. package/dist/tools/cli-command.d.ts +29 -0
  19. package/dist/tools/cli-command.d.ts.map +1 -0
  20. package/dist/tools/cli.d.ts +11 -6
  21. package/dist/tools/cli.d.ts.map +1 -1
  22. package/dist/tools/define-download-tool.d.ts +17 -0
  23. package/dist/tools/define-download-tool.d.ts.map +1 -0
  24. package/dist/tools/define-upload-tool.d.ts +14 -0
  25. package/dist/tools/define-upload-tool.d.ts.map +1 -0
  26. package/dist/tools/define-view-file-tool.d.ts +10 -0
  27. package/dist/tools/define-view-file-tool.d.ts.map +1 -0
  28. package/dist/tools/define-wait-tool.d.ts +22 -0
  29. package/dist/tools/define-wait-tool.d.ts.map +1 -0
  30. package/dist/tools/download-core.d.ts +25 -0
  31. package/dist/tools/download-core.d.ts.map +1 -0
  32. package/dist/tools/execute.d.ts +3 -1
  33. package/dist/tools/execute.d.ts.map +1 -1
  34. package/dist/tools/mcp-round.d.ts +0 -1
  35. package/dist/tools/mcp-round.d.ts.map +1 -1
  36. package/dist/tools/mcp-stdio-signals.d.ts +33 -0
  37. package/dist/tools/mcp-stdio-signals.d.ts.map +1 -0
  38. package/dist/tools/mount-download.d.ts +5 -0
  39. package/dist/tools/mount-download.d.ts.map +1 -1
  40. package/dist/tools/mount-upload.d.ts.map +1 -1
  41. package/dist/tools/mount-wait.d.ts.map +1 -1
  42. package/dist/tools/native-definition.d.ts +15 -0
  43. package/dist/tools/native-definition.d.ts.map +1 -0
  44. package/dist/tools/runtime-tool.d.ts +5 -4
  45. package/dist/tools/runtime-tool.d.ts.map +1 -1
  46. package/dist/tools/surface.d.ts.map +1 -1
  47. package/dist/tools/upload-core.d.ts +3 -0
  48. package/dist/tools/upload-core.d.ts.map +1 -0
  49. package/dist/tools/view-file.d.ts +80 -20
  50. package/dist/tools/view-file.d.ts.map +1 -1
  51. package/dist/tools/wait-core.d.ts +10 -1
  52. package/dist/tools/wait-core.d.ts.map +1 -1
  53. package/dist/tools.d.ts +10 -2
  54. package/dist/tools.d.ts.map +1 -1
  55. package/dist/tools.js +570 -379
  56. package/llms-full.txt +312 -35
  57. package/package.json +1 -1
package/dist/tools.js CHANGED
@@ -4,29 +4,37 @@ import {
4
4
  } from "./index-yez419px.js";
5
5
  import {
6
6
  DEFAULT_CORS_ALLOW_HEADERS,
7
- contractOnlyService
8
- } from "./index-yydqk4fh.js";
7
+ DEFAULT_PROCESS_SIGNALS,
8
+ contractOnlyService,
9
+ defaultSignalSource,
10
+ guardSignalCallback,
11
+ reportSignalError
12
+ } from "./index-b15rtha1.js";
9
13
  import {
10
14
  assertToolName,
11
15
  assertUniqueToolName,
12
16
  buildToolPresentationSchema,
13
17
  coerceJsonArgs,
18
+ collectToolSurface,
14
19
  collectTools,
15
20
  createCli,
21
+ createRuntimeToolFactory,
16
22
  createToolRunner,
23
+ defineCliCommand,
24
+ defineRuntimeTool,
17
25
  executeToolMethod,
18
26
  fetchGuarded,
19
27
  fetchPinnedDocument,
20
28
  flattenToolJsonSchema,
21
29
  formatToolError,
22
30
  isObjectPresentationSchema,
23
- pollUntil,
24
31
  presentationMetadata,
25
32
  readCapped,
33
+ runWaitOperation,
26
34
  toolErrorFromResult,
27
35
  toolResultFromError,
28
36
  writeDownload
29
- } from "./index-c78w2y09.js";
37
+ } from "./index-7vb44hy8.js";
30
38
  import {
31
39
  isWithinDir,
32
40
  realPathWithinDir,
@@ -54,148 +62,6 @@ import {
54
62
 
55
63
  // src/tools/agent.ts
56
64
  import { jsonSchema, tool } from "ai";
57
-
58
- // src/tools/runtime-tool.ts
59
- function defineRuntimeTool(definition) {
60
- if (definition.transports?.length === 0) {
61
- throw new Error(`Runtime tool "${definition.name}" must expose at least one transport`);
62
- }
63
- return definition;
64
- }
65
- function createRuntimeToolFactory(config) {
66
- function parseContext(context) {
67
- const parsed = config.context.parse(context);
68
- return {
69
- ...context,
70
- ...parsed,
71
- params: undefined,
72
- input: context.input
73
- };
74
- }
75
- function define(definition) {
76
- if (definition.output !== undefined) {
77
- const { action: action2, method: method2, meta: meta2, handler: handler2, ...tool2 } = definition;
78
- const identity2 = {
79
- serviceName: config.serviceName,
80
- action: action2,
81
- method: method2,
82
- ...config.scope !== undefined && { scope: config.scope },
83
- ...(meta2 ?? config.meta) !== undefined && { meta: meta2 ?? config.meta }
84
- };
85
- return defineRuntimeTool({
86
- ...tool2,
87
- identity: identity2,
88
- handler: (context) => handler2(parseContext(context))
89
- });
90
- }
91
- const { action, method, meta, handler, ...tool } = definition;
92
- const identity = {
93
- serviceName: config.serviceName,
94
- action,
95
- method,
96
- ...config.scope !== undefined && { scope: config.scope },
97
- ...(meta ?? config.meta) !== undefined && { meta: meta ?? config.meta }
98
- };
99
- return defineRuntimeTool({
100
- ...tool,
101
- identity,
102
- handler: (context) => handler(parseContext(context))
103
- });
104
- }
105
- return { define };
106
- }
107
- function runtimeToolSupports(definition, transport) {
108
- if (definition.transports?.length === 0) {
109
- throw new Error(`Runtime tool "${definition.name}" must expose at least one transport`);
110
- }
111
- return !definition.transports || definition.transports.includes(transport);
112
- }
113
- function runtimeToolIdentity(definition) {
114
- return {
115
- method: definition.identity.method,
116
- desc: definition.description,
117
- serviceName: definition.identity.serviceName,
118
- key: definition.identity.action,
119
- toolName: definition.name,
120
- scope: definition.identity.scope,
121
- meta: definition.identity.meta,
122
- annotations: definition.annotations,
123
- ui: definition.ui,
124
- mcp: definition.mcp
125
- };
126
- }
127
- function runtimeToolMountable(definition, assertName = true) {
128
- if (assertName) {
129
- assertToolName(definition.name, definition.identity.serviceName, definition.identity.action);
130
- }
131
- const method = {
132
- ...runtimeToolIdentity(definition),
133
- inputSchema: definition.input,
134
- outputSchema: definition.output,
135
- handler: definition.handler
136
- };
137
- return {
138
- method,
139
- name: definition.name,
140
- argumentSchema: definition.input,
141
- presentationSchema: buildToolPresentationSchema({
142
- inputSchema: definition.input,
143
- unrepresentable: "any"
144
- }),
145
- shouldExtend: false
146
- };
147
- }
148
-
149
- // src/tools/surface.ts
150
- function duplicateLabel(transport) {
151
- if (transport === "MCP")
152
- return "MCP tool name";
153
- if (transport === "AGENT")
154
- return "agent tool name";
155
- return "CLI command";
156
- }
157
- function collectToolSurface({
158
- surface,
159
- transport,
160
- assertUniqueNames = true,
161
- ...collectConfig
162
- }) {
163
- const entries = [];
164
- const names = new Set;
165
- const append = (entry) => {
166
- if (assertUniqueNames) {
167
- assertUniqueToolName(entry.mountable.name, names.has(entry.mountable.name), duplicateLabel(transport));
168
- }
169
- names.add(entry.mountable.name);
170
- entries.push(entry);
171
- };
172
- for (const service of surface.services ?? []) {
173
- for (const mountable of collectTools(service, transport, collectConfig)) {
174
- append({
175
- kind: "contract",
176
- service: service.name,
177
- action: mountable.method.key,
178
- mountable
179
- });
180
- }
181
- }
182
- if (transport === "CLI")
183
- return entries;
184
- for (const definition of surface.runtimeTools ?? []) {
185
- if (!runtimeToolSupports(definition, transport))
186
- continue;
187
- append({
188
- kind: "runtime",
189
- service: definition.identity.serviceName,
190
- action: definition.identity.action,
191
- mountable: runtimeToolMountable(definition, collectConfig.assertNames),
192
- definition
193
- });
194
- }
195
- return entries;
196
- }
197
-
198
- // src/tools/agent.ts
199
65
  function mountAgent(services, config = {}) {
200
66
  const serviceList = Array.isArray(services) ? services : [services];
201
67
  const tools = {};
@@ -262,6 +128,426 @@ function mountAgent(services, config = {}) {
262
128
  }
263
129
  return tools;
264
130
  }
131
+ // src/tools/download-core.ts
132
+ import { basename, extname, resolve } from "node:path";
133
+ import { z } from "zod";
134
+ var DEFAULT_MAX_BYTES = 100 * 1024 * 1024;
135
+ var MIME_EXT = {
136
+ "image/png": ".png",
137
+ "image/jpeg": ".jpg",
138
+ "image/webp": ".webp",
139
+ "image/gif": ".gif",
140
+ "video/mp4": ".mp4",
141
+ "video/webm": ".webm",
142
+ "audio/mpeg": ".mp3",
143
+ "audio/wav": ".wav",
144
+ "audio/ogg": ".ogg"
145
+ };
146
+ var DownloadResultSchema = z.object({
147
+ path: z.string(),
148
+ size: z.number().int().nonnegative(),
149
+ mimeType: z.string()
150
+ });
151
+
152
+ class DownloadOperationError extends Error {
153
+ code;
154
+ status;
155
+ constructor(code, message, status) {
156
+ super(message);
157
+ this.code = code;
158
+ this.status = status;
159
+ this.name = "DownloadOperationError";
160
+ }
161
+ }
162
+ function extensionFor(url, mime) {
163
+ if (MIME_EXT[mime])
164
+ return MIME_EXT[mime];
165
+ try {
166
+ const ext = extname(new URL(url).pathname).toLowerCase();
167
+ if (ext)
168
+ return ext;
169
+ } catch {}
170
+ return ".bin";
171
+ }
172
+ function baseNameFor(url) {
173
+ try {
174
+ const path = new URL(url).pathname;
175
+ return (basename(path, extname(path)) || "download").slice(0, 60);
176
+ } catch {
177
+ return "download";
178
+ }
179
+ }
180
+ async function runDownloadOperation(config) {
181
+ const res = await fetchGuarded(new URL(config.url), config.allowPrivateHosts ?? false, {
182
+ timeoutMs: config.timeoutMs,
183
+ signal: config.signal
184
+ });
185
+ if (!res.ok) {
186
+ throw new DownloadOperationError("DOWNLOAD_HTTP_ERROR", `HTTP ${res.status}`, 502);
187
+ }
188
+ const mimeType = (res.headers.get("content-type") ?? "").split(";")[0]?.trim() || "application/octet-stream";
189
+ const max = config.maxBytes ?? DEFAULT_MAX_BYTES;
190
+ const declared = Number(res.headers.get("content-length") ?? 0);
191
+ if (declared > max) {
192
+ await res.body?.cancel();
193
+ throw new DownloadOperationError("DOWNLOAD_TOO_LARGE", `file exceeds the ${max}-byte cap`, 413);
194
+ }
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 };
203
+ }
204
+
205
+ // src/tools/native-definition.ts
206
+ function managedNativeIdentity(identity, method) {
207
+ return { ...identity, method };
208
+ }
209
+
210
+ // src/tools/define-download-tool.ts
211
+ function defineDownloadTool(config) {
212
+ return defineRuntimeTool({
213
+ name: config.name ?? "download",
214
+ description: config.description,
215
+ identity: managedNativeIdentity(config.identity, "POST"),
216
+ input: config.input,
217
+ output: DownloadResultSchema,
218
+ transports: config.transports,
219
+ annotations: config.annotations,
220
+ present: config.present,
221
+ handler: async (context) => {
222
+ const url = await config.resolveUrl(context.input, context);
223
+ if (!url) {
224
+ throw new AppError("DOWNLOAD_NOT_FOUND", "Nothing to download", 404);
225
+ }
226
+ try {
227
+ return await runDownloadOperation({
228
+ url,
229
+ dir: config.dirFromInput?.(context.input) ?? config.defaultDir,
230
+ allowPrivateHosts: config.allowPrivateHosts,
231
+ maxBytes: config.maxBytes,
232
+ timeoutMs: config.timeoutMs,
233
+ signal: context.signal
234
+ });
235
+ } catch (error) {
236
+ if (error instanceof DownloadOperationError) {
237
+ throw new AppError(error.code, `Download failed: ${error.message}`, error.status);
238
+ }
239
+ throw error;
240
+ }
241
+ }
242
+ });
243
+ }
244
+ // src/tools/define-upload-tool.ts
245
+ import { z as z2 } from "zod";
246
+
247
+ // src/tools/upload-core.ts
248
+ function runUploadOperation(path, upload, signal) {
249
+ signal?.throwIfAborted();
250
+ return upload(path, signal);
251
+ }
252
+
253
+ // src/tools/define-upload-tool.ts
254
+ var UploadToolInputSchema = z2.object({
255
+ path: z2.string().min(1).describe("Path to a local file on this machine")
256
+ });
257
+ function defineUploadTool(config) {
258
+ return defineRuntimeTool({
259
+ name: config.name ?? "upload",
260
+ description: config.description,
261
+ identity: managedNativeIdentity(config.identity, "POST"),
262
+ input: UploadToolInputSchema,
263
+ output: config.output,
264
+ transports: config.transports,
265
+ annotations: config.annotations,
266
+ present: config.present,
267
+ handler: async (context) => runUploadOperation(context.input.path, (path) => config.upload(path, context), context.signal)
268
+ });
269
+ }
270
+ // src/tools/view-file.ts
271
+ import { readFile, stat } from "node:fs/promises";
272
+ import { extname as extname2, resolve as resolve2 } from "node:path";
273
+ import { z as z3 } from "zod";
274
+ var MAX_INLINE_BYTES = 20 * 1024 * 1024;
275
+ var MAX_VIEW_FILES = 20;
276
+ var EXT_MIME = {
277
+ ".png": "image/png",
278
+ ".jpg": "image/jpeg",
279
+ ".jpeg": "image/jpeg",
280
+ ".gif": "image/gif",
281
+ ".webp": "image/webp",
282
+ ".mp3": "audio/mpeg",
283
+ ".wav": "audio/wav",
284
+ ".ogg": "audio/ogg",
285
+ ".m4a": "audio/mp4",
286
+ ".mp4": "video/mp4",
287
+ ".webm": "video/webm",
288
+ ".mov": "video/quicktime"
289
+ };
290
+ var McpAnnotationsSchema = z3.object({
291
+ audience: z3.array(z3.enum(["user", "assistant"])).optional(),
292
+ priority: z3.number().optional()
293
+ });
294
+ var McpMediaContentSchema = z3.discriminatedUnion("type", [
295
+ z3.object({ type: z3.literal("text"), text: z3.string() }),
296
+ z3.object({
297
+ type: z3.literal("image"),
298
+ data: z3.string(),
299
+ mimeType: z3.string(),
300
+ annotations: McpAnnotationsSchema.optional()
301
+ }),
302
+ z3.object({
303
+ type: z3.literal("audio"),
304
+ data: z3.string(),
305
+ mimeType: z3.string(),
306
+ annotations: McpAnnotationsSchema.optional()
307
+ })
308
+ ]);
309
+ var ViewFileErrorSchema = z3.object({
310
+ path: z3.string(),
311
+ message: z3.string()
312
+ });
313
+ var ViewFileOutputSchema = z3.object({
314
+ content: z3.array(McpMediaContentSchema),
315
+ errors: z3.array(ViewFileErrorSchema)
316
+ });
317
+ async function fetchSource(pathOrUrl, options, maxBytes) {
318
+ const extMime = EXT_MIME[extname2(pathOrUrl).toLowerCase()];
319
+ if (pathOrUrl.startsWith("http://") || pathOrUrl.startsWith("https://")) {
320
+ const url = new URL(pathOrUrl);
321
+ const res = await fetchGuarded(url, options.allowPrivateHosts ?? false, {
322
+ timeoutMs: options.timeoutMs,
323
+ signal: options.signal
324
+ });
325
+ if (!res.ok)
326
+ throw new Error(`HTTP ${res.status}`);
327
+ const headerMime = (res.headers.get("content-type") ?? "").split(";")[0]?.trim() ?? "";
328
+ const mimeType = headerMime || extMime || "application/octet-stream";
329
+ if (mimeType.startsWith("video/")) {
330
+ await res.body?.cancel();
331
+ return { video: true, mimeType, bytesRead: 0 };
332
+ }
333
+ const declared = Number(res.headers.get("content-length") ?? 0);
334
+ if (declared > maxBytes) {
335
+ await res.body?.cancel();
336
+ return { tooLarge: true, mimeType, bytesRead: 0 };
337
+ }
338
+ const buffer2 = await readCapped(res, maxBytes);
339
+ return buffer2 ? { buffer: buffer2, mimeType, bytesRead: buffer2.length } : { tooLarge: true, mimeType, bytesRead: maxBytes };
340
+ }
341
+ if (!options.baseDir) {
342
+ throw new Error("local file paths are disabled — set baseDir to allow them");
343
+ }
344
+ options.signal?.throwIfAborted();
345
+ 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");
352
+ }
353
+ const realTarget = await realPathWithinDir(root, target);
354
+ if (realTarget === null) {
355
+ throw new Error("path escapes the allowed directory");
356
+ }
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
+ }
365
+ async function resolveMediaWithinBudget(pathOrUrl, options, maxBytes) {
366
+ if (maxBytes <= 0) {
367
+ return {
368
+ content: [
369
+ { type: "text", text: `[media] total inline budget exhausted — ${pathOrUrl}` }
370
+ ],
371
+ bytes: 0
372
+ };
373
+ }
374
+ const extMime = EXT_MIME[extname2(pathOrUrl).toLowerCase()];
375
+ if (extMime?.startsWith("video/")) {
376
+ return {
377
+ content: [{ type: "text", text: `[video] ${extMime} — ${pathOrUrl}` }],
378
+ bytes: 0
379
+ };
380
+ }
381
+ const result = await fetchSource(pathOrUrl, options, maxBytes);
382
+ if ("video" in result) {
383
+ return {
384
+ content: [{ type: "text", text: `[video] ${result.mimeType} — ${pathOrUrl}` }],
385
+ bytes: result.bytesRead
386
+ };
387
+ }
388
+ if ("tooLarge" in result) {
389
+ return {
390
+ content: [
391
+ { type: "text", text: `[${result.mimeType}] too large to inline — ${pathOrUrl}` }
392
+ ],
393
+ bytes: result.bytesRead
394
+ };
395
+ }
396
+ const { buffer, mimeType } = result;
397
+ const sizeKb = (buffer.length / 1024).toFixed(0);
398
+ if (mimeType.startsWith("image/")) {
399
+ return {
400
+ content: [
401
+ {
402
+ type: "image",
403
+ data: buffer.toString("base64"),
404
+ mimeType,
405
+ annotations: { audience: ["user", "assistant"], priority: 0.9 }
406
+ },
407
+ { type: "text", text: `[image] ${mimeType}, ${sizeKb}KB` }
408
+ ],
409
+ bytes: buffer.length
410
+ };
411
+ }
412
+ if (mimeType.startsWith("audio/")) {
413
+ return {
414
+ content: [
415
+ {
416
+ type: "audio",
417
+ data: buffer.toString("base64"),
418
+ mimeType,
419
+ annotations: { audience: ["user", "assistant"], priority: 0.9 }
420
+ },
421
+ { type: "text", text: `[audio] ${mimeType}, ${sizeKb}KB` }
422
+ ],
423
+ bytes: buffer.length
424
+ };
425
+ }
426
+ return {
427
+ content: [{ type: "text", text: `[${mimeType}] ${sizeKb}KB — ${pathOrUrl}` }],
428
+ bytes: buffer.length
429
+ };
430
+ }
431
+ async function resolveMedia(pathOrUrl, options = {}) {
432
+ return (await resolveMediaWithinBudget(pathOrUrl, options, MAX_INLINE_BYTES)).content;
433
+ }
434
+ var ViewFileInputSchema = z3.object({
435
+ paths: z3.union([z3.string(), z3.array(z3.string()).max(MAX_VIEW_FILES)]).describe("Media URL(s) or file path(s) to view")
436
+ });
437
+ async function runViewFileOperation(paths, options = {}, signal) {
438
+ const list = Array.isArray(paths) ? paths : [paths];
439
+ const content = [];
440
+ const errors = [];
441
+ let remainingBytes = MAX_INLINE_BYTES;
442
+ for (const pathOrUrl of list) {
443
+ signal?.throwIfAborted();
444
+ try {
445
+ const resolved = await resolveMediaWithinBudget(pathOrUrl, { ...options, signal }, remainingBytes);
446
+ remainingBytes -= resolved.bytes;
447
+ content.push(...resolved.content);
448
+ } catch (error) {
449
+ if (signal?.aborted)
450
+ throw signal.reason ?? error;
451
+ const message = error instanceof Error ? error.message : String(error);
452
+ errors.push({ path: pathOrUrl, message });
453
+ content.push({ type: "text", text: `[${pathOrUrl}] Error: ${message}` });
454
+ }
455
+ }
456
+ return ViewFileOutputSchema.parse({ content, errors });
457
+ }
458
+ function mountViewFile(server, options = {}) {
459
+ server.registerTool("view_file", {
460
+ description: "View media (image, audio, video) by URL or local path — returns it as content you can SEE / HEAR. Pass several paths to view multiple files at once. Use it on a generation `output` url to inspect the result.",
461
+ inputSchema: ViewFileInputSchema.shape,
462
+ annotations: { title: "View Media", readOnlyHint: true, idempotentHint: true }
463
+ }, async (args) => {
464
+ const result = await runViewFileOperation(args.paths, options);
465
+ return { content: result.content };
466
+ });
467
+ }
468
+
469
+ // src/tools/define-view-file-tool.ts
470
+ function defineViewFileTool(config) {
471
+ const defaults = {
472
+ mcp: (output) => ({ content: output.content }),
473
+ agent: (output) => ({
474
+ type: "content",
475
+ value: output.content.map((part) => {
476
+ if (part.type === "text")
477
+ return part;
478
+ return {
479
+ type: "file",
480
+ data: { type: "data", data: part.data },
481
+ mediaType: part.mimeType
482
+ };
483
+ })
484
+ })
485
+ };
486
+ return defineRuntimeTool({
487
+ name: config.name ?? "view_file",
488
+ description: config.description,
489
+ identity: managedNativeIdentity(config.identity, "GET"),
490
+ input: ViewFileInputSchema,
491
+ output: ViewFileOutputSchema,
492
+ transports: config.transports,
493
+ annotations: config.annotations ?? {
494
+ title: "View Media",
495
+ readOnlyHint: true,
496
+ idempotentHint: true
497
+ },
498
+ present: { ...defaults, ...config.present },
499
+ handler: (context) => runViewFileOperation(context.input.paths, {
500
+ baseDir: config.baseDir,
501
+ allowPrivateHosts: config.allowPrivateHosts,
502
+ timeoutMs: config.timeoutMs
503
+ }, context.signal)
504
+ });
505
+ }
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
+ }
265
551
  // src/tools/invoker.ts
266
552
  function createToolInvoker(services, config) {
267
553
  const serviceList = Array.isArray(services) ? services : [services];
@@ -358,7 +644,7 @@ import {
358
644
  createRequestStateCodec,
359
645
  McpServer
360
646
  } from "@modelcontextprotocol/server";
361
- import { z as z2 } from "zod";
647
+ import { z as z5 } from "zod";
362
648
 
363
649
  // src/tools/mcp-app.ts
364
650
  import { readFileSync } from "node:fs";
@@ -975,13 +1261,13 @@ function runInMcpRequestContext(context, toolName, body) {
975
1261
  import {
976
1262
  isCallToolResult
977
1263
  } from "@modelcontextprotocol/server";
978
- import { z } from "zod";
1264
+ import { z as z4 } from "zod";
979
1265
  function mountPreparedRuntimeMcp(server, tools, config) {
980
1266
  for (const { definition, descriptor } of tools) {
981
1267
  const { mountable } = descriptor;
982
1268
  const toolConfig = {
983
1269
  description: definition.description,
984
- inputSchema: z.looseObject({}).meta(presentationMetadata(descriptor.inputSchema))
1270
+ inputSchema: z4.looseObject({}).meta(presentationMetadata(descriptor.inputSchema))
985
1271
  };
986
1272
  if (descriptor.outputSchema)
987
1273
  toolConfig.outputSchema = descriptor.outputSchema;
@@ -1070,7 +1356,7 @@ function mountPreparedMcp(mcpServer, prepared, config = {}) {
1070
1356
  const { mountable } = descriptor;
1071
1357
  const toolConfig = {
1072
1358
  description: mountable.method.desc,
1073
- inputSchema: z2.looseObject({}).meta(presentationMetadata(descriptor.inputSchema))
1359
+ inputSchema: z5.looseObject({}).meta(presentationMetadata(descriptor.inputSchema))
1074
1360
  };
1075
1361
  if (descriptor.outputSchema)
1076
1362
  toolConfig.outputSchema = descriptor.outputSchema;
@@ -1324,8 +1610,8 @@ function createMcpHandler(config) {
1324
1610
  const authByCarrier = new WeakMap;
1325
1611
  let closed = false;
1326
1612
  let resolveClosed;
1327
- const closedSignal = new Promise((resolve) => {
1328
- resolveClosed = resolve;
1613
+ const closedSignal = new Promise((resolve3) => {
1614
+ resolveClosed = resolve3;
1329
1615
  });
1330
1616
  const sdkHandler = createSdkMcpHandler(({ authInfo }) => {
1331
1617
  const resolved = authInfo ? authByCarrier.get(authInfo) : undefined;
@@ -1409,9 +1695,103 @@ async function createStdioMcpServer(config) {
1409
1695
  maxSubscriptions: 0
1410
1696
  });
1411
1697
  }
1698
+ // src/tools/mcp-stdio-signals.ts
1699
+ var bound = new WeakSet;
1700
+ function bindStdioProcessSignals(handle, options = {}) {
1701
+ if (bound.has(handle)) {
1702
+ throw new Error("[stitchkit] bindStdioProcessSignals: this stdio handle is already bound; close the first binding before creating another");
1703
+ }
1704
+ bound.add(handle);
1705
+ const signals = [...new Set(options.signals ?? DEFAULT_PROCESS_SIGNALS)];
1706
+ const source = options.signalSource ?? defaultSignalSource;
1707
+ const handlers = [];
1708
+ let started = false;
1709
+ let sameTurnAsStart = false;
1710
+ let closed = false;
1711
+ let resolveChain = () => {
1712
+ return;
1713
+ };
1714
+ let rejectChain = () => {
1715
+ return;
1716
+ };
1717
+ const promise = new Promise((resolve3, reject) => {
1718
+ resolveChain = resolve3;
1719
+ rejectChain = reject;
1720
+ });
1721
+ promise.catch(() => {
1722
+ return;
1723
+ });
1724
+ const removeListeners = () => {
1725
+ if (closed)
1726
+ return;
1727
+ closed = true;
1728
+ for (const [signal, handler] of handlers)
1729
+ source.off(signal, handler);
1730
+ if (!started)
1731
+ bound.delete(handle);
1732
+ };
1733
+ const close = () => {
1734
+ const hadStarted = started;
1735
+ removeListeners();
1736
+ if (!hadStarted)
1737
+ resolveChain();
1738
+ };
1739
+ const run = async (signal) => {
1740
+ try {
1741
+ await options.onClose?.(signal);
1742
+ } catch (error) {
1743
+ reportSignalError("prepare", error, options.onError);
1744
+ }
1745
+ try {
1746
+ await handle.close();
1747
+ } catch (error) {
1748
+ rejectChain(error);
1749
+ reportSignalError("close", error, options.onError);
1750
+ removeListeners();
1751
+ return;
1752
+ }
1753
+ resolveChain();
1754
+ try {
1755
+ await options.onComplete?.();
1756
+ } catch (error) {
1757
+ reportSignalError("complete", error, options.onError);
1758
+ } finally {
1759
+ removeListeners();
1760
+ }
1761
+ };
1762
+ const onSignal = (signal) => {
1763
+ if (!started) {
1764
+ started = true;
1765
+ sameTurnAsStart = true;
1766
+ queueMicrotask(() => {
1767
+ sameTurnAsStart = false;
1768
+ });
1769
+ run(signal).catch((error) => reportSignalError("close", error, options.onError));
1770
+ return;
1771
+ }
1772
+ if (sameTurnAsStart)
1773
+ return;
1774
+ removeListeners();
1775
+ guardSignalCallback(() => options.onRepeatedSignal?.(signal), "close", options.onError);
1776
+ let restored = false;
1777
+ try {
1778
+ restored = source.raiseDefault(signal);
1779
+ } catch (error) {
1780
+ reportSignalError("close", error, options.onError);
1781
+ }
1782
+ if (!restored) {
1783
+ guardSignalCallback(() => options.onEscalationBlocked?.(signal), "close", options.onError);
1784
+ }
1785
+ };
1786
+ for (const signal of signals) {
1787
+ const handler = () => onSignal(signal);
1788
+ handlers.push([signal, handler]);
1789
+ source.on(signal, handler);
1790
+ }
1791
+ return { promise, close };
1792
+ }
1412
1793
  // src/tools/mount-download.ts
1413
- import { basename, extname, resolve } from "node:path";
1414
- import { z as z3 } from "zod";
1794
+ import { z as z6 } from "zod";
1415
1795
 
1416
1796
  // src/tools/native-result.ts
1417
1797
  function textResult(text, isError = false) {
@@ -1420,84 +1800,43 @@ function textResult(text, isError = false) {
1420
1800
  }
1421
1801
 
1422
1802
  // src/tools/mount-download.ts
1423
- var DEFAULT_MAX_BYTES = 100 * 1024 * 1024;
1424
- var MIME_EXT = {
1425
- "image/png": ".png",
1426
- "image/jpeg": ".jpg",
1427
- "image/webp": ".webp",
1428
- "image/gif": ".gif",
1429
- "video/mp4": ".mp4",
1430
- "video/webm": ".webm",
1431
- "audio/mpeg": ".mp3",
1432
- "audio/wav": ".wav",
1433
- "audio/ogg": ".ogg"
1434
- };
1435
- function extensionFor(url, mime) {
1436
- if (MIME_EXT[mime])
1437
- return MIME_EXT[mime];
1438
- try {
1439
- const ext = extname(new URL(url).pathname).toLowerCase();
1440
- if (ext)
1441
- return ext;
1442
- } catch {}
1443
- return ".bin";
1444
- }
1445
- function baseNameFor(url) {
1446
- try {
1447
- const path = new URL(url).pathname;
1448
- return (basename(path, extname(path)) || "download").slice(0, 60);
1449
- } catch {
1450
- return "download";
1451
- }
1452
- }
1453
1803
  function mountDownload(server, config) {
1454
1804
  const name = config.name ?? "download";
1455
1805
  assertToolName(name, "<native>", "download");
1456
- server.registerTool(name, { description: config.description, inputSchema: z3.object(config.inputSchema) }, async (rawArgs) => {
1806
+ server.registerTool(name, { description: config.description, inputSchema: z6.object(config.inputSchema) }, async (rawArgs) => {
1457
1807
  const args = isRecord(rawArgs) ? rawArgs : {};
1458
1808
  try {
1459
1809
  const url = await config.resolveUrl(args);
1460
1810
  if (!url)
1461
1811
  return textResult("Nothing to download.", true);
1462
- const res = await fetchGuarded(new URL(url), config.allowPrivateHosts ?? false, {
1812
+ const result = await runDownloadOperation({
1813
+ url,
1814
+ dir: config.dirFromArgs?.(args) ?? config.defaultDir,
1815
+ allowPrivateHosts: config.allowPrivateHosts,
1816
+ maxBytes: config.maxBytes,
1463
1817
  timeoutMs: config.timeoutMs
1464
1818
  });
1465
- if (!res.ok)
1466
- return textResult(`Download failed: HTTP ${res.status}`, true);
1467
- const mimeType = (res.headers.get("content-type") ?? "").split(";")[0]?.trim() || "application/octet-stream";
1468
- const max = config.maxBytes ?? DEFAULT_MAX_BYTES;
1469
- const declared = Number(res.headers.get("content-length") ?? 0);
1470
- if (declared > max) {
1471
- await res.body?.cancel();
1472
- return textResult(`Download failed: file exceeds the ${max}-byte cap`, true);
1473
- }
1474
- const buffer = await readCapped(res, max);
1475
- if (!buffer)
1476
- return textResult(`Download failed: file exceeds the ${max}-byte cap`, true);
1477
- const dir = resolve(config.dirFromArgs?.(args) ?? config.defaultDir);
1478
- const filePath = resolve(dir, `${baseNameFor(url)}${extensionFor(url, mimeType)}`);
1479
- await writeDownload(dir, filePath, buffer);
1480
- return textResult(JSON.stringify({ path: filePath, size: buffer.length, mimeType }, null, 2));
1819
+ return textResult(JSON.stringify(result, null, 2));
1481
1820
  } catch (err) {
1482
1821
  return textResult(`Download failed: ${err instanceof Error ? err.message : String(err)}`, true);
1483
1822
  }
1484
1823
  });
1485
1824
  }
1486
1825
  // src/tools/mount-upload.ts
1487
- import { z as z4 } from "zod";
1826
+ import { z as z7 } from "zod";
1488
1827
  function mountUpload(server, config) {
1489
1828
  const name = config.name ?? "upload";
1490
1829
  assertToolName(name, "<native>", "upload");
1491
1830
  server.registerTool(name, {
1492
1831
  description: config.description,
1493
- inputSchema: { path: z4.string().describe("Path to a local file on this machine") }
1832
+ inputSchema: { path: z7.string().describe("Path to a local file on this machine") }
1494
1833
  }, async (rawArgs) => {
1495
1834
  const args = isRecord(rawArgs) ? rawArgs : {};
1496
1835
  const path = typeof args.path === "string" ? args.path : "";
1497
1836
  if (!path)
1498
1837
  return textResult("Provide `path`.", true);
1499
1838
  try {
1500
- const uploaded = await config.upload(path);
1839
+ const uploaded = await runUploadOperation(path, config.upload);
1501
1840
  return textResult(JSON.stringify(uploaded ?? null, null, 2));
1502
1841
  } catch (err) {
1503
1842
  return textResult(`Upload failed: ${err instanceof Error ? err.message : String(err)}`, true);
@@ -1505,15 +1844,16 @@ function mountUpload(server, config) {
1505
1844
  });
1506
1845
  }
1507
1846
  // src/tools/mount-wait.ts
1508
- import { z as z5 } from "zod";
1847
+ import { z as z8 } from "zod";
1509
1848
  function mountWait(server, config) {
1510
1849
  const name = config.name ?? "wait";
1511
1850
  assertToolName(name, "<native>", "wait");
1512
- server.registerTool(name, { description: config.description, inputSchema: z5.object(config.inputSchema) }, async (rawArgs) => {
1851
+ server.registerTool(name, { description: config.description, inputSchema: z8.object(config.inputSchema) }, async (rawArgs) => {
1513
1852
  const args = isRecord(rawArgs) ? rawArgs : {};
1514
1853
  try {
1515
- const { state, timedOut } = await pollUntil({
1516
- poll: () => config.poll(args),
1854
+ const { state, timedOut } = await runWaitOperation({
1855
+ input: args,
1856
+ poll: (input) => config.poll(input),
1517
1857
  done: config.done,
1518
1858
  backoff: config.backoff,
1519
1859
  timeoutSec: config.timeoutFromArgs?.(args) ?? config.defaultTimeout
@@ -1527,19 +1867,19 @@ function mountWait(server, config) {
1527
1867
  }
1528
1868
  // src/tools/oauth-provider.ts
1529
1869
  import { randomUUID } from "node:crypto";
1530
- import { z as z6 } from "zod";
1870
+ import { z as z9 } from "zod";
1531
1871
  function isPlainDisplayCharacter(character) {
1532
1872
  const codePoint = character.codePointAt(0);
1533
1873
  return codePoint !== undefined && codePoint >= 32 && codePoint !== 127 && character !== "<" && character !== ">";
1534
1874
  }
1535
- var CimdClientMetadataSchema = z6.object({
1536
- client_id: z6.url(),
1537
- redirect_uris: z6.array(z6.url()).min(1),
1538
- client_name: z6.string().trim().min(1).max(200).refine((value) => Array.from(value).every(isPlainDisplayCharacter), {
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), {
1539
1879
  message: "client_name must be plain display text"
1540
1880
  }),
1541
- token_endpoint_auth_method: z6.literal("none"),
1542
- application_type: z6.enum(["native", "web"]).optional()
1881
+ token_endpoint_auth_method: z9.literal("none"),
1882
+ application_type: z9.enum(["native", "web"]).optional()
1543
1883
  });
1544
1884
  function createSecureClientMetadataFetcher(policy = {}) {
1545
1885
  assertPositiveInteger("cimd.maxBytes", policy.maxBytes ?? DEFAULT_CIMD_MAX_BYTES);
@@ -2332,168 +2672,6 @@ function summarizeTransports(surface) {
2332
2672
  sources
2333
2673
  };
2334
2674
  }
2335
- // src/tools/view-file.ts
2336
- import { readFile, stat } from "node:fs/promises";
2337
- import { extname as extname2, resolve as resolve2 } from "node:path";
2338
- import { z as z7 } from "zod";
2339
- var MAX_INLINE_BYTES = 20 * 1024 * 1024;
2340
- var MAX_VIEW_FILES = 20;
2341
- var EXT_MIME = {
2342
- ".png": "image/png",
2343
- ".jpg": "image/jpeg",
2344
- ".jpeg": "image/jpeg",
2345
- ".gif": "image/gif",
2346
- ".webp": "image/webp",
2347
- ".mp3": "audio/mpeg",
2348
- ".wav": "audio/wav",
2349
- ".ogg": "audio/ogg",
2350
- ".m4a": "audio/mp4",
2351
- ".mp4": "video/mp4",
2352
- ".webm": "video/webm",
2353
- ".mov": "video/quicktime"
2354
- };
2355
- async function fetchSource(pathOrUrl, options, maxBytes) {
2356
- const extMime = EXT_MIME[extname2(pathOrUrl).toLowerCase()];
2357
- if (pathOrUrl.startsWith("http://") || pathOrUrl.startsWith("https://")) {
2358
- const url = new URL(pathOrUrl);
2359
- const res = await fetchGuarded(url, options.allowPrivateHosts ?? false, {
2360
- timeoutMs: options.timeoutMs
2361
- });
2362
- if (!res.ok)
2363
- throw new Error(`HTTP ${res.status}`);
2364
- const headerMime = (res.headers.get("content-type") ?? "").split(";")[0]?.trim() ?? "";
2365
- const mimeType = headerMime || extMime || "application/octet-stream";
2366
- if (mimeType.startsWith("video/")) {
2367
- await res.body?.cancel();
2368
- return { video: true, mimeType, bytesRead: 0 };
2369
- }
2370
- const declared = Number(res.headers.get("content-length") ?? 0);
2371
- if (declared > maxBytes) {
2372
- await res.body?.cancel();
2373
- return { tooLarge: true, mimeType, bytesRead: 0 };
2374
- }
2375
- const buffer2 = await readCapped(res, maxBytes);
2376
- return buffer2 ? { buffer: buffer2, mimeType, bytesRead: buffer2.length } : { tooLarge: true, mimeType, bytesRead: maxBytes };
2377
- }
2378
- if (!options.baseDir) {
2379
- throw new Error("local file paths are disabled — set baseDir to allow them");
2380
- }
2381
- if (!extMime) {
2382
- throw new Error("refusing to read a non-media file");
2383
- }
2384
- const root = resolve2(options.baseDir);
2385
- const target = resolve2(root, pathOrUrl);
2386
- if (!isWithinDir(root, target)) {
2387
- throw new Error("path escapes the allowed directory");
2388
- }
2389
- const realTarget = await realPathWithinDir(root, target);
2390
- if (realTarget === null) {
2391
- throw new Error("path escapes the allowed directory");
2392
- }
2393
- const info = await stat(realTarget).catch(() => null);
2394
- if (!info?.isFile())
2395
- throw new Error("file not found");
2396
- if (info.size > maxBytes)
2397
- return { tooLarge: true, mimeType: extMime, bytesRead: 0 };
2398
- const buffer = await readFile(realTarget);
2399
- return { buffer, mimeType: extMime, bytesRead: buffer.length };
2400
- }
2401
- async function resolveMediaWithinBudget(pathOrUrl, options, maxBytes) {
2402
- if (maxBytes <= 0) {
2403
- return {
2404
- content: [
2405
- { type: "text", text: `[media] total inline budget exhausted — ${pathOrUrl}` }
2406
- ],
2407
- bytes: 0
2408
- };
2409
- }
2410
- const extMime = EXT_MIME[extname2(pathOrUrl).toLowerCase()];
2411
- if (extMime?.startsWith("video/")) {
2412
- return {
2413
- content: [{ type: "text", text: `[video] ${extMime} — ${pathOrUrl}` }],
2414
- bytes: 0
2415
- };
2416
- }
2417
- const result = await fetchSource(pathOrUrl, options, maxBytes);
2418
- if ("video" in result) {
2419
- return {
2420
- content: [{ type: "text", text: `[video] ${result.mimeType} — ${pathOrUrl}` }],
2421
- bytes: result.bytesRead
2422
- };
2423
- }
2424
- if ("tooLarge" in result) {
2425
- return {
2426
- content: [
2427
- { type: "text", text: `[${result.mimeType}] too large to inline — ${pathOrUrl}` }
2428
- ],
2429
- bytes: result.bytesRead
2430
- };
2431
- }
2432
- const { buffer, mimeType } = result;
2433
- const sizeKb = (buffer.length / 1024).toFixed(0);
2434
- if (mimeType.startsWith("image/")) {
2435
- return {
2436
- content: [
2437
- {
2438
- type: "image",
2439
- data: buffer.toString("base64"),
2440
- mimeType,
2441
- annotations: { audience: ["user", "assistant"], priority: 0.9 }
2442
- },
2443
- { type: "text", text: `[image] ${mimeType}, ${sizeKb}KB` }
2444
- ],
2445
- bytes: buffer.length
2446
- };
2447
- }
2448
- if (mimeType.startsWith("audio/")) {
2449
- return {
2450
- content: [
2451
- {
2452
- type: "audio",
2453
- data: buffer.toString("base64"),
2454
- mimeType,
2455
- annotations: { audience: ["user", "assistant"], priority: 0.9 }
2456
- },
2457
- { type: "text", text: `[audio] ${mimeType}, ${sizeKb}KB` }
2458
- ],
2459
- bytes: buffer.length
2460
- };
2461
- }
2462
- return {
2463
- content: [{ type: "text", text: `[${mimeType}] ${sizeKb}KB — ${pathOrUrl}` }],
2464
- bytes: buffer.length
2465
- };
2466
- }
2467
- async function resolveMedia(pathOrUrl, options = {}) {
2468
- return (await resolveMediaWithinBudget(pathOrUrl, options, MAX_INLINE_BYTES)).content;
2469
- }
2470
- var ViewFileInputSchema = z7.object({
2471
- paths: z7.union([z7.string(), z7.array(z7.string()).max(MAX_VIEW_FILES)]).describe("Media URL(s) or file path(s) to view")
2472
- });
2473
- function mountViewFile(server, options = {}) {
2474
- server.registerTool("view_file", {
2475
- description: "View media (image, audio, video) by URL or local path — returns it as content you can SEE / HEAR. Pass several paths to view multiple files at once. Use it on a generation `output` url to inspect the result.",
2476
- inputSchema: ViewFileInputSchema.shape,
2477
- annotations: { title: "View Media", readOnlyHint: true, idempotentHint: true }
2478
- }, async (args) => {
2479
- const list = Array.isArray(args.paths) ? args.paths : [args.paths];
2480
- const content = [];
2481
- let remainingBytes = MAX_INLINE_BYTES;
2482
- for (const pathOrUrl of list) {
2483
- try {
2484
- const resolved = await resolveMediaWithinBudget(pathOrUrl, options, remainingBytes);
2485
- remainingBytes -= resolved.bytes;
2486
- content.push(...resolved.content);
2487
- } catch (err) {
2488
- content.push({
2489
- type: "text",
2490
- text: `[${pathOrUrl}] Error: ${err instanceof Error ? err.message : String(err)}`
2491
- });
2492
- }
2493
- }
2494
- return { content };
2495
- });
2496
- }
2497
2675
  export {
2498
2676
  wwwAuthenticateHeader,
2499
2677
  validateMcpSchemas,
@@ -2516,7 +2694,12 @@ export {
2516
2694
  flattenToolJsonSchema,
2517
2695
  findUntypedProperties,
2518
2696
  findNonPortableFormats,
2697
+ defineWaitTool,
2698
+ defineViewFileTool,
2699
+ defineUploadTool,
2519
2700
  defineRuntimeTool,
2701
+ defineDownloadTool,
2702
+ defineCliCommand,
2520
2703
  createToolkit,
2521
2704
  createToolLogger,
2522
2705
  createToolInvoker,
@@ -2530,8 +2713,16 @@ export {
2530
2713
  coerceJsonArgs,
2531
2714
  buildToolManifest,
2532
2715
  buildMcpServer,
2716
+ bindStdioProcessSignals,
2717
+ ViewFileOutputSchema,
2718
+ ViewFileInputSchema,
2719
+ ViewFileErrorSchema,
2720
+ UploadToolInputSchema,
2533
2721
  RESOURCE_MIME_TYPE,
2534
2722
  PROTECTED_RESOURCE_PATH,
2535
2723
  PORTABLE_JSON_SCHEMA_FORMATS,
2536
- EXT_APPS_BUNDLE_PLACEHOLDER
2724
+ McpMediaContentSchema,
2725
+ McpAnnotationsSchema,
2726
+ EXT_APPS_BUNDLE_PLACEHOLDER,
2727
+ DownloadResultSchema
2537
2728
  };