stitchkit 0.53.1 → 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-6j4j5wf9.js → index-7vb44hy8.js} +404 -72
  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/llms-full.txt CHANGED
@@ -2364,8 +2364,9 @@ must not be used as authorization input.
2364
2364
  ### Modern discovery, routing and cache hints
2365
2365
 
2366
2366
  Stitchkit forwards the negotiated protocol era and validated MCP request data
2367
- to `RequestEvent.mcp`; untrusted routing headers never become application
2368
- identity. The SDK validates `MCP-Protocol-Version`, JSON-RPC shape and routing
2367
+ to both `RequestEvent.mcp` and the current managed call's typed `context.mcp`;
2368
+ untrusted routing headers never become application identity. The SDK validates
2369
+ `MCP-Protocol-Version`, JSON-RPC shape and routing
2369
2370
  metadata before lifecycle or tool handlers run. Framework-owned tool and
2370
2371
  resource registration preserves declaration order across HTTP and stdio. Tool
2371
2372
  manifests preserve mount order; `listToolNames` is a sorted diagnostics view.
@@ -2380,6 +2381,30 @@ finite `surfaces` registry when identities share a provably immutable surface.
2380
2381
  Stitchkit never advertises list-change or subscription capabilities
2381
2382
  without an implementation.
2382
2383
 
2384
+ ### Typed MCP call metadata
2385
+
2386
+ Contract handlers, runtime-tool handlers/factories, lifecycle and tool hooks all
2387
+ see the same optional `McpCallContext` for the current invocation:
2388
+
2389
+ ```ts
2390
+ const sourceLabel = context.mcp?.clientInfo?.name ?? context.source
2391
+
2392
+ context.mcp?.era // 'modern' | 'legacy'
2393
+ context.mcp?.method // validated MCP method
2394
+ context.mcp?.toolName // resolved framework tool name
2395
+ context.mcp?.protocolVersion // when supplied by a modern host
2396
+ context.mcp?.clientInfo // { name, version } when validated
2397
+ context.mcp?.outcome // multi-round attempt outcome
2398
+ context.mcp?.round // multi-round attempt number
2399
+ ```
2400
+
2401
+ The field is absent on HTTP, Agent, CLI and bring-your-own transports. A legacy
2402
+ MCP call exposes `era: 'legacy'` but does not invent modern client information.
2403
+ `clientInfo` is the host's self-description: use it for labels or analytics,
2404
+ never for authentication, RBAC, tenant selection or rate limiting. Verified
2405
+ identity still comes from `auth`; the `context(auth)` callback remains the
2406
+ application-context factory and receives no protocol metadata.
2407
+
2383
2408
  ### Multi-round tool input (`input_required`)
2384
2409
 
2385
2410
  An MCP-only operation can require typed user input before its final side effect:
@@ -2598,7 +2623,7 @@ client (Claude Desktop, Claude Code, Cursor, Codex), on the user's machine, so
2598
2623
  it can reach the local filesystem.
2599
2624
 
2600
2625
  ```ts
2601
- import { createStdioMcpServer } from 'stitchkit/tools'
2626
+ import { bindStdioProcessSignals, createStdioMcpServer } from 'stitchkit/tools'
2602
2627
 
2603
2628
  const stdio = await createStdioMcpServer({
2604
2629
  serverInfo: { name: 'my-app', version: '1.0.0' },
@@ -2606,8 +2631,16 @@ const stdio = await createStdioMcpServer({
2606
2631
  services: [usersService],
2607
2632
  })
2608
2633
 
2609
- // During graceful shutdown:
2610
- await stdio.close()
2634
+ const signals = bindStdioProcessSignals(stdio, {
2635
+ onClose: () => stopWorkers(),
2636
+ onComplete: () => { process.exitCode = 0 },
2637
+ onError: (_phase, error) => {
2638
+ console.error(error) // stderr only — stdout is JSON-RPC
2639
+ process.exitCode = 1
2640
+ },
2641
+ })
2642
+
2643
+ await signals.promise
2611
2644
  ```
2612
2645
 
2613
2646
  A stdio server is a single process serving one client, so `auth` is a value (or
@@ -2620,6 +2653,14 @@ through the official stdio adapter. Use `legacy: 'reject'` for a modern-only
2620
2653
  binary. Each invocation builds a fresh server; the returned handle owns
2621
2654
  transport shutdown.
2622
2655
 
2656
+ Signal binding is explicit: `createStdioMcpServer` installs no global process
2657
+ listeners by itself. The first `SIGINT`/`SIGTERM` starts exactly one official
2658
+ `close()` chain. Stdio has no force primitive, so a later signal restores the
2659
+ OS default disposition instead of reporting a fictional forced result.
2660
+ `signals.close()` removes an idle binding; the framework never calls
2661
+ `process.exit()` or chooses the exit code. See
2662
+ [deployment lifecycle](./testing-and-deployment.md#stdio-process-signals).
2663
+
2623
2664
  Both transports build the server through the shared `buildMcpServer` — same
2624
2665
  contract/runtime pipeline, same surface selection, context, hooks, raw escape
2625
2666
  hatch and instructions.
@@ -2943,7 +2984,9 @@ const countRecords = knowledgeTools.define({
2943
2984
  `defineRuntimeTool` when tools do not share a context schema or identity.
2944
2985
 
2945
2986
  `transports` defaults to `['MCP', 'AGENT']`; set an explicit subset when an
2946
- operation belongs on only one surface. The configured identity becomes the
2987
+ operation belongs on only one surface. Include `'CLI'` explicitly to reuse the
2988
+ definition through `createCli({ runtimeTools })`; adding CLI support never
2989
+ widens existing definitions silently. The configured identity becomes the
2947
2990
  hook/lifecycle `OperationIdentity` and the tool `RequestEvent`
2948
2991
  (`serviceName`, `action`, `httpMethod`). A runtime operation has no HTTP route,
2949
2992
  so no fake `path` is added.
@@ -2960,12 +3003,88 @@ The MCP registration uses an identity carrier: the SDK advertises the compiled
2960
3003
  JSON Schema but forwards the raw object into Stitchkit. Input failures therefore
2961
3004
  use the same validation, lifecycle and hook path as contract tools.
2962
3005
 
3006
+ ### Managed wait, download, upload and view-file
3007
+
3008
+ The common imperative shapes have typed definition factories. They produce
3009
+ ordinary runtime-tool definitions, so one value can enter `runtimeTools` on MCP
3010
+ and Agent and receives the same lifecycle, hooks, context, cancellation,
3011
+ collision checks and introspection as any `defineRuntimeTool` operation:
3012
+
3013
+ ```ts
3014
+ import {
3015
+ defineDownloadTool,
3016
+ defineUploadTool,
3017
+ defineViewFileTool,
3018
+ defineWaitTool,
3019
+ } from 'stitchkit/tools'
3020
+ import { z } from 'zod'
3021
+
3022
+ const waitForJob = defineWaitTool({
3023
+ name: 'wait_for_job',
3024
+ description: 'Wait for a job to finish',
3025
+ identity: { serviceName: 'jobs', action: 'waitForJob', scope: 'user' },
3026
+ input: z.object({ id: z.string() }),
3027
+ state: z.object({ id: z.string(), status: z.enum(['RUNNING', 'DONE']) }),
3028
+ poll: ({ id }, context) => getJob(id, context.signal),
3029
+ done: (state) => state.status === 'DONE',
3030
+ backoff: [2, 3, 5],
3031
+ defaultTimeout: 120,
3032
+ })
3033
+
3034
+ const downloadResult = defineDownloadTool({
3035
+ name: 'download_result',
3036
+ description: 'Download a completed result',
3037
+ identity: { serviceName: 'jobs', action: 'downloadResult', scope: 'user' },
3038
+ input: z.object({ id: z.string(), dir: z.string().optional() }),
3039
+ resolveUrl: ({ id }) => resolveResultUrl(id),
3040
+ defaultDir: './downloads',
3041
+ dirFromInput: ({ dir }) => dir,
3042
+ })
3043
+
3044
+ const uploadInput = defineUploadTool({
3045
+ name: 'upload_input',
3046
+ description: 'Upload a local input file',
3047
+ identity: { serviceName: 'jobs', action: 'uploadInput', scope: 'user' },
3048
+ output: z.object({ url: z.url() }),
3049
+ upload: (path, context) => uploadFile(path, context.signal),
3050
+ })
3051
+
3052
+ const viewResult = defineViewFileTool({
3053
+ name: 'view_result',
3054
+ description: 'Inspect generated media',
3055
+ identity: { serviceName: 'jobs', action: 'viewResult', scope: 'user' },
3056
+ // Local files are disabled when baseDir is omitted. URL fetches retain SSRF
3057
+ // protection; use allowPrivateHosts only for an explicitly trusted network.
3058
+ baseDir: '/srv/job-media',
3059
+ })
3060
+
3061
+ const runtimeTools = [waitForJob, downloadResult, uploadInput, viewResult]
3062
+ ```
3063
+
3064
+ Managed factories return neutral validated data. Expected failures go through
3065
+ the standard failed `ToolResult` path rather than masquerading as a successful
3066
+ payload with `isError`. Wait sleep is abort-aware, guarded downloads receive the
3067
+ active signal, and upload implementations receive it on their typed context.
3068
+ Managed view-file accepts one or several paths/URLs, retains successful
3069
+ multimodal content beside structured per-item `errors`, and charges every item
3070
+ to one total 20 MB batch budget. Its MCP and Agent presenters are built in;
3071
+ remote fetch cancellation follows the active call signal.
3072
+
3073
+ The older `mountWait`, `mountDownload`, `mountUpload` and `mountViewFile`
3074
+ functions remain
3075
+ intentional raw MCP adapters with their text-envelope behavior. Use them under
3076
+ `rawTools` only when opting out of managed policy is deliberate; they share the
3077
+ same neutral mechanics but do not gain lifecycle or hooks. The raw view-file
3078
+ adapter preserves its content-only MCP envelope while using the same bounded
3079
+ batch operation.
3080
+
2963
3081
  ### Explicit raw SDK registration
2964
3082
 
2965
3083
  `rawTools` is deliberately named as an escape hatch. A tool registered there
2966
3084
  does **not** receive stitchkit schema policy, lifecycle, per-call context or
2967
- hooks. The built-in `mountViewFile` helper remains raw for callers that choose
2968
- that boundary; it fetches media with SSRF and path-traversal defenses:
3085
+ hooks. The built-in `mountViewFile`, `mountWait`, `mountDownload` and
3086
+ `mountUpload` helpers remain raw for callers that choose that boundary; the
3087
+ media helper fetches with SSRF and path-traversal defenses:
2969
3088
 
2970
3089
  ```ts
2971
3090
  import { createMcpHandler, mountViewFile } from 'stitchkit/tools'
@@ -2979,8 +3098,10 @@ const handleMcp = createMcpHandler({
2979
3098
  ```
2980
3099
 
2981
3100
  Use raw registration only when opting out is intentional. For a protected
2982
- `view_file`, define it with `defineRuntimeTool`, include it in `runtimeTools`,
2983
- and call the exported `resolveMedia` core from its handler.
3101
+ `view_file`, use `defineViewFileTool` in `runtimeTools`; it supplies the
3102
+ canonical schemas, shared batch budget, structured partial failures and both
3103
+ multimodal presenters. `resolveMedia` remains the single-item core for custom
3104
+ operations whose contract intentionally differs.
2984
3105
 
2985
3106
  ## Introspecting the complete tool surface
2986
3107
 
@@ -3061,11 +3182,13 @@ identical. Write the handler once; it serves every surface.
3061
3182
 
3062
3183
  # CLI
3063
3184
 
3064
- The same contract that drives the HTTP API, MCP tools and agent tools also
3065
- drives a command-line program. `createCli` turns contract methods into commands
3066
- `myapp generate "a fox" --wait`, `myapp models list --json | jq …` — run
3067
- through the very same validation, auth gate and error model as every other
3068
- surface (HTTP MCP agent CLI, [ADR 0014](../decisions/0014-tool-http-parity.md)).
3185
+ The same contract and managed runtime tools that drive HTTP, MCP and agent
3186
+ surfaces can also drive a command-line program. `createCli` composes their
3187
+ commands with explicitly local binary commands in one router and help tree.
3188
+ Managed commands `myapp generate "a fox" --wait`,
3189
+ `myapp models list --json | jq …` — run through the same validation, auth gate
3190
+ and error model as every other managed surface (HTTP ≡ MCP ≡ agent ≡ CLI,
3191
+ [ADR 0014](../decisions/0014-tool-http-parity.md)).
3069
3192
 
3070
3193
  It exists for what the other three surfaces cannot do: a generation kicked off
3071
3194
  with `Bash(run_in_background)` that notifies on exit, a `SKILL.md` that shells
@@ -3086,8 +3209,10 @@ the CLI never silently turns your existing API tools into shell commands.
3086
3209
  ```
3087
3210
 
3088
3211
  A fresh contract shows **zero** CLI commands until methods opt in — that is the
3089
- design, not a bug. The command name is the tool name `toolName` if set, else a
3090
- verb-aware name from the method + prefix (`list` `list_widgets`, `get`
3212
+ design, not a bug. A pathless runtime definition follows the same rule: it must
3213
+ explicitly include `'CLI'` in `transports`; the undefined default remains
3214
+ `['MCP', 'AGENT']`. The command name is the tool name — `toolName` if set, else
3215
+ a verb-aware name from the method + prefix (`list` → `list_widgets`, `get` →
3091
3216
  `get_widget`), not a literal `prefix_key`.
3092
3217
 
3093
3218
  ## A minimal CLI
@@ -3105,7 +3230,7 @@ await createCli({
3105
3230
  name: 'myapp',
3106
3231
  version: '1.0.0',
3107
3232
  services: [catalogService, generateService],
3108
- auth: process.env.MYAPP_TOKEN, // resolved ONCE, like a stdio MCP server
3233
+ resolveAuth: () => resolveToken(process.env.MYAPP_TOKEN), // lazy, at most once
3109
3234
  })
3110
3235
  ```
3111
3236
 
@@ -3117,6 +3242,81 @@ await createCli({
3117
3242
  `stitchkit/cli` pulls in neither the MCP SDK nor `ai`, so a CLI binary needs no
3118
3243
  MCP/agent peer dependencies.
3119
3244
 
3245
+ ## Pathless managed commands
3246
+
3247
+ Use `runtimeTools` when an application operation has no HTTP path but must keep
3248
+ the canonical operation identity, context, lifecycle/RBAC, hooks and
3249
+ introspection. It can share one definition with MCP and Agent while opting into
3250
+ CLI explicitly:
3251
+
3252
+ ```ts
3253
+ import { createCli } from 'stitchkit/cli'
3254
+ import { defineUploadTool } from 'stitchkit/tools'
3255
+ import { z } from 'zod'
3256
+
3257
+ const uploadInput = defineUploadTool({
3258
+ name: 'upload_input',
3259
+ description: 'Upload one local input file',
3260
+ identity: { serviceName: 'jobs', action: 'uploadInput', scope: 'user' },
3261
+ output: z.object({ url: z.url() }),
3262
+ transports: ['MCP', 'AGENT', 'CLI'],
3263
+ upload: (path, context) => uploadFile(path, context.signal),
3264
+ })
3265
+
3266
+ await createCli({
3267
+ name: 'myapp',
3268
+ version: '1.0.0',
3269
+ runtimeTools: [uploadInput],
3270
+ })
3271
+ ```
3272
+
3273
+ `services` and `runtimeTools` may each be static arrays or factories receiving
3274
+ the resolved identity. A runtime-only CLI is valid; `stitchkit/cli` still pulls
3275
+ in neither MCP nor AI peers. Contract/runtime name collisions and reserved
3276
+ option fields fail through the same checks before managed dispatch.
3277
+
3278
+ ## Native binary commands
3279
+
3280
+ Login, self-update, diagnostics, integration setup and shell completion belong
3281
+ to the executable, not to HTTP/MCP/Agent. Define them with `defineCliCommand`:
3282
+
3283
+ ```ts
3284
+ import { createCli, defineCliCommand } from 'stitchkit/cli'
3285
+ import { z } from 'zod'
3286
+
3287
+ const login = defineCliCommand({
3288
+ name: 'login',
3289
+ description: 'Store credentials for later managed commands',
3290
+ input: z.object({ token: z.string() }),
3291
+ output: z.object({ configured: z.boolean() }),
3292
+ handler: async ({ input }) => {
3293
+ await saveToken(input.token)
3294
+ return { configured: true }
3295
+ },
3296
+ })
3297
+
3298
+ await createCli({
3299
+ name: 'myapp',
3300
+ version: '1.0.0',
3301
+ commands: [login],
3302
+ resolveAuth: loadStoredIdentity,
3303
+ services: (identity) => createRemoteServices(identity),
3304
+ })
3305
+ ```
3306
+
3307
+ Native commands receive only typed `input`, parsed global `options` and the
3308
+ configured stdout/stderr writers. They reuse help, argv/stdin parsing,
3309
+ validation, dry-run, error envelopes and exit mapping, but deliberately have no
3310
+ fake service/action/scope/method identity, lifecycle or tool hooks and never
3311
+ appear in MCP/Agent manifests.
3312
+
3313
+ `--version`, a selected native command and its command help run before
3314
+ `resolveAuth`, services, context or runtime-tool factories. Top-level help is
3315
+ also credential-free when managed surfaces are static. A dynamic factory must
3316
+ resolve identity to discover its command names; its collisions are checked at
3317
+ that resolution boundary. If eager global collision proof matters, keep the
3318
+ surface static.
3319
+
3120
3320
  ## Calling commands
3121
3321
 
3122
3322
  ```
@@ -3201,7 +3401,7 @@ const authHook = createAuthHook({ /* resolve, resolveFromContext, rules */ })
3201
3401
  await createCli({
3202
3402
  name: 'myapp',
3203
3403
  version: '1.0.0',
3204
- auth: await resolveIdentityFromToken(process.env.MYAPP_TOKEN),
3404
+ resolveAuth: () => resolveIdentityFromToken(process.env.MYAPP_TOKEN),
3205
3405
  context: (identity) => ({ user: identity }), // resolveFromContext reads this
3206
3406
  lifecycle: { beforeHandle: authHook }, // same policy; HTTP wires it as authorize
3207
3407
  services,
@@ -3233,11 +3433,12 @@ await tools.createCli({
3233
3433
  })
3234
3434
  ```
3235
3435
 
3236
- ## Not in v1
3436
+ ## Remaining boundary
3237
3437
 
3238
- File-upload (`multipart`) endpoints are CLI-invisible, the same as on MCP /
3239
- agent a dedicated upload command (auto-uploading local paths) is future work.
3240
- Streaming (SSE) output is not yet piped to stdout.
3438
+ File-upload (`multipart`) contract endpoints remain CLI-invisible, the same as
3439
+ on MCP/Agent: their wire body is not a JSON tool form. Model file-oriented
3440
+ application behavior as a managed pathless command, or binary-only behavior as
3441
+ a native command. Streaming (SSE) output is not yet piped to stdout.
3241
3442
 
3242
3443
 
3243
3444
  ==============================================================================
@@ -5079,6 +5280,38 @@ domain run-state remain application resources and close explicitly after server
5079
5280
  drain. Do not call `runtime.stop()` or `socket.io.close()` in parallel with
5080
5281
  `shutdown()`.
5081
5282
 
5283
+ ### Stdio process signals
5284
+
5285
+ An MCP stdio handle has `close()`, not managed HTTP `shutdown()` with a force
5286
+ signal and deadline. Bind its process lifecycle with the truthful close-only
5287
+ sibling from `stitchkit/tools`:
5288
+
5289
+ ```ts
5290
+ import {
5291
+ bindStdioProcessSignals,
5292
+ createStdioMcpServer,
5293
+ } from 'stitchkit/tools'
5294
+
5295
+ const stdio = await createStdioMcpServer(config)
5296
+ const binding = bindStdioProcessSignals(stdio, {
5297
+ onClose: () => stopWorkers(),
5298
+ onComplete: () => { process.exitCode = 0 },
5299
+ onError: (phase, error) => {
5300
+ console.error(phase, error) // stderr; stdout stays JSON-RPC-only
5301
+ process.exitCode = 1
5302
+ },
5303
+ })
5304
+
5305
+ await binding.promise
5306
+ ```
5307
+
5308
+ The first signal starts exactly one close chain. A same-turn duplicate is
5309
+ ignored; a later signal restores the default OS disposition because the
5310
+ official stdio close is not abortable and Stitchkit will not pretend otherwise.
5311
+ `binding.close()` removes idle listeners and resolves the promise with
5312
+ `undefined`. No listener is installed until the binder is called, and the
5313
+ framework never calls `process.exit()`.
5314
+
5082
5315
  ### Deploy on Node
5083
5316
 
5084
5317
  The contract, `implement`, hooks, auth and the client are runtime-agnostic. Only
@@ -6443,8 +6676,11 @@ from the root `stitchkit`.
6443
6676
  | `HttpMethod` | _type_ | `GET \| HEAD \| POST \| PUT \| PATCH \| DELETE` |
6444
6677
  | `Transport` | _type_ | `HTTP \| MCP \| AGENT \| CLI` |
6445
6678
  | `TransportSource` | _type_ | `http \| mcp \| agent \| cli` — the value of `ctx.source` |
6446
- | `RuntimeContext` | _type_ | the loose context seen by transport and hooks |
6447
- | `HandlerContext` | _type_ | the typed context seen by a handler |
6679
+ | `RuntimeContext` | _type_ | the loose context seen by transport and hooks, including optional typed `mcp` metadata |
6680
+ | `HandlerContext` | _type_ | the typed context seen by a handler, including optional typed `mcp` metadata |
6681
+ | `McpCallContext` | _type_ | validated metadata for the active managed MCP call (`era`, method, tool, client and multi-round fields) |
6682
+ | `McpClientInfo` | _type_ | self-reported MCP host name/version; attribution only, never application identity |
6683
+ | `McpRoundOutcome` | _type_ | managed multi-round attempt outcome |
6448
6684
  | `EndpointHandlerContext` | _type_ | one endpoint handler's fully inferred params, input, files and runtime context |
6449
6685
  | `EndpointFn` | _type_ | the call signature of one client method |
6450
6686
  | `TypedClient` | _type_ | the full typed client for a contract |
@@ -6775,14 +7011,16 @@ payload.
6775
7011
  | `createMcpHandler` | function | a stateless dual-era Streamable-HTTP MCP handler — [guide](../guide/mcp-and-agents.md#mcp--createmcphandler) |
6776
7012
  | `createMcpHttpRoute` | function | framework-owned `RawRoute` adapter for an MCP HTTP handler |
6777
7013
  | `createStdioMcpServer` | function | a complete stdio MCP server — [guide](../guide/mcp-and-agents.md#mcp-over-stdio--createstdiomcpserver) |
7014
+ | `bindStdioProcessSignals` | function | explicitly bind OS signals to one close-only stdio handle — [guide](../guide/testing-and-deployment.md#stdio-process-signals) |
6778
7015
  | `buildMcpServer` | function | build an `McpServer` from contract/runtime surfaces; no-auth configs omit the second argument |
6779
7016
  | `mountMcp` | function | add contract tools to an existing `McpServer` — [guide](../guide/mcp-and-agents.md#mountmcp) |
6780
7017
  | `implementRemote` | function | bind a contract to a remote HTTP API — [guide](../guide/mcp-and-agents.md#proxying-a-remote-api--implementremote) |
6781
7018
  | `mountAgent` | function | a Vercel AI SDK `ToolSet` from a service — [guide](../guide/mcp-and-agents.md#ai-agents--mountagent) |
6782
- | `defineRuntimeTool` | function | define one validated pathless operation for MCP, Agent or both — [guide](../guide/mcp-and-agents.md#pathless-runtime-tools-and-multimodal-results) |
7019
+ | `defineRuntimeTool` | function | define one validated pathless operation for explicit MCP, Agent and/or CLI surfaces — [guide](../guide/mcp-and-agents.md#pathless-runtime-tools-and-multimodal-results) |
6783
7020
  | `createRuntimeToolFactory` | function | bind shared identity and Zod-validated per-call context for runtime tools — [guide](../guide/mcp-and-agents.md#pathless-runtime-tools-and-multimodal-results) |
6784
7021
  | `createToolInvoker` | function | compile an exposure-aware in-process dispatcher over the canonical tool runner — [guide](../guide/mcp-and-agents.md#in-process-calls--createtoolinvoker) |
6785
7022
  | `createCli` | function | a command-line program from contracts — [guide](../guide/cli.md) (also on `stitchkit/cli`) |
7023
+ | `defineCliCommand` | function | define one typed CLI-only command with no fake managed-tool identity — [guide](../guide/cli.md#native-binary-commands) (also on `stitchkit/cli`) |
6786
7024
  | `createToolkit` | function | context-typed tool mounts — [guide](../guide/cli.md#typed-context) |
6787
7025
  | `mountViewFile` | function | a native multimodal "view file" MCP tool |
6788
7026
  | `resolveMedia` | function | resolve a media reference for a tool result |
@@ -6796,6 +7034,10 @@ payload.
6796
7034
  | `McpLegacyPolicy` | _type_ | `'serve' \| 'reject'` protocol-era compatibility policy |
6797
7035
  | `McpStdioHandle` | _type_ | closeable official stdio transport handle |
6798
7036
  | `StdioMcpServerConfig` | _type_ | config for `createStdioMcpServer` |
7037
+ | `StdioCloseTarget` | _type_ | minimal close-only target accepted by `bindStdioProcessSignals` |
7038
+ | `StdioProcessSignalsOptions` | _type_ | signal source, close callbacks and phased error reporting |
7039
+ | `StdioProcessSignalsBinding` | _type_ | close-only signal binding with observed `promise` and listener `close()` |
7040
+ | `StdioProcessSignalsErrorPhase` | _type_ | `'prepare' \| 'close' \| 'complete'` reporting phase |
6799
7041
  | `McpServerBuildConfig` | _type_ | shared config for `buildMcpServer` |
6800
7042
  | `McpServerSharedConfig` | _type_ | transport-neutral options shared by direct and finite surface configs |
6801
7043
  | `McpServer` | _type_ | official split-SDK server instance accepted by raw extension points |
@@ -6833,10 +7075,16 @@ payload.
6833
7075
  | `CimdCacheEvent` | _type_ | observable CIMD cache hit, miss, revalidation and eviction event |
6834
7076
  | `createSecureClientMetadataFetcher` | function | production HTTPS, DNS/IP-pinned CIMD fetcher |
6835
7077
  | `RuntimeAgentModelOutput` | _type_ | AI SDK model-facing text/JSON/content output returned by `present.agent` |
6836
- | `RuntimeToolTransport` | _type_ | runtime exposure: `'MCP' \| 'AGENT'` |
7078
+ | `RuntimeToolTransport` | _type_ | runtime exposure: `'MCP' \| 'AGENT' \| 'CLI'`; omission still means MCP+Agent only |
6837
7079
  | `AgentMountConfig` | _type_ | config for `mountAgent` |
6838
7080
  | `AgentContext` | _type_ | the context merged into agent tool handlers |
6839
7081
  | `CliConfig` | _type_ | config for `createCli` |
7082
+ | `CliSurfaceSource` | _type_ | static managed surface or identity-dependent surface factory for `createCli` |
7083
+ | `CliCommandDefinition` | _type_ | Zod-first CLI-only command union |
7084
+ | `CliCommandDefinitionBase` | _type_ | native command name, description and input schema |
7085
+ | `CliCommandDefinitionWithOutput` | _type_ | native command with declared output schema and validated handler result |
7086
+ | `CliCommandDefinitionWithoutOutput` | _type_ | void native command with no output schema |
7087
+ | `CliCommandContext` | _type_ | parsed native command input, global options and injected writers |
6840
7088
  | `CliWaitConfig` | _type_ | `--wait` polling config |
6841
7089
  | `ExitCodeMap` | _type_ | `ToolResult.code` → process exit code |
6842
7090
  | `Toolkit` | _type_ | the context-pinned tool surface from `createToolkit` |
@@ -6853,9 +7101,15 @@ payload.
6853
7101
  | `ToolInvokerConfig` | _type_ | compile-time exposure, extension and presentation options |
6854
7102
  | `ToolInvocationOptions` | _type_ | per-call source, context, lifecycle, hooks and output-strip reporter |
6855
7103
  | `ToolInvokerTransport` | _type_ | invoker exposure policy: `MCP \| AGENT \| CLI` |
6856
- | `ToolCallContext` | _type_ | the context every tool hook receives — `{ source }` plus whatever the mount's `context` added |
6857
- | `ViewFileOptions` | _type_ | options for `mountViewFile` |
7104
+ | `ToolCallContext` | _type_ | the context every tool hook receives — `{ source, mcp? }` plus whatever the mount's `context` added |
7105
+ | `ViewFileOptions` | _type_ | shared URL/local-sandbox policy for `defineViewFileTool`, `mountViewFile` and `resolveMedia` |
7106
+ | `ViewFileOutput` | _type_ | neutral managed batch result with multimodal `content` and per-item `errors` |
7107
+ | `ViewFileInputSchema` | constant | fixed one-or-many media path/URL input schema |
7108
+ | `ViewFileOutputSchema` | constant | Zod schema for the neutral managed view-file batch result |
7109
+ | `ViewFileErrorSchema` | constant | Zod schema for one structured per-item view failure |
6858
7110
  | `McpAnnotations` | _type_ | MCP annotations on a media result |
7111
+ | `McpAnnotationsSchema` | constant | Zod schema for MCP media annotations |
7112
+ | `McpMediaContentSchema` | constant | Zod schema for text/image/audio media content |
6859
7113
  | `CollectToolsConfig` | _type_ | options for `collectTools` |
6860
7114
  | `findUntypedProperties` | function | every property in a JSON Schema with no `type`/`enum`/`$ref` — what a model is shown and cannot obey ([guide](../guide/mcp-and-agents.md)) |
6861
7115
  | `UntypedProperty` | _type_ | one such property — `{ path, description? }` |
@@ -6868,13 +7122,28 @@ payload.
6868
7122
 
6869
7123
  ### Native tools
6870
7124
 
6871
- Generic host-supplied tools mounted onto a server not derived from a contract.
7125
+ Generic host-supplied operationsmanaged definitions for the canonical
7126
+ runtime-tool runner, plus deliberate raw MCP adapters over the same mechanics.
6872
7127
 
6873
7128
  | Export | Kind | Summary |
6874
7129
  |--------|------|---------|
6875
- | `mountDownload` | function | a "download a URL to disk" tool (SSRF-guarded, size-capped) |
6876
- | `mountUpload` | function | an "upload a local file" tool |
6877
- | `mountWait` | function | a generic `--wait`-style polling tool |
7130
+ | `defineDownloadTool` | function | define a guarded, size-capped managed download for MCP/Agent `runtimeTools` |
7131
+ | `defineUploadTool` | function | define a typed managed local-file upload for MCP/Agent `runtimeTools` |
7132
+ | `defineWaitTool` | function | define a typed, cancellable managed polling operation for MCP/Agent `runtimeTools` |
7133
+ | `defineViewFileTool` | function | define a guarded, batch-capped managed multimodal operation for MCP/Agent `runtimeTools` |
7134
+ | `DefineDownloadToolConfig` | _type_ | Zod input, stable identity, URL resolver, storage policy and presenters for `defineDownloadTool` |
7135
+ | `DefineUploadToolConfig` | _type_ | stable identity, typed output, upload callback and presenters for `defineUploadTool` |
7136
+ | `DefineWaitToolConfig` | _type_ | Zod input/state, identity, poll/done/timeout policy and presenters for `defineWaitTool` |
7137
+ | `DefineViewFileToolConfig` | _type_ | identity, media security policy and optional presenters for `defineViewFileTool` |
7138
+ | `ManagedNativeToolConfig` | _type_ | shared name, description, identity, exposure and annotations for managed native factories |
7139
+ | `NativeToolIdentity` | _type_ | pathless service/action/scope/meta identity; semantic method is factory-owned |
7140
+ | `ManagedWaitRender` | _type_ | optional managed wait terminal text and failure classification |
7141
+ | `DownloadResultSchema` | constant | Zod schema for a saved download's path, byte size and MIME type |
7142
+ | `DownloadResult` | _type_ | validated output of `defineDownloadTool` |
7143
+ | `UploadToolInputSchema` | constant | fixed `{ path: string }` input schema for `defineUploadTool` |
7144
+ | `mountDownload` | function | raw MCP "download a URL to disk" adapter (SSRF-guarded, size-capped) |
7145
+ | `mountUpload` | function | raw MCP "upload a local file" adapter |
7146
+ | `mountWait` | function | raw MCP generic `--wait`-style polling adapter |
6878
7147
  | `DownloadToolConfig` | _type_ | config for `mountDownload` |
6879
7148
  | `UploadToolConfig` | _type_ | config for `mountUpload` |
6880
7149
  | `WaitToolConfig` | _type_ | config for `mountWait` |
@@ -6979,17 +7248,25 @@ runtime-agnostic pieces of `stitchkit/server` and the error helpers.
6979
7248
 
6980
7249
  ## `stitchkit/cli`
6981
7250
 
6982
- Server-only. Turns contracts into a command-line program — the fourth transport.
6983
- Light by design: needs neither the MCP SDK nor the `ai` peer.
7251
+ Server-only. Composes contract/runtime managed operations and CLI-only native
7252
+ commands into one command-line program. Light by design: needs neither the MCP
7253
+ SDK nor the `ai` peer.
6984
7254
 
6985
7255
  | Export | Kind | Summary |
6986
7256
  |--------|------|---------|
6987
7257
  | `createCli` | function | build and run a CLI from contracts — [guide](../guide/cli.md) |
7258
+ | `defineCliCommand` | function | define one Zod-typed CLI-only executable command |
6988
7259
  | `parseCliArgs` | function | argv → typed tool args against a schema (advanced) |
6989
7260
  | `pollUntilDone` | function | the generic `--wait` poller (advanced) |
6990
7261
  | `emitResult` | function | write a `ToolResult` to stdout/stderr + exit code (advanced) |
6991
7262
  | `DEFAULT_EXIT_CODES` | const | the default `ToolResult.code` → exit-code map |
6992
7263
  | `CliConfig` | _type_ | config for `createCli` |
7264
+ | `CliSurfaceSource` | _type_ | static service/runtime array or identity-dependent factory |
7265
+ | `CliCommandDefinition` | _type_ | native command definition union |
7266
+ | `CliCommandDefinitionBase` | _type_ | native command name, description and input schema |
7267
+ | `CliCommandDefinitionWithOutput` | _type_ | native command with validated declared output |
7268
+ | `CliCommandDefinitionWithoutOutput` | _type_ | native void command without an output contract |
7269
+ | `CliCommandContext` | _type_ | parsed input, global options and stdout/stderr writers |
6993
7270
  | `CliRunOptions` | _type_ | parsed global flags (`--json`, `--wait`, …) |
6994
7271
  | `ParsedCliArgs` | _type_ | result of `parseCliArgs` |
6995
7272
  | `CliWaitConfig` | _type_ | per-command `--wait` polling config |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.53.1",
3
+ "version": "0.54.0",
4
4
  "description": "Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.",
5
5
  "keywords": [
6
6
  "bun",