stitchkit 0.103.13 → 0.104.1

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 (223) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/darwin-native.cjs +1 -3
  3. package/dist/agent-runtime/store-conformance-lifecycle.d.ts +10 -10
  4. package/dist/agent-runtime/store-conformance-support.d.ts +5 -5
  5. package/dist/application/directory-inbox-accept.d.ts +13 -0
  6. package/dist/application/directory-inbox-accept.d.ts.map +1 -0
  7. package/dist/application/directory-inbox-contract.d.ts +42 -5
  8. package/dist/application/directory-inbox-contract.d.ts.map +1 -1
  9. package/dist/application/directory-inbox-delivery.d.ts +14 -0
  10. package/dist/application/directory-inbox-delivery.d.ts.map +1 -0
  11. package/dist/application/directory-inbox-files.d.ts +28 -0
  12. package/dist/application/directory-inbox-files.d.ts.map +1 -0
  13. package/dist/application/directory-inbox-state.d.ts +14 -0
  14. package/dist/application/directory-inbox-state.d.ts.map +1 -0
  15. package/dist/application/directory-inbox.d.ts +2 -1
  16. package/dist/application/directory-inbox.d.ts.map +1 -1
  17. package/dist/application/file-state-store.d.ts +2 -8
  18. package/dist/application/file-state-store.d.ts.map +1 -1
  19. package/dist/application/state-store.d.ts +6 -1
  20. package/dist/application/state-store.d.ts.map +1 -1
  21. package/dist/browser/client-arguments.d.ts +4 -0
  22. package/dist/browser/client-arguments.d.ts.map +1 -0
  23. package/dist/browser/client-executors.d.ts.map +1 -1
  24. package/dist/browser/client.d.ts.map +1 -1
  25. package/dist/browser/contract-stream.d.ts.map +1 -1
  26. package/dist/browser/http.d.ts +2 -1
  27. package/dist/browser/http.d.ts.map +1 -1
  28. package/dist/browser/native-fetch.d.ts +8 -0
  29. package/dist/browser/native-fetch.d.ts.map +1 -0
  30. package/dist/contract/errors.d.ts +16 -12
  31. package/dist/contract/errors.d.ts.map +1 -1
  32. package/dist/contract/normalize.d.ts +5 -1
  33. package/dist/contract/normalize.d.ts.map +1 -1
  34. package/dist/contract/stream.d.ts +1 -0
  35. package/dist/contract/stream.d.ts.map +1 -1
  36. package/dist/entrypoints/agent-runtime/browser.js +3 -3
  37. package/dist/entrypoints/agent-runtime/coding-tools.js +6 -6
  38. package/dist/entrypoints/agent-runtime/harness-tools.js +8 -8
  39. package/dist/entrypoints/agent-runtime/harness.js +11 -11
  40. package/dist/entrypoints/agent-runtime/realtime.js +3 -3
  41. package/dist/entrypoints/agent-runtime/sandbox.js +6 -6
  42. package/dist/entrypoints/agent-runtime.js +13 -13
  43. package/dist/entrypoints/application/diagnostic-journal.js +1 -1
  44. package/dist/entrypoints/application/directory-inbox.js +424 -119
  45. package/dist/entrypoints/application/grammy.js +1 -1
  46. package/dist/entrypoints/application.d.ts +2 -2
  47. package/dist/entrypoints/application.d.ts.map +1 -1
  48. package/dist/entrypoints/application.js +7 -3
  49. package/dist/entrypoints/cli.d.ts +2 -0
  50. package/dist/entrypoints/cli.d.ts.map +1 -1
  51. package/dist/entrypoints/cli.js +757 -136
  52. package/dist/entrypoints/contract.js +3 -3
  53. package/dist/entrypoints/files/packaging.d.ts +3 -0
  54. package/dist/entrypoints/files/packaging.d.ts.map +1 -0
  55. package/dist/entrypoints/files/packaging.js +133 -0
  56. package/dist/entrypoints/files.js +10 -107
  57. package/dist/entrypoints/index.js +7 -7
  58. package/dist/entrypoints/node.js +8 -8
  59. package/dist/entrypoints/observability.js +2 -2
  60. package/dist/entrypoints/react.js +1 -1
  61. package/dist/entrypoints/remote.js +19 -6
  62. package/dist/entrypoints/server.d.ts +1 -1
  63. package/dist/entrypoints/server.d.ts.map +1 -1
  64. package/dist/entrypoints/server.js +23 -17
  65. package/dist/entrypoints/telegram.d.ts +1 -0
  66. package/dist/entrypoints/telegram.d.ts.map +1 -1
  67. package/dist/entrypoints/telegram.js +214 -98
  68. package/dist/entrypoints/testing.js +7 -7
  69. package/dist/entrypoints/tools/connections.d.ts +2 -2
  70. package/dist/entrypoints/tools/connections.d.ts.map +1 -1
  71. package/dist/entrypoints/tools/connections.js +427 -294
  72. package/dist/entrypoints/tools/invoker.js +6 -6
  73. package/dist/entrypoints/tools/mcp.d.ts +12 -0
  74. package/dist/entrypoints/tools/mcp.d.ts.map +1 -0
  75. package/dist/entrypoints/tools/mcp.js +45 -0
  76. package/dist/entrypoints/tools.d.ts +1 -0
  77. package/dist/entrypoints/tools.d.ts.map +1 -1
  78. package/dist/entrypoints/tools.js +82 -1147
  79. package/dist/entrypoints/tracking.js +1 -1
  80. package/dist/files/boundary.d.ts +2 -8
  81. package/dist/files/boundary.d.ts.map +1 -1
  82. package/dist/files/file-io.d.ts +0 -5
  83. package/dist/files/file-io.d.ts.map +1 -1
  84. package/dist/files/native-packaging-layout.d.ts +12 -0
  85. package/dist/files/native-packaging-layout.d.ts.map +1 -0
  86. package/dist/files/native-packaging.d.ts +72 -0
  87. package/dist/files/native-packaging.d.ts.map +1 -0
  88. package/dist/files/read.d.ts +2 -2
  89. package/dist/files/read.d.ts.map +1 -1
  90. package/dist/index-1a6jtnd9.js +88 -0
  91. package/dist/index-269zqj8m.js +77 -0
  92. package/dist/{index-r34mvse1.js → index-27a2pj09.js} +1 -1
  93. package/dist/{index-r956rndn.js → index-2hyavjjj.js} +3 -3
  94. package/dist/{index-m2xqkv55.js → index-2vnh05w4.js} +83 -14
  95. package/dist/{index-pw99k9h5.js → index-43p08g7t.js} +1 -1
  96. package/dist/{index-xwq9j1sf.js → index-4e0ah203.js} +9 -7
  97. package/dist/{index-qapzv4p5.js → index-5z6jn897.js} +1 -1
  98. package/dist/{index-ahvsksw5.js → index-6d9xaxw1.js} +1 -1
  99. package/dist/{index-8zfsgar0.js → index-6drst9sg.js} +11 -5
  100. package/dist/{index-x87j7csh.js → index-77nxk6ay.js} +2 -2
  101. package/dist/{index-dnn9z96q.js → index-a0zzh8ak.js} +3 -3
  102. package/dist/{index-kmpzj1cq.js → index-a3wma1hk.js} +1 -1
  103. package/dist/{index-j8aw2c0e.js → index-bakgedh8.js} +35 -7
  104. package/dist/index-bhb950qs.js +1145 -0
  105. package/dist/{index-tgzhq07n.js → index-cvda079a.js} +1 -1
  106. package/dist/{index-n2gre124.js → index-fa4wg312.js} +4 -4
  107. package/dist/{index-zp4jpkp0.js → index-fk18k56k.js} +2 -2
  108. package/dist/{index-afjvjyck.js → index-g2j7pnak.js} +2 -134
  109. package/dist/index-g95fk660.js +116 -0
  110. package/dist/{index-r5c4fedv.js → index-h2fhjqy7.js} +2 -1
  111. package/dist/{index-sybedc8b.js → index-hbd32c6a.js} +23 -94
  112. package/dist/{index-fyphpknh.js → index-hp9d77wk.js} +1 -1
  113. package/dist/index-jd470k6q.js +109 -0
  114. package/dist/{index-b2bz0kdm.js → index-kb8styz3.js} +4 -3
  115. package/dist/{index-0c419yx7.js → index-kemm0ej5.js} +12 -9
  116. package/dist/{index-db2spkv1.js → index-ptn0g0gj.js} +3 -3
  117. package/dist/{index-w575evhq.js → index-r4r70zmh.js} +1 -1
  118. package/dist/{index-12m2v2a9.js → index-s4evrmp5.js} +1 -1
  119. package/dist/{index-1khy8y7n.js → index-t1sr4aks.js} +3 -3
  120. package/dist/{index-45tdvqa3.js → index-wh0ecvyk.js} +1 -1
  121. package/dist/{index-n53mswt3.js → index-x928baz2.js} +8 -1
  122. package/dist/index-yk4g8805.js +133 -0
  123. package/dist/{index-ygkka6zn.js → index-ywppsaby.js} +28 -5
  124. package/dist/{index-9853ymwb.js → index-za6j7nwy.js} +18 -4
  125. package/dist/internal/bounded-file-read.d.ts +41 -0
  126. package/dist/internal/bounded-file-read.d.ts.map +1 -0
  127. package/dist/internal/exclusive-lock.d.ts +2 -0
  128. package/dist/internal/exclusive-lock.d.ts.map +1 -1
  129. package/dist/internal/with-exclusive-lock.d.ts +4 -2
  130. package/dist/internal/with-exclusive-lock.d.ts.map +1 -1
  131. package/dist/telegram/broadcast-delivery.d.ts +23 -0
  132. package/dist/telegram/broadcast-delivery.d.ts.map +1 -0
  133. package/dist/telegram/broadcast-failure.d.ts +22 -0
  134. package/dist/telegram/broadcast-failure.d.ts.map +1 -0
  135. package/dist/telegram/broadcast-state.d.ts +6 -3
  136. package/dist/telegram/broadcast-state.d.ts.map +1 -1
  137. package/dist/telegram/broadcast.d.ts +10 -5
  138. package/dist/telegram/broadcast.d.ts.map +1 -1
  139. package/dist/tools/cli/format.d.ts.map +1 -1
  140. package/dist/tools/cli/invoke.d.ts +2 -2
  141. package/dist/tools/cli/invoke.d.ts.map +1 -1
  142. package/dist/tools/cli/publication-assets.d.ts +10 -0
  143. package/dist/tools/cli/publication-assets.d.ts.map +1 -0
  144. package/dist/tools/cli/publication-contract.d.ts +75 -0
  145. package/dist/tools/cli/publication-contract.d.ts.map +1 -0
  146. package/dist/tools/cli/publication-control.d.ts +4 -0
  147. package/dist/tools/cli/publication-control.d.ts.map +1 -0
  148. package/dist/tools/cli/publication-storage.d.ts +32 -0
  149. package/dist/tools/cli/publication-storage.d.ts.map +1 -0
  150. package/dist/tools/cli/publication.d.ts +35 -0
  151. package/dist/tools/cli/publication.d.ts.map +1 -0
  152. package/dist/tools/cli/update.d.ts.map +1 -1
  153. package/dist/tools/connections/define.d.ts.map +1 -1
  154. package/dist/tools/connections/error-projection.d.ts +4 -0
  155. package/dist/tools/connections/error-projection.d.ts.map +1 -0
  156. package/dist/tools/connections/errors.d.ts +24 -8
  157. package/dist/tools/connections/errors.d.ts.map +1 -1
  158. package/dist/tools/connections/limits.d.ts +11 -31
  159. package/dist/tools/connections/limits.d.ts.map +1 -1
  160. package/dist/tools/connections/mcp-client.d.ts +6 -4
  161. package/dist/tools/connections/mcp-client.d.ts.map +1 -1
  162. package/dist/tools/connections/mcp.d.ts.map +1 -1
  163. package/dist/tools/connections/openapi.d.ts.map +1 -1
  164. package/dist/tools/connections/operation-limits.d.ts +25 -0
  165. package/dist/tools/connections/operation-limits.d.ts.map +1 -0
  166. package/dist/tools/connections/sse-frames.d.ts +11 -2
  167. package/dist/tools/connections/sse-frames.d.ts.map +1 -1
  168. package/dist/tools/connections/sse-session.d.ts +15 -2
  169. package/dist/tools/connections/sse-session.d.ts.map +1 -1
  170. package/dist/tools/connections/types.d.ts +19 -1
  171. package/dist/tools/connections/types.d.ts.map +1 -1
  172. package/dist/tools/execute-result.d.ts.map +1 -1
  173. package/dist/tools/execute.d.ts.map +1 -1
  174. package/dist/tools/internal/observe-hook.d.ts +3 -0
  175. package/dist/tools/internal/observe-hook.d.ts.map +1 -0
  176. package/dist/tools/internal/surface-projector.d.ts +2 -1
  177. package/dist/tools/internal/surface-projector.d.ts.map +1 -1
  178. package/dist/tools/mcp/mount.d.ts +2 -2
  179. package/dist/tools/mcp/mount.d.ts.map +1 -1
  180. package/dist/tools/mcp/prepare.d.ts +4 -5
  181. package/dist/tools/mcp/prepare.d.ts.map +1 -1
  182. package/dist/tools/mcp/register.d.ts +4 -1
  183. package/dist/tools/mcp/register.d.ts.map +1 -1
  184. package/dist/tools/mcp/runtime-tools.d.ts.map +1 -1
  185. package/dist/tools/remote.d.ts.map +1 -1
  186. package/dist/tools/runtime-tool-execution.d.ts +65 -0
  187. package/dist/tools/runtime-tool-execution.d.ts.map +1 -0
  188. package/dist/tools/runtime-tool-mcp.d.ts +27 -0
  189. package/dist/tools/runtime-tool-mcp.d.ts.map +1 -0
  190. package/dist/tools/runtime-tool.d.ts +9 -60
  191. package/dist/tools/runtime-tool.d.ts.map +1 -1
  192. package/dist/tools/surface.d.ts +12 -14
  193. package/dist/tools/surface.d.ts.map +1 -1
  194. package/llms/application.2.txt +221 -44
  195. package/llms/application.3.txt +3 -1
  196. package/llms/application.txt +1 -1
  197. package/llms/cli.2.txt +144 -1
  198. package/llms/cli.txt +123 -104
  199. package/llms/contract.txt +1 -1
  200. package/llms/files-packaging.txt +27 -0
  201. package/llms/process.txt +63 -1
  202. package/llms/server.4.txt +4 -1
  203. package/llms/server.5.txt +1 -0
  204. package/llms/stitchkit.2.txt +14 -0
  205. package/llms/stitchkit.txt +6 -4
  206. package/llms/telegram.txt +64 -2
  207. package/llms/tools-connections.txt +3 -0
  208. package/llms/tools-mcp.txt +29 -0
  209. package/llms/tools.2.txt +59 -0
  210. package/llms/tools.3.txt +1 -0
  211. package/llms/tools.txt +11 -52
  212. package/llms/upgrading-0.37.0-0.44.0.txt +444 -0
  213. package/llms/{upgrading-0.44.0-0.60.0.txt → upgrading-0.44.0-0.61.0.txt} +140 -271
  214. package/llms/{upgrading-0.61.0-0.74.0.txt → upgrading-0.62.0-0.80.0.txt} +217 -140
  215. package/llms/{upgrading-0.75.0-0.103.0.txt → upgrading-0.81.0-0.104.0.txt} +154 -217
  216. package/llms/upgrading.txt +4 -4
  217. package/llms-full.txt +745 -60
  218. package/llms.txt +14 -12
  219. package/native-assets.json +8 -0
  220. package/package.json +12 -3
  221. package/dist/index-wykebpn9.js +0 -221
  222. package/llms/upgrading-0.37.0.txt +0 -176
  223. package/dist/{index-69nvze1q.js → index-pptyv13k.js} +3 -3
package/llms/tools.2.txt CHANGED
@@ -13,6 +13,65 @@ Parts of this slice:
13
13
  # Guide: MCP & agents (docs/guide/mcp-and-agents.md) (continued)
14
14
  ==============================================================================
15
15
 
16
+ ## Proxying a remote API — `implementRemote`
17
+
18
+ `implement` binds a contract to local handlers. `implementRemote` binds it to a
19
+ remote HTTP API instead — every handler forwards the call to a deployed server
20
+ through the contract's typed client:
21
+
22
+ ```ts
23
+ import { createHttpClient } from 'stitchkit'
24
+ import { implementRemote } from 'stitchkit/remote'
25
+ import { createStdioMcpServer } from 'stitchkit/tools/mcp'
26
+
27
+ const http = createHttpClient({
28
+ baseUrl: 'https://api.example.com',
29
+ headers: () => ({ Authorization: `Bearer ${apiKey}` }),
30
+ })
31
+
32
+ const stdio = await createStdioMcpServer({
33
+ serverInfo: { name: 'my-app', version: '1.0.0' },
34
+ auth: null,
35
+ services: contracts.map((c) => implementRemote(c, http)),
36
+ })
37
+
38
+ // During graceful shutdown:
39
+ await stdio.close()
40
+ ```
41
+
42
+ This is how you ship a thin **local** MCP server for an API that already runs in
43
+ the cloud: the local process owns only the transport and any filesystem-facing
44
+ native tools, while every contract tool proxies to the remote API. One contract,
45
+ no duplicated business logic.
46
+
47
+ `implementRemote(contract, http, { transformArgs })` takes an optional
48
+ `transformArgs` hook that rewrites a call's arguments before they are forwarded
49
+ — e.g. to upload a local file referenced in the args and swap in its URL.
50
+
51
+ The call's signal reaches the existing typed HTTP client's request options, for endpoints
52
+ with or without arguments. An already cancelled call, including cancellation while an async
53
+ argument transform runs, dispatches no request. Bind your process signal to `createCli` or
54
+ `createCliInvoker`; `REQUEST_ABORTED` is a safe nonretryable failure with CLI exit130.
55
+ Cooperative origins observe `ctx.signal` and release resources when their work ends. An
56
+ origin or provider ignoring cancellation may continue after the local wait stops.
57
+
58
+ An explicit `AppError.retryable` boolean also survives this remote hop. `false` on502 and
59
+ `true` on409 remain the application's recommendation through HTTP, CLI, MCP and AI tools.
60
+ Absent metadata keeps the previous defaults and does not alter HTTP transport retries.
61
+
62
+ ## Structured output
63
+
64
+ When a contract endpoint or runtime tool declares `output`, its MCP tool
65
+ registers that exact schema as `outputSchema` and returns the validated value as
66
+ `structuredContent` alongside a JSON text block. Every JSON root type is kept
67
+ unchanged: object, array, string, number, boolean and `null`. Stitchkit does not
68
+ wrap non-object values in `{ result }`; protocol-era adaptation belongs to the
69
+ official SDK.
70
+
71
+ A tool without an output contract advertises no `outputSchema` and returns no
72
+ invented structured value. Its successful MCP result has an empty `content`
73
+ list.
74
+
16
75
  ## A different answer for tools — `withToolView`
17
76
 
18
77
  An admin page wants the full record; a model wants a card. Every byte of a
package/llms/tools.3.txt CHANGED
@@ -278,6 +278,7 @@ Advanced building blocks — the shared machinery the mounts are built on.
278
278
  | `TransportSummary` | _type_ | `{ contractServices, runtimeTools, totals, sources }` from `summarizeTransports` |
279
279
  | `TransportCounts` | _type_ | per-transport counts (`{ HTTP, MCP, AGENT, CLI }`) |
280
280
  | `ToolSurfaceDefinition` | _type_ | shared object-shaped `{ services?, runtimeTools? }` introspection surface |
281
+ | `ToolSurfaceProjection` | _type_ | one readonly services/runtime container shared by full and MCP-only surfaces; executable mounts specialize the runtime definition without pulling in another adapter's peers |
281
282
  | `ToolManifestConfig` | _type_ | mixed surface plus required model-facing `transport` and presentation options |
282
283
  | `coerceJsonArgs` | function | coerce JSON-stringified array/object tool arguments |
283
284
  | `flattenToolJsonSchema` | function | project structurally identifiable discriminated unions into conservative object joins; scalar collisions retain provable types, object/array collisions retain structural alternatives, and field descriptions retain discriminator labels plus availability/requiredness hints; the projection never executes validation |
package/llms/tools.txt CHANGED
@@ -147,13 +147,20 @@ get the view's defaults and projected answer, which is what `collectTools` and
147
147
 
148
148
  ## MCP — `createMcpHandler`
149
149
 
150
+ An MCP-only server imports `stitchkit/tools/mcp` and installs
151
+ `stitchkit`, `zod` and `@modelcontextprotocol/server`. The leaf's runtime and strict
152
+ NodeNext declarations require no `ai`. It exports the existing HTTP/stdio server,
153
+ schema policy, catalog and resource helpers; their implementation and security are shared
154
+ with the full `stitchkit/tools` barrel. Use that full barrel when the application also
155
+ needs its AI SDK features.
156
+
150
157
  `createMcpHandler` builds a complete stateless Streamable-HTTP MCP endpoint on
151
158
  the official TypeScript SDK v2 server package. It owns server creation,
152
159
  protocol-era negotiation and shutdown; application code does not import the SDK
153
160
  to mount a normal endpoint.
154
161
 
155
162
  ```ts
156
- import { createMcpHandler, createMcpHttpRoute } from 'stitchkit/tools'
163
+ import { createMcpHandler, createMcpHttpRoute } from 'stitchkit/tools/mcp'
157
164
  import { createServer } from 'stitchkit/server'
158
165
 
159
166
  const mcp = createMcpHandler({
@@ -287,7 +294,7 @@ Every advertised tool and every tool result therefore carries the fingerprint of
287
294
  the catalog it came from:
288
295
 
289
296
  ```ts
290
- import { readMcpCatalogStamp } from 'stitchkit/tools';
297
+ import { readMcpCatalogStamp } from 'stitchkit/tools/mcp';
291
298
 
292
299
  const listed = await client.listTools();
293
300
  let known = readMcpCatalogStamp(listed.tools[0]?._meta); // { digest, tools }
@@ -792,7 +799,7 @@ If you already run an `McpServer` from the SDK, `mountMcp` adds contract tools
792
799
  to it instead of owning the lifecycle:
793
800
 
794
801
  ```ts
795
- import { mountMcp } from 'stitchkit/tools'
802
+ import { mountMcp } from 'stitchkit/tools/mcp'
796
803
 
797
804
  mountMcp(mcpServer, [usersService], { context: { source: 'mcp' } })
798
805
  ```
@@ -808,7 +815,7 @@ client (Claude Desktop, Claude Code, Cursor, Codex), on the user's machine, so
808
815
  it can reach the local filesystem.
809
816
 
810
817
  ```ts
811
- import { bindStdioProcessSignals, createStdioMcpServer } from 'stitchkit/tools'
818
+ import { bindStdioProcessSignals, createStdioMcpServer } from 'stitchkit/tools/mcp'
812
819
 
813
820
  const stdio = await createStdioMcpServer({
814
821
  serverInfo: { name: 'my-app', version: '1.0.0' },
@@ -953,51 +960,3 @@ successes cannot reset a failing client's backoff.
953
960
  > Dynamic Client Registration is an explicit interoperability mode, not the
954
961
  > default. Enabling it changes public discovery and adds `/register`; keep it off
955
962
  > unless a client that cannot publish CIMD requires it.
956
-
957
- ## Proxying a remote API — `implementRemote`
958
-
959
- `implement` binds a contract to local handlers. `implementRemote` binds it to a
960
- remote HTTP API instead — every handler forwards the call to a deployed server
961
- through the contract's typed client:
962
-
963
- ```ts
964
- import { createHttpClient } from 'stitchkit'
965
- import { implementRemote } from 'stitchkit/remote'
966
- import { createStdioMcpServer } from 'stitchkit/tools'
967
-
968
- const http = createHttpClient({
969
- baseUrl: 'https://api.example.com',
970
- headers: () => ({ Authorization: `Bearer ${apiKey}` }),
971
- })
972
-
973
- const stdio = await createStdioMcpServer({
974
- serverInfo: { name: 'my-app', version: '1.0.0' },
975
- auth: null,
976
- services: contracts.map((c) => implementRemote(c, http)),
977
- })
978
-
979
- // During graceful shutdown:
980
- await stdio.close()
981
- ```
982
-
983
- This is how you ship a thin **local** MCP server for an API that already runs in
984
- the cloud: the local process owns only the transport and any filesystem-facing
985
- native tools, while every contract tool proxies to the remote API. One contract,
986
- no duplicated business logic.
987
-
988
- `implementRemote(contract, http, { transformArgs })` takes an optional
989
- `transformArgs` hook that rewrites a call's arguments before they are forwarded
990
- — e.g. to upload a local file referenced in the args and swap in its URL.
991
-
992
- ## Structured output
993
-
994
- When a contract endpoint or runtime tool declares `output`, its MCP tool
995
- registers that exact schema as `outputSchema` and returns the validated value as
996
- `structuredContent` alongside a JSON text block. Every JSON root type is kept
997
- unchanged: object, array, string, number, boolean and `null`. Stitchkit does not
998
- wrap non-object values in `{ result }`; protocol-era adaptation belongs to the
999
- official SDK.
1000
-
1001
- A tool without an output contract advertises no `outputSchema` and returns no
1002
- invented structured value. Its successful MCP result has an empty `content`
1003
- list.
@@ -0,0 +1,444 @@
1
+ # Upgrading 0.37.0 → 0.44.0 — stitchkit docs slice
2
+
3
+ > Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
4
+ > `llms.txt` in this package.
5
+
6
+ Contains: Guide: Upgrading, 0.37.0 → 0.44.0 (docs/guide/upgrading.md)
7
+
8
+ ==============================================================================
9
+ # Guide: Upgrading, 0.37.0 → 0.44.0 (docs/guide/upgrading.md)
10
+ ==============================================================================
11
+
12
+ ## Historical breaking migrations through 0.44.0
13
+
14
+ HTTP observability now completes inside the framework handler instead of a
15
+ nested fetch wrapper. Configure request and tool sinks explicitly:
16
+
17
+ ```ts
18
+ // before
19
+ const audit = createAuditHook({ write })
20
+ createServer({
21
+ services,
22
+ wrapFetch: (handler) => wrapInRequestContext(audit.http(handler)),
23
+ })
24
+ mountAgent(services, { hooks: audit.toolCall })
25
+
26
+ // after
27
+ const observability = createObservability({
28
+ request: { write, includePayload: true },
29
+ tools: { write },
30
+ })
31
+ createServer({ services, observability: observability.request })
32
+ mountAgent(services, { hooks: observability.toolCall })
33
+ ```
34
+
35
+ Body capture changed from always-on for body methods to opt-in. Set
36
+ `includePayload: true` only when the request sink needs the sanitized JSON body.
37
+ There is no `createAuditHook` or `audit.http` compatibility path;
38
+ `wrapInRequestContext` remains only for custom fetch pipelines.
39
+
40
+ Tool introspection now accepts one object-shaped contract/runtime surface. Stop
41
+ calling the internal contract collector or merging a locally converted runtime
42
+ manifest:
43
+
44
+ ```ts
45
+ // before
46
+ buildToolManifest(services.flatMap((service) => collectTools(service, 'AGENT')))
47
+ listToolNames(services)
48
+ summarizeTransports(services)
49
+
50
+ // after
51
+ const surface = { services, runtimeTools }
52
+ buildToolManifest({ ...surface, transport: 'AGENT' })
53
+ listToolNames(surface)
54
+ summarizeTransports(surface)
55
+ ```
56
+
57
+ `ToolNameEntry` adds `kind: 'contract' | 'runtime'`. `TransportSummary` is now
58
+ `{ contractServices, runtimeTools, totals, sources }`; replace `services` and
59
+ `perService` reads with the explicit counts and mixed-source breakdown. There
60
+ is no positional overload and no `buildRuntimeToolManifest`: Stitchkit owns the
61
+ combined order, transport filtering, canonical presentation schema and
62
+ cross-origin collision checks.
63
+
64
+ `defineErrors` now uses one Zod-first definition object and returns constructors
65
+ instead of positional throwers. Add explicit `throw`, move message/details/hint
66
+ into one options object, and declare a details schema when that code carries
67
+ structured context:
68
+
69
+ ```ts
70
+ // before
71
+ const { errors } = defineErrors({ QUOTA_EXCEEDED: 429 })
72
+ errors.QUOTA_EXCEEDED('Try later', { retryAfterSeconds: 30 }, 'Wait')
73
+
74
+ // after
75
+ const { errors, definitions } = defineErrors({
76
+ QUOTA_EXCEEDED: {
77
+ status: 429,
78
+ details: z.object({ retryAfterSeconds: z.number().positive() }),
79
+ },
80
+ })
81
+ throw errors.QUOTA_EXCEEDED({
82
+ message: 'Try later',
83
+ details: { retryAfterSeconds: 30 },
84
+ hint: 'Wait',
85
+ })
86
+ ```
87
+
88
+ There is no positional overload. A code without `details` forbids them; use an
89
+ optional object schema when the details object itself is optional. Read status
90
+ and schemas from the frozen `definitions` registry instead of maintaining a
91
+ parallel map.
92
+
93
+ Managed MCP runtime tools are now declared as immutable data. Move protected
94
+ registrar calls to `runtimeTools`; rename deliberate raw SDK registration to
95
+ `rawTools`. There is no registrar alias:
96
+
97
+ ```ts
98
+ // before — protected
99
+ createMcpHandler({
100
+ services,
101
+ nativeTools: ({ registerTool }) => registerTool(preview),
102
+ })
103
+
104
+ // after — protected and prepared with the rest of the surface
105
+ createMcpHandler({ services, runtimeTools: [preview] })
106
+
107
+ // before — deliberate raw SDK opt-out
108
+ nativeTools: ({ rawServer }, auth) => mountRaw(rawServer, auth)
109
+
110
+ // after — still a deliberate raw SDK opt-out
111
+ rawTools: (server, auth) => mountRaw(server, auth)
112
+ ```
113
+
114
+ When identity selects from a bounded set, replace a repeatedly prepared
115
+ `services(auth)` factory with a finite registry:
116
+
117
+ ```ts
118
+ createMcpHandler({
119
+ surfaces: {
120
+ admin: { services: allServices, runtimeTools: [preview] },
121
+ member: { services: memberServices, runtimeTools: [preview] },
122
+ },
123
+ selectSurface: (auth) => auth.isAdmin ? 'admin' : 'member',
124
+ })
125
+ ```
126
+
127
+ Keep direct `services(auth)` / `runtimeTools(auth)` only for genuinely
128
+ unbounded definitions; Stitchkit intentionally does not cache arbitrary auth
129
+ values.
130
+
131
+ Contract success bodies are now determined by the presence of `output`, not by
132
+ the handler's runtime value. A nullable output returns JSON `null` with status
133
+ `200`; `undefined` with a declared output and non-null data without an output
134
+ schema are contract violations:
135
+
136
+ ```ts
137
+ // nullable JSON data: 200 with body `null`
138
+ session: {
139
+ method: 'GET', path: '/session', desc: 'Current session',
140
+ output: SessionSchema.nullable(),
141
+ }
142
+
143
+ // bodyless operation: 204 with no body
144
+ logout: {
145
+ method: 'POST', path: '/logout', desc: 'End the session',
146
+ }
147
+ ```
148
+
149
+ Add an output schema to every handler that returns data. Omit `output` and
150
+ return nothing for bodyless operations; runtime tools follow the same rule and
151
+ type no-output handlers as `void`.
152
+
153
+ `createToolInvoker` now separates immutable registry preparation from per-call
154
+ runtime state. Move source/context/lifecycle/hooks/output-strip reporting from
155
+ the factory config to the third invocation argument. Use `invokeOrThrow` when a
156
+ nested operation should preserve the normalized `AppError` instead of returning
157
+ a model-facing failure envelope:
158
+
159
+ ```ts
160
+ // before
161
+ const invoker = createToolInvoker(services, {
162
+ transport: 'AGENT', context: { identity }, lifecycle, hooks,
163
+ })
164
+ const result = await invoker.invoke(name, args)
165
+
166
+ // after
167
+ const invoker = createToolInvoker(services, { transport: 'AGENT' })
168
+ const result = await invoker.invoke(name, args, {
169
+ context: { identity }, lifecycle, hooks,
170
+ })
171
+ const data = await invoker.invokeOrThrow(name, args, {
172
+ context: { identity }, lifecycle, hooks,
173
+ })
174
+ ```
175
+
176
+ There is no static runtime-config overload: request identity must not be retained
177
+ by a reusable compiled registry.
178
+
179
+ Entity cache handlers now require the cached list shape and CRUD policies. Move
180
+ `listKey` under `list`, make detail keys event-aware, and state the list-item
181
+ identity/projection explicitly:
182
+
183
+ ```ts
184
+ // before
185
+ createEntityCacheHandlers<Entity>({
186
+ getId,
187
+ listKey: ['entities'],
188
+ detailKey: (id) => ['entities', id],
189
+ })
190
+
191
+ // after
192
+ createEntityCacheHandlers<Entity, EntityListItem>({
193
+ getId,
194
+ getListItemId: (item) => item.id,
195
+ toListItem: (entity) => ({ id: entity.id, name: entity.name }),
196
+ list: {
197
+ key: ['entities'],
198
+ shape: 'paginated',
199
+ createAt: 'start',
200
+ updateMissing: 'skip',
201
+ },
202
+ detailKey: (event) => ['entities', event.id],
203
+ })
204
+ ```
205
+
206
+ Choose `array`, `paginated`, `infinite-array` or `infinite-paginated` to match
207
+ the actual cached data. A dynamic `list.key` / `detailKey` receives a
208
+ discriminated event and can derive scoped keys from the created/updated entity
209
+ or deleted payload. Add `compare` only when the backend has a canonical order;
210
+ the framework does not guess it or mutate pagination metadata.
211
+
212
+ Protected native MCP operations now use the transport-neutral runtime tool
213
+ definition. Return the schema-owned value from the handler and move MCP content
214
+ or metadata into `present.mcp`; `structuredContent` and `isError` are
215
+ framework-owned:
216
+
217
+ ```ts
218
+ // before
219
+ registerTool({ input, output, handler: async () => ({
220
+ content: [{ type: 'image', data, mimeType: 'image/png' }],
221
+ structuredContent: { assetId },
222
+ }) })
223
+
224
+ // after
225
+ const preview = defineRuntimeTool({
226
+ name: 'render_preview', description, identity, input, output,
227
+ handler: async () => ({ assetId, data }),
228
+ present: {
229
+ mcp: (result) => ({
230
+ content: [{ type: 'image', data: result.data, mimeType: 'image/png' }],
231
+ }),
232
+ },
233
+ })
234
+ runtimeTools: [preview]
235
+ ```
236
+
237
+ The removed `NativeMcp*` types have no aliases. Use `RuntimeToolDefinition`,
238
+ `RuntimeToolIdentity`, `RuntimeToolHandlerContext` and
239
+ `RuntimeMcpPresentation`. The same definition can now be passed to
240
+ `mountAgent(services, { runtimeTools: [preview] })`; add `present.agent` only
241
+ when the model needs rich text/file content instead of the neutral JSON result.
242
+
243
+ Trailing wildcards must be named consistently across the path and params schema:
244
+
245
+ ```ts
246
+ // before
247
+ path: '/app/:slug/*'
248
+ params: z.object({ slug: z.string(), '*': z.string() })
249
+ ctx.params['*']
250
+ api.app({ slug: 'foo', '*': 'a/b' })
251
+
252
+ // after
253
+ path: '/app/:slug/*filePath'
254
+ params: z.object({ slug: z.string(), filePath: z.string() })
255
+ ctx.params.filePath
256
+ api.app({ slug: 'foo', filePath: 'a/b' })
257
+ ```
258
+
259
+ Bare wildcards have no compatibility alias; raw routes use the same named form.
260
+
261
+ ### Expected-401 matchers
262
+
263
+ `HttpClientConfig.authEndpoints` is removed. Replace manual path prefixes with
264
+ the operations whose 401 response is expected:
265
+
266
+ ```ts
267
+ // before
268
+ createHttpClient({ baseUrl, authEndpoints: ['/api/auth/'] })
269
+
270
+ // after
271
+ createHttpClient({
272
+ baseUrl,
273
+ suppressUnauthorizedFor: contractEndpointMatchers(authContract, ['login', 'verify']),
274
+ })
275
+ ```
276
+
277
+ There is no implicit `/auth/` suppression. Omit `suppressUnauthorizedFor` when
278
+ every 401 should emit the global `unauthorized` event.
279
+
280
+ ## The 0.37 migration
281
+
282
+ Tool presentation is no longer an executable Zod parser. Replace the removed
283
+ flatten helpers with the JSON Schema compiler, and choose the explicit
284
+ `MountableTool` surface when using the advanced collection API:
285
+
286
+ ```ts
287
+ // before
288
+ const flat = flattenUnionsDeep(zodSchema)
289
+ mountable.schema
290
+
291
+ // after
292
+ const flat = flattenToolJsonSchema(
293
+ z.toJSONSchema(zodSchema, { target: 'draft-07', io: 'input' }),
294
+ )
295
+ mountable.presentationSchema // model/MCP/manifest JSON Schema
296
+ mountable.argumentSchema // executable CLI argument adapter only
297
+ ```
298
+
299
+ Contract and native handlers keep their original Zod schemas; MCP and agent SDK
300
+ adapters now forward raw arguments so defaults, coercions, refinements and
301
+ transforms execute exactly once inside Stitchkit. There are no compatibility
302
+ exports for `flattenDiscriminatedUnion`, `flattenUnionsDeep` or
303
+ `MountableTool.schema`. → ADR 0050
304
+
305
+ Tool-call hooks now take one options object. Migrate all three callbacks; there
306
+ are no positional overloads:
307
+
308
+ ```ts
309
+ // before
310
+ beforeToolCall: (toolName, args, context, endpoint) => {}
311
+ afterToolCall: (toolName, args, result, durationMs, context, endpoint, error) => {}
312
+ onToolError: (toolName, error, context, endpoint) => {}
313
+
314
+ // after
315
+ beforeToolCall: ({ toolName, args, context, endpoint }) => {}
316
+ afterToolCall: ({ toolName, args, result, durationMs, context, endpoint, error }) => {}
317
+ onToolError: ({ toolName, error, context, endpoint }) => {}
318
+ ```
319
+
320
+ ### MCP schema validation
321
+
322
+ The standalone validator now takes one object and live MCP configs carry the
323
+ same rules under `schemaValidation`. Migrate every positional call and every
324
+ `onIncompatibleSchema` field; there is no old-shape overload:
325
+
326
+ ```ts
327
+ // before
328
+ validateMcpSchemas(services, 'throw', logger, { requireTypedProperties: true })
329
+ createMcpHandler({ services, onIncompatibleSchema: 'throw' })
330
+
331
+ // after
332
+ validateMcpSchemas({ services, policy: 'throw', logger, requireTypedProperties: true })
333
+ createMcpHandler({ services, schemaValidation: { policy: 'throw' } })
334
+ ```
335
+
336
+ Put `extend` and `flattenUnionInput` beside `services`, not inside
337
+ `schemaValidation`. The handler applies the profile to the exact prepared schema
338
+ it advertises. Add `requirePortableFormats: true` when every custom JSON Schema
339
+ format must be rejected before a client sees it.
340
+
341
+ Native MCP registration also changed shape in 0.37. Move protected tools to the
342
+ framework registrar; keep an SDK-raw tool only by naming the opt-out:
343
+
344
+ ```ts
345
+ // before
346
+ nativeTools: (server, auth) => server.registerTool(name, config, handler)
347
+
348
+ // after — lifecycle, hooks and schema policy apply
349
+ nativeTools: ({ registerTool }, auth) => registerTool({
350
+ name, description, identity, input, output, handler,
351
+ })
352
+
353
+ // after — intentionally raw
354
+ nativeTools: ({ rawServer }, auth) => rawServer.registerTool(name, config, handler)
355
+ ```
356
+
357
+ If a tool hook annotated `endpoint` as `MethodDef`, remove that annotation or
358
+ use `OperationIdentity`: native operations have service/action/scope/method but
359
+ no HTTP path.
360
+
361
+ ### Node-facing server types
362
+
363
+ `stitchkit/server` remains Bun-concrete: an explicitly annotated `RawRoute`
364
+ still receives `BunServer`. `stitchkit/node` no longer drags Bun declarations
365
+ into a Node project. Its raw routes default the host server to `unknown`, and
366
+ its Socket.IO handle exposes only Node capabilities:
367
+
368
+ ```ts
369
+ // before — Node entry still leaked Bun-only fields and ambient types
370
+ const route: RawRoute = { handler: (_req, ctx) => ctx.server?.upgrade(...) }
371
+ const socket = await createSocketIOServer(config)
372
+ socket.websocket
373
+
374
+ // after — name a custom embedding host only when one exists
375
+ const route: RawRoute<MyHostServer> = {
376
+ handler: (_req, ctx) => useHost(ctx.server),
377
+ }
378
+ const socket = await createSocketIOServer(config)
379
+ socket.io
380
+ socket.attach(nodeHttpServer)
381
+ ```
382
+
383
+ Node consumers can remove `@types/bun` unless another dependency independently
384
+ requires it.
385
+
386
+ ### Managed server shutdown
387
+
388
+ `createServer()` and `serveNode()` now return the same structural managed
389
+ lifecycle. Replace every direct runtime stop and parallel Socket.IO close:
390
+
391
+ ```ts
392
+ // before — split ownership
393
+ const socket = await createSocketIOServer(config)
394
+ const server = createServer({
395
+ services,
396
+ websocket: socket.websocket,
397
+ rawRoutes: [socket.route],
398
+ })
399
+ server.stop()
400
+ await socket.io.close()
401
+
402
+ // after — one owner and one total deadline
403
+ const socket = await createSocketIOServer(config)
404
+ const server = createServer({ services, socket })
405
+ const result = await server.shutdown({ gracePeriodMs: 30_000 })
406
+ ```
407
+
408
+ On Node, keep the same `socket` field and replace `handle.close()` with
409
+ `handle.shutdown()`. Runtime-specific diagnostics move under `handle.runtime`;
410
+ do not use it as a second shutdown path. Standalone CLI/tools that create a
411
+ Socket.IO handle without an HTTP server call `await socket.close()`.
412
+
413
+ If Bun Socket.IO shares the port with a raw lane, keep the explicit composition
414
+ but let the server mount the Socket.IO route:
415
+
416
+ ```ts
417
+ createServer({
418
+ services,
419
+ socket,
420
+ websocket: composeWebSocketHandlers([
421
+ webSocketLane({ match: isRaw, handlers: rawHandlers }),
422
+ socketIoLane(socket.websocket),
423
+ ]),
424
+ rawRoutes: [rawUpgradeRoute],
425
+ })
426
+ ```
427
+
428
+ Move native Bun `routes` entries to `rawRoutes`. Native routes run before the
429
+ Fetch handler and therefore cannot participate in admission or drain. Wire
430
+ `SIGTERM`/`SIGINT` in the application; the first signal starts `shutdown()`, and
431
+ a later signal may abort the same controller. Close MCP, databases and queues
432
+ after the server result—those resources remain application-owned.
433
+
434
+ Move a handshake policy from the Node-only callback shape inside
435
+ `serverOptions` to the runtime-neutral top-level policy. It receives a Web
436
+ `Request`, may be async, and returns whether to admit the handshake:
437
+
438
+ ```ts
439
+ // before
440
+ serverOptions: { allowRequest: (request, done) => done(null, allowed(request)) }
441
+
442
+ // after
443
+ allowRequest: (request) => allowed(request)
444
+ ```