stitchkit 0.43.0 → 0.44.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 (68) hide show
  1. package/README.md +12 -4
  2. package/dist/browser/client.d.ts.map +1 -1
  3. package/dist/browser/http.d.ts +2 -1
  4. package/dist/browser/http.d.ts.map +1 -1
  5. package/dist/browser/request-id.d.ts +3 -0
  6. package/dist/browser/request-id.d.ts.map +1 -0
  7. package/dist/cli.js +2 -2
  8. package/dist/contract/define.d.ts +18 -0
  9. package/dist/contract/define.d.ts.map +1 -1
  10. package/dist/contract/index.d.ts +1 -1
  11. package/dist/contract/index.d.ts.map +1 -1
  12. package/dist/{index-310bfer5.js → index-36f1xkdb.js} +1 -1
  13. package/dist/{index-esqmem78.js → index-4fyn9f09.js} +2 -1
  14. package/dist/{index-h2wdcsby.js → index-mvjyq9h9.js} +176 -12
  15. package/dist/{index-gex6gxhe.js → index-yh0p5ddn.js} +34 -2
  16. package/dist/index.js +19 -7
  17. package/dist/internal/secure-fetch.d.ts +21 -1
  18. package/dist/internal/secure-fetch.d.ts.map +1 -1
  19. package/dist/node.js +2 -2
  20. package/dist/observability/audit.d.ts.map +1 -1
  21. package/dist/observability/event.d.ts +16 -0
  22. package/dist/observability/event.d.ts.map +1 -1
  23. package/dist/observability/index.d.ts +1 -1
  24. package/dist/observability/index.d.ts.map +1 -1
  25. package/dist/observability/index.js +30 -1
  26. package/dist/observability/trace.d.ts +10 -0
  27. package/dist/observability/trace.d.ts.map +1 -1
  28. package/dist/server/implement.d.ts.map +1 -1
  29. package/dist/server/index.js +3 -3
  30. package/dist/server/types.d.ts +14 -2
  31. package/dist/server/types.d.ts.map +1 -1
  32. package/dist/tools/execute.d.ts +1 -1
  33. package/dist/tools/execute.d.ts.map +1 -1
  34. package/dist/tools/mcp-app.d.ts +3 -0
  35. package/dist/tools/mcp-app.d.ts.map +1 -1
  36. package/dist/tools/mcp-handler.d.ts +34 -23
  37. package/dist/tools/mcp-handler.d.ts.map +1 -1
  38. package/dist/tools/mcp-round.d.ts +43 -0
  39. package/dist/tools/mcp-round.d.ts.map +1 -0
  40. package/dist/tools/mcp-stdio.d.ts +11 -18
  41. package/dist/tools/mcp-stdio.d.ts.map +1 -1
  42. package/dist/tools/mcp-trace.d.ts +4 -0
  43. package/dist/tools/mcp-trace.d.ts.map +1 -0
  44. package/dist/tools/mcp.d.ts +24 -3
  45. package/dist/tools/mcp.d.ts.map +1 -1
  46. package/dist/tools/mount-download.d.ts +2 -2
  47. package/dist/tools/mount-download.d.ts.map +1 -1
  48. package/dist/tools/mount-upload.d.ts +1 -1
  49. package/dist/tools/mount-upload.d.ts.map +1 -1
  50. package/dist/tools/mount-wait.d.ts +3 -3
  51. package/dist/tools/mount-wait.d.ts.map +1 -1
  52. package/dist/tools/mount.d.ts +1 -1
  53. package/dist/tools/mount.d.ts.map +1 -1
  54. package/dist/tools/native-mcp.d.ts +3 -2
  55. package/dist/tools/native-mcp.d.ts.map +1 -1
  56. package/dist/tools/oauth-provider.d.ts +77 -7
  57. package/dist/tools/oauth-provider.d.ts.map +1 -1
  58. package/dist/tools/runtime-tool.d.ts +27 -20
  59. package/dist/tools/runtime-tool.d.ts.map +1 -1
  60. package/dist/tools/toolkit.d.ts +5 -5
  61. package/dist/tools/toolkit.d.ts.map +1 -1
  62. package/dist/tools/view-file.d.ts +1 -1
  63. package/dist/tools/view-file.d.ts.map +1 -1
  64. package/dist/tools.d.ts +5 -5
  65. package/dist/tools.d.ts.map +1 -1
  66. package/dist/tools.js +829 -200
  67. package/llms-full.txt +424 -82
  68. package/package.json +5 -4
package/llms-full.txt CHANGED
@@ -27,7 +27,7 @@ bun add stitchkit zod
27
27
 
28
28
  `zod` is a required peer — schemas are the source of truth everywhere. Other
29
29
  peers are optional and pulled in only when you use the matching feature
30
- (`@modelcontextprotocol/sdk` for MCP, `ai` for agents, `socket.io*` for
30
+ (`@modelcontextprotocol/server` for MCP servers, `ai` for agents, `socket.io*` for
31
31
  realtime, `@tanstack/react-query` + `react-query-kit` for React). See
32
32
  [deps](#dependencies) below.
33
33
 
@@ -141,9 +141,9 @@ map — feature → packages:
141
141
  | anything (validation) | `zod` |
142
142
  | `createServer` (Bun) | — (uses `Bun.serve`) |
143
143
  | `serveNode` (Node ≥ 22) | `srvx` (+ `@types/bun` dev) |
144
- | MCP tools (`stitchkit/tools`) | `@modelcontextprotocol/sdk` |
144
+ | MCP / agent tools (`stitchkit/tools`) | `@modelcontextprotocol/server` `ai` |
145
+ | MCP host/client tests | `@modelcontextprotocol/client` |
145
146
  | MCP Apps UI widgets | `@modelcontextprotocol/ext-apps` |
146
- | agent tools (`stitchkit/tools`) | `ai` |
147
147
  | React data layer (`stitchkit/react`) | `@tanstack/react-query` `react-query-kit` |
148
148
  | **Socket.IO server on Bun** | `socket.io` `@socket.io/bun-engine` |
149
149
  | **Socket.IO server on Node** | `socket.io` |
@@ -153,9 +153,22 @@ map — feature → packages:
153
153
  bun add socket.io @socket.io/bun-engine # e.g. the Socket.IO server on Bun
154
154
  ```
155
155
 
156
- If an optional peer is missing, the feature that needs it fails with an
157
- actionable error naming the package and the install command — not a bare
158
- `Cannot find module`.
156
+ Dynamic optional-peer adapters fail with an actionable error naming the package
157
+ and install command. Static entrypoints such as `stitchkit/tools` fail during
158
+ ESM resolution by naming the exact missing package; the feature-to-peer matrix
159
+ below is the canonical install command.
160
+
161
+ The combined tools entry owns MCP and AI-agent adapters, so install both runtime
162
+ peers before importing `stitchkit/tools`:
163
+
164
+ ```bash
165
+ bun add @modelcontextprotocol/server@^2 ai@^7
166
+ ```
167
+
168
+ Browser, HTTP-client and React entrypoints remain usable without either MCP
169
+ package. MCP hosts and client E2E additionally install
170
+ `@modelcontextprotocol/client@^2`; Apps additionally install
171
+ `@modelcontextprotocol/ext-apps`.
159
172
 
160
173
  ## Next
161
174
 
@@ -1546,10 +1559,16 @@ try {
1546
1559
  err.message // 'Note not found'
1547
1560
  err.details // structured details, if any
1548
1561
  err.hint // optional hint
1562
+ err.traceId // x-request-id — correlate this failure with backend logs
1549
1563
  }
1550
1564
  }
1551
1565
  ```
1552
1566
 
1567
+ `traceId` is present when the server returned `x-request-id`. A network error,
1568
+ timeout or abort without an HTTP response cannot carry one. Cross-origin
1569
+ browser code can read it through Stitchkit's default CORS expose list; a custom
1570
+ `cors.exposeHeaders` policy must keep `x-request-id` exposed.
1571
+
1553
1572
  The error model is shared with the server — see [Auth & errors](./auth-and-errors.md).
1554
1573
 
1555
1574
  ## Auth events
@@ -1798,27 +1817,28 @@ the invoker is created.
1798
1817
 
1799
1818
  ## MCP — `createMcpHandler`
1800
1819
 
1801
- `createMcpHandler` builds a complete Streamable-HTTP MCP server as a single
1802
- `Request Response` handler. It owns the SDK server and transport lifecycle, so
1803
- your app never imports `@modelcontextprotocol/sdk` itself.
1820
+ `createMcpHandler` builds a complete stateless Streamable-HTTP MCP endpoint on
1821
+ the official TypeScript SDK v2 server package. It owns server creation,
1822
+ protocol-era negotiation and shutdown; application code does not import the SDK
1823
+ to mount a normal endpoint.
1804
1824
 
1805
1825
  ```ts
1806
- import { createMcpHandler } from 'stitchkit/tools'
1826
+ import { createMcpHandler, createMcpHttpRoute } from 'stitchkit/tools'
1827
+ import { createServer } from 'stitchkit/server'
1807
1828
 
1808
- const handleMcp = createMcpHandler({
1829
+ const mcp = createMcpHandler({
1809
1830
  serverInfo: { name: 'my-app', version: '1.0.0' },
1810
1831
  auth: (req) => resolveApiKey(req), // → an identity, or null for 401
1811
1832
  services: [usersService, catalogService],
1812
1833
  })
1813
- ```
1814
-
1815
- Mount the returned handler on a raw route — typically `/mcp`:
1816
1834
 
1817
- ```ts
1818
1835
  createServer({
1819
1836
  services,
1820
- rawRoutes: [{ method: 'ALL', path: '/mcp', handler: (req) => handleMcp(req) }],
1837
+ rawRoutes: [createMcpHttpRoute({ path: '/mcp', handler: mcp })],
1821
1838
  })
1839
+
1840
+ // During graceful shutdown:
1841
+ await mcp.close()
1822
1842
  ```
1823
1843
 
1824
1844
  ### `McpHandlerConfig`
@@ -1833,6 +1853,7 @@ createServer({
1833
1853
  | `selectSurface` | `(auth) => declaredKey` — required with `surfaces` |
1834
1854
  | `context` | `(auth) => {…}` — values merged into every tool handler's `ctx` |
1835
1855
  | `lifecycle` | `beforeHandle` / `afterHandle` — the tool-side auth gate (see below) |
1856
+ | `multiRound` | Typed MRTR state policy for guarded multi-round tool calls |
1836
1857
  | `hooks` | tool-call observability hooks — `afterToolCall` fires on every result |
1837
1858
  | `extend` | extra advertised arguments resolved into handler context |
1838
1859
  | `schemaValidation` | compatibility policy, typed-property guard and portable-format guard |
@@ -1845,7 +1866,10 @@ createServer({
1845
1866
  | `errorHint` | add a project-owned hint to failed tool results |
1846
1867
  | `onOutputStrip` | observe output keys removed by contract validation |
1847
1868
  | `protectedResource` | RFC 9728 metadata used by HTTP `401` responses |
1848
- | `sessionMode` | `'stateless'` (default) or explicit `'stateful'` session/SSE continuity |
1869
+ | `legacy` | `'serve'` (default) or `'reject'` for pre-2026 clients |
1870
+ | `security` | optional Host/Origin allowlists at the Fetch boundary |
1871
+ | `onTransportRejected` | observe HTTP/protocol rejections without entering lifecycle or tool hooks |
1872
+ | `cache` | explicit operation cache hints; omitted means zero/private |
1849
1873
 
1850
1874
  Direct `services` / `runtimeTools` factories, `context`, `selectSurface` and
1851
1875
  `rawTools` receive the resolved identity, so a tenant can be shown only its own
@@ -1870,20 +1894,106 @@ const handleMcp = createMcpHandler({
1870
1894
  Every declared entry is schema-validated and prepared once when the handler is
1871
1895
  constructed. The selected immutable descriptors are shared; the SDK server,
1872
1896
  transport, auth-derived context, lifecycle runner and tool-call context are
1873
- fresh for every stateless request (or every stateful session). Unknown keys
1897
+ fresh for every request. Unknown keys
1874
1898
  fail before the server connects. The registry never retains auth values.
1875
1899
 
1876
- ### Stateless by default; stateful only when required
1877
-
1878
- The default `sessionMode: 'stateless'` creates a fresh SDK server, transport,
1879
- resolved auth/context and runner for each HTTP request. Static direct and finite
1880
- registry schemas are still prepared once when the handler is constructed. There is no session
1881
- map, event store, sweep timer or `Mcp-Session-Id`, so process replacement and
1882
- load balancing cannot strand a client on an in-memory session.
1900
+ ### Stateless transport and protocol eras
1901
+
1902
+ Every request gets a fresh SDK server, resolved auth/context and tool-call
1903
+ runner. Static direct and finite registry schemas are still prepared once when
1904
+ the handler is constructed. There is no session map, event store,
1905
+ `Mcp-Session-Id` or resumable cross-request SSE state. Process replacement and
1906
+ load balancing therefore cannot strand a client on an in-memory session.
1907
+
1908
+ The default `legacy: 'serve'` lets the official SDK negotiate either modern
1909
+ `2026-07-28` or the supported legacy stateless opening on this same endpoint.
1910
+ Set `legacy: 'reject'` when every host is modern. Legacy support is protocol
1911
+ translation inside the SDK, not a second Stitchkit transport or compatibility
1912
+ API. Stateful MCP and subscriptions are intentionally outside this layer.
1913
+
1914
+ `createMcpHttpRoute` is the framework-owned raw-route adapter. It registers
1915
+ `ALL` because Streamable HTTP uses multiple HTTP methods, delegates only to
1916
+ `handler.fetch`, and leaves lifecycle ownership with the returned handler.
1917
+
1918
+ By default, a present `Host` header must match the request URL and a present
1919
+ browser `Origin` must be same-origin. Non-browser hosts may omit `Origin`. Behind
1920
+ a trusted reverse proxy, reconstruct the public request URL before calling the
1921
+ handler or configure exact `security.allowedHosts` / `allowedOrigins`; never
1922
+ copy arbitrary forwarded headers into the trusted URL. If a browser calls MCP
1923
+ cross-origin, its HTTP server or gateway must also answer CORS preflight and
1924
+ expose the protocol headers. `onTransportRejected` is the audit boundary for
1925
+ HTTP/security rejections; it is deliberately separate from tool lifecycle and
1926
+ must not be used as authorization input.
1927
+
1928
+ ### Modern discovery, routing and cache hints
1929
+
1930
+ Stitchkit forwards the negotiated protocol era and validated MCP request data
1931
+ to `RequestEvent.mcp`; untrusted routing headers never become application
1932
+ identity. The SDK validates `MCP-Protocol-Version`, JSON-RPC shape and routing
1933
+ metadata before lifecycle or tool handlers run. Framework-owned tool and
1934
+ resource registration preserves declaration order across HTTP and stdio. Tool
1935
+ manifests preserve mount order; `listToolNames` is a sorted diagnostics view.
1936
+ Consumer-owned raw SDK registrations are outside this guarantee.
1937
+
1938
+ Caching is opt-in. `cache.operations` maps directly to SDK v2 operation cache
1939
+ hints, while each `McpResourceDef` may declare its own `cacheHint`. Omission
1940
+ means zero/private: neither a prepared surface nor a deterministic response is
1941
+ silently treated as public. Unbounded auth-selected surface factories are
1942
+ always forced to zero/private even when an operation policy is present; use a
1943
+ finite `surfaces` registry when identities share a provably immutable surface.
1944
+ Stitchkit never advertises list-change or subscription capabilities
1945
+ without an implementation.
1946
+
1947
+ ### Multi-round tool input (`input_required`)
1948
+
1949
+ An MCP-only operation can require typed user input before its final side effect:
1950
+
1951
+ ```ts
1952
+ const DeleteConfirmationSchema = z.object({ confirmed: z.boolean() })
1953
+
1954
+ deleteProject: {
1955
+ method: 'DELETE',
1956
+ path: '/projects/:id',
1957
+ expose: ['MCP'],
1958
+ input: DeleteProjectSchema,
1959
+ output: DeleteProjectResultSchema,
1960
+ mcp: {
1961
+ inputRequired: [
1962
+ {
1963
+ key: 'confirmation',
1964
+ schema: DeleteConfirmationSchema,
1965
+ message: 'Confirm permanent deletion',
1966
+ },
1967
+ {
1968
+ key: 'reason',
1969
+ schema: z.object({ value: z.string().min(3) }),
1970
+ message: 'Record the reason',
1971
+ },
1972
+ ],
1973
+ },
1974
+ }
1975
+ ```
1883
1976
 
1884
- Opt into `sessionMode: 'stateful'` only when the client needs server-initiated
1885
- messages, cross-request progress or resumable SSE. That mode issues a server
1886
- session id and retains the bounded session/event stores until idle expiry.
1977
+ Each continuation accepts exactly the currently requested item. Only after the
1978
+ ordered sequence completes does the handler receive the exact aggregate as
1979
+ `ctx.mcpInput.confirmation` and `ctx.mcpInput.reason`; before then the framework
1980
+ returns `input_required` without calling it. Set
1981
+ `multiRound.serving.maxRounds` to the maximum declaration size you accept
1982
+ (default `10`); empty sequences, duplicate keys and overflow fail first. State
1983
+ is bound to principal, full operation identity, current round, accepted
1984
+ aggregate and canonical original-argument digest and is
1985
+ expiry-checked. Tampered, expired, cross-user or cross-tool state fails before
1986
+ the side effect. Output validation applies only to the final result.
1987
+
1988
+ Every attempt re-runs auth/lifecycle and emits its own tool hooks; audit metadata
1989
+ contains the attempt's `mcp.outcome` and `mcp.round`. Stitchkit does not merge
1990
+ separate HTTP attempts into one logical trace. Parallel
1991
+ rounds have isolated context. HTTP/stdio modern hosts and the supported legacy
1992
+ stdio bridge may use this declaration; Agent, CLI and ordinary HTTP invocation
1993
+ remain single-round and unchanged. Hosts without the capability receive an
1994
+ ordinary unsupported result rather than a falsely advertised round. Signed
1995
+ state is tamper-resistant, not an exactly-once replay store; destructive
1996
+ handlers must still be idempotent where retries matter.
1887
1997
 
1888
1998
  ### Guarding tools — `lifecycle`
1889
1999
 
@@ -1942,9 +2052,9 @@ cannot become a tool. `schemaValidation.policy` decides what happens:
1942
2052
  startup assertion or a test.
1943
2053
 
1944
2054
  For a static `services` array, collection, schema conversion and every enabled
1945
- validation guard run once when the handler is created. Each HTTP request or
1946
- stateful session still receives a fresh `McpServer`, runner, context and native
1947
- registration over that immutable prepared surface. A `services(auth)` factory
2055
+ validation guard run once when the handler is created. Each HTTP request still
2056
+ receives a fresh `McpServer`, runner, context and native registration over that
2057
+ immutable prepared surface. A `services(auth)` factory
1948
2058
  is deliberately prepared after resolving each identity because its tool set may
1949
2059
  change by tenant.
1950
2060
 
@@ -2051,11 +2161,14 @@ it can reach the local filesystem.
2051
2161
  ```ts
2052
2162
  import { createStdioMcpServer } from 'stitchkit/tools'
2053
2163
 
2054
- await createStdioMcpServer({
2164
+ const stdio = await createStdioMcpServer({
2055
2165
  serverInfo: { name: 'my-app', version: '1.0.0' },
2056
2166
  auth: resolveIdentity(), // resolved once at startup, not per request
2057
2167
  services: [usersService],
2058
2168
  })
2169
+
2170
+ // During graceful shutdown:
2171
+ await stdio.close()
2059
2172
  ```
2060
2173
 
2061
2174
  A stdio server is a single process serving one client, so `auth` is a value (or
@@ -2063,6 +2176,11 @@ a promise of one) resolved once at startup — typically from an env var — rat
2063
2176
  than a per-request `(req) => …`. Keep all logging on **stderr**: stdout is the
2064
2177
  JSON-RPC channel.
2065
2178
 
2179
+ The default `legacy: 'serve'` negotiates the modern or supported legacy opening
2180
+ through the official stdio adapter. Use `legacy: 'reject'` for a modern-only
2181
+ binary. Each invocation builds a fresh server; the returned handle owns
2182
+ transport shutdown.
2183
+
2066
2184
  Both transports build the server through the shared `buildMcpServer` — same
2067
2185
  contract/runtime pipeline, same surface selection, context, hooks, raw escape
2068
2186
  hatch and instructions.
@@ -2071,41 +2189,45 @@ hatch and instructions.
2071
2189
 
2072
2190
  A remote MCP server is connectable from Claude (Desktop / web "custom
2073
2191
  connector") only through the MCP authorization spec: OAuth 2.1 with PKCE, plus
2074
- the discovery documents (RFC 9728 / 8414), Dynamic Client Registration
2075
- (RFC 7591) and resource indicators (RFC 8707). A Bearer-only server returns a
2192
+ the discovery documents (RFC 9728 / 8414), Client ID Metadata Documents (CIMD)
2193
+ and resource indicators (RFC 8707). A Bearer-only server returns a
2076
2194
  bare `401` and the connector never establishes.
2077
2195
 
2078
2196
  stitchkit ships the OAuth **protocol mechanics**; the app supplies only
2079
2197
  **identity and storage**. Three pieces wire it together:
2080
2198
 
2081
2199
  ```ts
2082
- import { createMcpHandler, mountOAuthProvider, oauthProtectedResourceRoute } from 'stitchkit/tools'
2200
+ import { createMcpHandler, createMcpHttpRoute, mountOAuthProvider, oauthProtectedResourceRoute } from 'stitchkit/tools'
2083
2201
  import { createServer } from 'stitchkit/server'
2084
2202
 
2085
2203
  const resource = 'https://api.example.com/mcp'
2086
2204
  const issuer = 'https://api.example.com'
2087
2205
 
2088
2206
  // 1. Resource server — the 401 now points at the metadata.
2089
- const handleMcp = createMcpHandler({
2207
+ const mcp = createMcpHandler({
2090
2208
  serverInfo: { name: 'my-app', version: '1.0.0' },
2091
2209
  auth: resolveOAuthToken, // validate the Bearer JWT (verifyJwt + audience)
2092
2210
  services,
2093
2211
  protectedResource: { resource, authorizationServers: [issuer] },
2094
2212
  })
2095
2213
 
2096
- // 2. Authorization server — DCR, /authorize (PKCE), /token.
2214
+ // 2. Authorization server — CIMD, /authorize (PKCE), /token.
2097
2215
  const oauthRoutes = mountOAuthProvider({
2098
2216
  issuer,
2099
2217
  resource,
2100
2218
  signingSecret: env.OAUTH_SECRET,
2101
- clients, codes, refreshTokens, // your stores (DB or in-memory)
2102
- authorizeUser, // your login + consent → { userId } | Response
2219
+ clientRegistration: {
2220
+ preRegistered: { get: getFirstPartyClient },
2221
+ // CIMD is enabled with secure defaults; DCR remains disabled.
2222
+ },
2223
+ codes, refreshTokens, // your stores (DB or in-memory)
2224
+ authorizeUser, // → { userId, approvedScopes } | Response
2103
2225
  })
2104
2226
 
2105
2227
  createServer({
2106
2228
  services,
2107
2229
  rawRoutes: [
2108
- { method: 'ALL', path: '/mcp', handler: (req) => handleMcp(req) },
2230
+ createMcpHttpRoute({ path: '/mcp', handler: mcp }),
2109
2231
  oauthProtectedResourceRoute({ resource, authorizationServers: [issuer] }),
2110
2232
  ...oauthRoutes,
2111
2233
  ],
@@ -2116,11 +2238,27 @@ Access tokens are signed HS256 JWTs (`signJwt`) whose `aud` is the resource and
2116
2238
  whose `iss` is the issuer — validate both in `auth` with
2117
2239
  `verifyJwt(token, secret, { audience: resource, issuer })`. `authorizeUser` is
2118
2240
  where the app authenticates the user (reuse an existing session) and records
2119
- consent; return `{ userId }` to issue a code, or a `Response` to redirect the
2120
- browser to a login page first. The AS and resource server can co-locate or live
2241
+ consent; return `{ userId, approvedScopes }`, where approved scopes are a subset
2242
+ of the requested/supported values, or a `Response` to redirect the browser to a
2243
+ login page first. The AS and resource server can co-locate or live
2121
2244
  on separate origins. See
2122
2245
  [ADR 0015](../decisions/0015-oauth-resource-server.md).
2123
2246
 
2247
+ Client resolution is deterministic: exact pre-registered client, then an HTTPS
2248
+ URL client id resolved as CIMD, then explicitly enabled DCR. CIMD is on by
2249
+ default; DCR is off unless `clientRegistration.dcr` supplies both `register` and
2250
+ `get`. A CIMD-only deployment therefore exposes neither `/register` nor a
2251
+ `registration_endpoint`. The metadata document must declare a `client_id` that
2252
+ exactly equals its URL, explicit redirect URIs and
2253
+ `token_endpoint_auth_method: 'none'`.
2254
+
2255
+ The production fetcher rejects credentials, fragments, non-HTTPS URLs and
2256
+ private/reserved targets. Every redirect is DNS-resolved and IP-pinned again;
2257
+ timeouts, redirect count and body size are bounded. Its bounded cache respects
2258
+ `Cache-Control` (including `no-store`/`no-cache`), `Age`, `Expires`, `ETag` and
2259
+ `Last-Modified`, coalesces concurrent misses and reports sanitized cache events;
2260
+ invalid or unavailable identity receives only a short fail-closed cache entry.
2261
+
2124
2262
  ### Authorization hardening (MCP 2026-07-28)
2125
2263
 
2126
2264
  - **`iss` on every authorization response (RFC 9207, SEP-2468).** Success *and*
@@ -2138,9 +2276,9 @@ on separate origins. See
2138
2276
  behaves exactly as before (loopback allowed); an unknown value is rejected
2139
2277
  rather than silently defaulted.
2140
2278
 
2141
- > Dynamic Client Registration is **deprecated** in the 2026-07-28 spec in favour
2142
- > of Client ID Metadata Documents (CIMD), with a ≥12-month window. DCR keeps
2143
- > working and stays supported here; CIMD support is tracked separately.
2279
+ > Dynamic Client Registration is an explicit interoperability mode, not the
2280
+ > default. Enabling it changes public discovery and adds `/register`; keep it off
2281
+ > unless a client that cannot publish CIMD requires it.
2144
2282
 
2145
2283
  ## Proxying a remote API — `implementRemote`
2146
2284
 
@@ -2157,11 +2295,14 @@ const http = createHttpClient({
2157
2295
  headers: () => ({ Authorization: `Bearer ${apiKey}` }),
2158
2296
  })
2159
2297
 
2160
- await createStdioMcpServer({
2298
+ const stdio = await createStdioMcpServer({
2161
2299
  serverInfo: { name: 'my-app', version: '1.0.0' },
2162
2300
  auth: null,
2163
2301
  services: contracts.map((c) => implementRemote(c, http)),
2164
2302
  })
2303
+
2304
+ // During graceful shutdown:
2305
+ await stdio.close()
2165
2306
  ```
2166
2307
 
2167
2308
  This is how you ship a thin **local** MCP server for an API that already runs in
@@ -2175,9 +2316,16 @@ no duplicated business logic.
2175
2316
 
2176
2317
  ## Structured output
2177
2318
 
2178
- When a contract endpoint declares an object `output`, its MCP tool registers
2179
- that as the tool `outputSchema` and the result carries `structuredContent`
2180
- alongside the text block the structured payload an MCP App UI consumes.
2319
+ When a contract endpoint or runtime tool declares `output`, its MCP tool
2320
+ registers that exact schema as `outputSchema` and returns the validated value as
2321
+ `structuredContent` alongside a JSON text block. Every JSON root type is kept
2322
+ unchanged: object, array, string, number, boolean and `null`. Stitchkit does not
2323
+ wrap non-object values in `{ result }`; protocol-era adaptation belongs to the
2324
+ official SDK.
2325
+
2326
+ A tool without an output contract advertises no `outputSchema` and returns no
2327
+ invented structured value. Its successful MCP result has an empty `content`
2328
+ list.
2181
2329
 
2182
2330
  ## AI agents — `mountAgent`
2183
2331
 
@@ -3342,8 +3490,9 @@ Use the exported `zodIssues(error)` to build the same structured list from a
3342
3490
  ### On the client
3343
3491
 
3344
3492
  The client parses that envelope back into an `ApiError` with the same `code`,
3345
- `status`, `details` and `hint` see [Typed client ApiError](./client.md#apierror).
3346
- The error round-trips: one model, server to client.
3493
+ `status`, `details` and `hint`, plus the response `x-request-id` as optional
3494
+ readonly `traceId` see [Typed client ApiError](./client.md#apierror). The
3495
+ error round-trips without adding correlation data to the response body.
3347
3496
 
3348
3497
  ### Stitch codes vs your codes
3349
3498
 
@@ -3701,7 +3850,8 @@ locally; you do not need to change `NODE_ENV` or deploy.
3701
3850
  Every response the stitchkit handler produces carries the resolved id as
3702
3851
  **`x-request-id`**. With `cors` configured it is in the default
3703
3852
  `Access-Control-Expose-Headers`, so a browser client can read it and quote it in
3704
- a bug report. Note the deliberate asymmetry: inbound the id may arrive as
3853
+ a bug report. Stitchkit browser clients retain it as `ApiError.traceId` on
3854
+ response-backed failures. Note the deliberate asymmetry: inbound the id may arrive as
3705
3855
  `X-Trace-Id` *or* `X-Request-Id`; outbound there is one name and no alias.
3706
3856
 
3707
3857
  Log the same id from nginx and the two logs join on one key. `log_format` and
@@ -3751,8 +3901,25 @@ cross-origin `trace: true` client works out of the box. If you set a custom
3751
3901
  `cors.headers`, extend `DEFAULT_CORS_ALLOW_HEADERS` rather than replacing it, or
3752
3902
  the preflight will reject the trace header.
3753
3903
 
3904
+ MCP tools use the same context. On a modern HTTP or stdio `tools/call`, the
3905
+ official SDK exposes W3C propagation values from request `_meta`; Stitchkit
3906
+ continues a valid `_meta.traceparent` before validation, lifecycle and hooks.
3907
+ If MCP metadata omits `traceparent`, an HTTP call keeps its ambient HTTP trace
3908
+ and a stdio call starts a new root. If the key is present but malformed or
3909
+ all-zero, the call still runs under a fresh local trace instead of trusting the
3910
+ ambient one. This makes explicit MCP propagation authoritative without allowing
3911
+ invalid input to break the tool.
3912
+
3913
+ Bounded `tracestate` and `baggage` are available through
3914
+ `getRequestContext()?.trace` for outbound propagation. Their contents are not
3915
+ copied into the standard `RequestEvent`; never use them for auth, RBAC, tenant
3916
+ selection or rate-limit identity. Every MRTR attempt reads its current request
3917
+ metadata, so callers that want one trace across rounds must propagate it on
3918
+ each request.
3919
+
3754
3920
  > **Span ids live in the request context, not on `ctx`.** The handler `ctx`
3755
- > carries a single `traceId`; the full `{ traceId, spanId, parentSpanId }` is on
3921
+ > carries a single `traceId`; the full
3922
+ > `{ traceId, spanId, parentSpanId, tracestate?, baggage? }` is on
3756
3923
  > the observability request context. To stamp `spanId` / `parentSpanId` into an
3757
3924
  > audit row read `getRequestContext()?.trace`, not `ctx.spanId`:
3758
3925
  >
@@ -4091,9 +4258,11 @@ an independently built Next.js frontend and Bun/Stitchkit API.
4091
4258
 
4092
4259
  ### MCP
4093
4260
 
4094
- If the app exposes MCP tools, `createMcpHandler` is mounted as a raw route
4095
- (`/mcp`). It needs the `@modelcontextprotocol/sdk` peer installed in production
4096
- it is optional only for apps that do not use MCP.
4261
+ If the app exposes MCP tools, mount `createMcpHandler` with
4262
+ `createMcpHttpRoute({ path: '/mcp', handler })` and close the handler during
4263
+ graceful shutdown. Production needs the `@modelcontextprotocol/server` v2 peer;
4264
+ apps without MCP do not. Only an MCP host or integration-test package needs
4265
+ `@modelcontextprotocol/client`.
4097
4266
 
4098
4267
 
4099
4268
  ==============================================================================
@@ -4362,6 +4531,174 @@ current one *up to* your target, and apply each snippet.
4362
4531
 
4363
4532
  ## Unreleased breaking migrations
4364
4533
 
4534
+ ### MCP TypeScript SDK v2 and protocol `2026-07-28`
4535
+
4536
+ This is a hard cut: there is one Stitchkit API and no v1 aliases. Applications
4537
+ that expose MCP install the split server package; applications that implement an
4538
+ MCP host or run client E2E install the split client package.
4539
+
4540
+ ```bash
4541
+ bun remove @modelcontextprotocol/sdk
4542
+ bun add @modelcontextprotocol/server@^2 ai@^7
4543
+ # MCP hosts and client-side E2E only:
4544
+ bun add -d @modelcontextprotocol/client@^2
4545
+ ```
4546
+
4547
+ Direct SDK imports move to the split packages too:
4548
+
4549
+ ```ts
4550
+ // before
4551
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
4552
+
4553
+ // after
4554
+ import { McpServer } from '@modelcontextprotocol/server'
4555
+ ```
4556
+
4557
+ MCP Apps additionally install `@modelcontextprotocol/ext-apps`. That adapter may
4558
+ carry its own isolated v1-era transitive/peer relationship while the ecosystem
4559
+ finishes its cutover; it does not permit application code to import the removed
4560
+ monolithic SDK. Application-owned MCP server and client code uses the v2 split
4561
+ packages exclusively.
4562
+
4563
+ ```ts
4564
+ // before
4565
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js'
4566
+ import { createMcpHandler } from 'stitchkit/tools'
4567
+
4568
+ const handleMcp = createMcpHandler({
4569
+ serverInfo,
4570
+ auth,
4571
+ services,
4572
+ sessionMode: 'stateless',
4573
+ })
4574
+ rawRoutes: [{ method: 'ALL', path: '/mcp', handler: handleMcp }]
4575
+
4576
+ // after
4577
+ import { Client } from '@modelcontextprotocol/client'
4578
+ import { createMcpHandler, createMcpHttpRoute } from 'stitchkit/tools'
4579
+
4580
+ const mcp = createMcpHandler({
4581
+ serverInfo,
4582
+ auth,
4583
+ services,
4584
+ legacy: 'serve',
4585
+ })
4586
+ rawRoutes: [createMcpHttpRoute({ path: '/mcp', handler: mcp })]
4587
+ // graceful shutdown: await mcp.close()
4588
+ ```
4589
+
4590
+ Keep the owned handle and close it from the same shutdown path as the HTTP
4591
+ server. A minimal runtime smoke should list tools before shutdown and prove the
4592
+ closed handler no longer serves requests:
4593
+
4594
+ ```ts
4595
+ const mcp = createMcpHandler(config)
4596
+ const route = createMcpHttpRoute({ path: '/mcp', handler: mcp })
4597
+
4598
+ const beforeClose = await route.handler(listToolsRequest)
4599
+ if (!beforeClose.ok) throw new Error('MCP list-tools smoke failed')
4600
+
4601
+ await mcp.close()
4602
+ ```
4603
+
4604
+ Remove `@modelcontextprotocol/sdk`, `sessionMode`, `McpSessionMode`, session
4605
+ stores and all `Mcp-Session-Id` handling. HTTP is always request-isolated and
4606
+ stateless. `legacy: 'serve'` (default) lets the official SDK negotiate supported
4607
+ pre-2026 stateless clients on the same endpoint; `legacy: 'reject'` makes it
4608
+ modern-only. This is not a stateful compatibility transport.
4609
+
4610
+ The stdio helper now returns an owned lifecycle handle:
4611
+
4612
+ ```ts
4613
+ // before
4614
+ await createStdioMcpServer(config)
4615
+
4616
+ // after
4617
+ const stdio = await createStdioMcpServer({ ...config, legacy: 'serve' })
4618
+ await stdio.close()
4619
+ ```
4620
+
4621
+ OAuth client registration is now one explicit policy object. CIMD is secure and
4622
+ enabled by default; DCR is disabled unless supplied:
4623
+
4624
+ ```ts
4625
+ // before
4626
+ mountOAuthProvider({ ...oauth, clients, codes, refreshTokens })
4627
+
4628
+ // after
4629
+ mountOAuthProvider({
4630
+ ...oauth,
4631
+ clientRegistration: {
4632
+ preRegistered: { get: clients.get },
4633
+ // optional: dcr: { register: clients.register, get: clients.get }
4634
+ },
4635
+ codes,
4636
+ refreshTokens,
4637
+ })
4638
+ ```
4639
+
4640
+ A URL client id must be HTTPS and serve a document whose `client_id` exactly
4641
+ matches that URL, with explicit `redirect_uris` and
4642
+ `token_endpoint_auth_method: 'none'`. Do not keep a consumer-side metadata
4643
+ fetcher or DCR fallback; Stitchkit owns SSRF-safe resolution and caching.
4644
+
4645
+ Multi-round input is opt-in on the operation and does not change Agent, CLI or
4646
+ ordinary HTTP handlers:
4647
+
4648
+ ```ts
4649
+ const ConfirmationSchema = z.object({ confirmed: z.boolean() })
4650
+
4651
+ mcp: {
4652
+ inputRequired: [{
4653
+ key: 'confirmation',
4654
+ message: 'Confirm this action',
4655
+ schema: ConfirmationSchema,
4656
+ }],
4657
+ }
4658
+ ```
4659
+
4660
+ Configure `multiRound.state` on the MCP server with a key of at least 32 bytes
4661
+ and a stable authenticated `principal`. Read accepted typed content from
4662
+ `ctx.mcpInput.confirmation`. Multiple declarations run in array order, the
4663
+ aggregate remains exactly typed by key, keys must be unique, and declarations
4664
+ must fit `multiRound.serving.maxRounds` (default `10`). Do not execute a
4665
+ destructive side effect before the complete aggregate is accepted. A modern
4666
+ request missing the declared elicitation capability receives JSON-RPC error
4667
+ code `-32021`. The official per-request legacy HTTP bridge cannot issue
4668
+ server-to-client elicitation, so it returns a deterministic failed tool result;
4669
+ multi-round input is never silently treated as complete.
4670
+
4671
+ #### Compatibility matrix
4672
+
4673
+ | Host / transport | Tools and Apps | Multi-round input | Continuity |
4674
+ |---|---|---|---|
4675
+ | `2026-07-28` HTTP | yes | yes | request-isolated |
4676
+ | supported legacy stateless HTTP | yes | unsupported result | request-isolated |
4677
+ | `2026-07-28` stdio | yes | yes | one process connection |
4678
+ | supported legacy stdio | yes | official SDK bridge | one process connection |
4679
+ | Agent / CLI / ordinary HTTP | unchanged | not exposed | unchanged |
4680
+
4681
+ Subscriptions, cross-request progress and resumable stateful SSE are not
4682
+ implemented or advertised.
4683
+
4684
+ #### Consumer checklist
4685
+
4686
+ 1. Replace the monolithic SDK with `@modelcontextprotocol/server@^2` and, only
4687
+ for hosts/tests, `@modelcontextprotocol/client@^2`.
4688
+ 2. Replace raw MCP route wiring with `createMcpHttpRoute`; retain and close the
4689
+ returned handler/stdio handle during shutdown.
4690
+ 3. Delete all session mode, event-store and session-id code.
4691
+ 4. Move OAuth client policy under `clientRegistration`; publish CIMD or enable
4692
+ DCR explicitly.
4693
+ 5. Snapshot `listToolNames`, run one contract tool, one runtime tool, any raw
4694
+ multimodal tool and every MCP App resource you use.
4695
+ 6. Exercise modern HTTP and stdio with protocol `2026-07-28`; exercise legacy
4696
+ only if `legacy: 'serve'` is part of your support policy.
4697
+ 7. Run the consumer's typecheck and runtime gates. A browser/HTTP-only consumer
4698
+ must continue to work without either MCP package.
4699
+
4700
+ ## Historical breaking migrations through 0.43.1
4701
+
4365
4702
  HTTP observability now completes inside the framework handler instead of a
4366
4703
  nested fetch wrapper. Configure request and tool sinks explicitly:
4367
4704
 
@@ -4709,23 +5046,6 @@ If a tool hook annotated `endpoint` as `MethodDef`, remove that annotation or
4709
5046
  use `OperationIdentity`: native operations have service/action/scope/method but
4710
5047
  no HTTP path.
4711
5048
 
4712
- ### MCP HTTP sessions
4713
-
4714
- Finally, replace the MCP HTTP session boolean. Omission changed meaning:
4715
-
4716
- ```ts
4717
- // before → after
4718
- stateless: true // → sessionMode: 'stateless'
4719
- stateless: false // → sessionMode: 'stateful'
4720
-
4721
- // before: omission was stateful
4722
- // after: omission is stateless
4723
- ```
4724
-
4725
- If the client relies on `Mcp-Session-Id`, server push, progress across requests
4726
- or resumable SSE, set `sessionMode: 'stateful'` explicitly. Synchronous tool
4727
- servers should omit it and use the new restart-safe default.
4728
-
4729
5049
  ### Node-facing server types
4730
5050
 
4731
5051
  `stitchkit/server` remains Bun-concrete: an explicitly annotated `RawRoute`
@@ -4839,7 +5159,7 @@ The browser-and-server entrypoint. Re-exports everything from
4839
5159
  | `contractEndpointMatchers` | function | compile exact pathname matchers for selected HTTP contract operations and expected-401 policy |
4840
5160
  | `PathPrefixArgs` | _type_ | required string-valued keys exposed to a typed dynamic `pathPrefix` callback |
4841
5161
  | `createHttpClient` | function | the Ky-based HTTP transport — [guide](../guide/client.md#createhttpclient) |
4842
- | `ApiError` | class | a non-2xx response, with `code` / `status` / `details` / `hint` |
5162
+ | `ApiError` | class | a non-2xx response, with `code` / `status` / `details` / `hint` and optional readonly `traceId` from `x-request-id` |
4843
5163
  | `HttpClient` | _type_ | the transport interface `createClient` builds on |
4844
5164
  | `ConfiguredHttpClient` | _type_ | a framework-created `HttpClient` carrying its readonly `baseUrl` for URL builders |
4845
5165
  | `HttpClientConfig` | _type_ | config for `createHttpClient` |
@@ -4874,7 +5194,7 @@ can emit / propagate a `traceparent` (see `HttpClientConfig.trace`).
4874
5194
  | `formatTraceparent` | function | render a `traceparent` header value |
4875
5195
  | `parseTraceparent` | function | parse a `traceparent` header |
4876
5196
  | `childSpan` | function | a child span of a parent trace |
4877
- | `TraceContext` | _type_ | `{ traceId, spanId, parentSpanId? }` |
5197
+ | `TraceContext` | _type_ | `{ traceId, spanId, parentSpanId?, tracestate?, baggage? }` |
4878
5198
 
4879
5199
  ---
4880
5200
 
@@ -4921,6 +5241,8 @@ from the root `stitchkit`.
4921
5241
  | `FileDescriptor` | _type_ | a React Native / Expo file — `{ uri, name, type }` |
4922
5242
  | `EndpointToolAnnotations` | _type_ | MCP behavioural hints on an endpoint (`readOnlyHint` / `destructiveHint` / `title`) |
4923
5243
  | `EndpointUiMeta` | _type_ | MCP Apps widget metadata on an endpoint |
5244
+ | `EndpointMcpInputRequired` | _type_ | typed MCP multi-round input request (`key`, message and Zod object schema) |
5245
+ | `EndpointMcpPolicy` | _type_ | MCP-only endpoint policy containing `inputRequired` |
4924
5246
 
4925
5247
  ### Errors
4926
5248
 
@@ -5138,11 +5460,12 @@ audit event. See the [Observability guide](../guide/observability.md).
5138
5460
  | Export | Kind | Summary |
5139
5461
  |--------|------|---------|
5140
5462
  | `resolveTraceContext` | function | the trace for a request — `traceparent` continued or fresh |
5463
+ | `resolvePropagationContext` | function | continue bounded MCP/W3C propagation metadata with optional ambient fallback |
5141
5464
  | `parseTraceparent` | function | parse a `traceparent` header |
5142
5465
  | `formatTraceparent` | function | render a `traceparent` header value |
5143
5466
  | `createTraceContext` | function | a fresh root trace |
5144
5467
  | `childSpan` | function | a child span of a parent trace |
5145
- | `TraceContext` | _type_ | `{ traceId, spanId, parentSpanId? }` |
5468
+ | `TraceContext` | _type_ | `{ traceId, spanId, parentSpanId?, tracestate?, baggage? }` |
5146
5469
 
5147
5470
  ### Sanitisation
5148
5471
 
@@ -5160,12 +5483,19 @@ audit event. See the [Observability guide](../guide/observability.md).
5160
5483
 
5161
5484
  ## `stitchkit/tools`
5162
5485
 
5163
- Server-only. Turns contracts into MCP and AI-agent tools. Needs the
5164
- `@modelcontextprotocol/sdk` peer (for MCP) and the `ai` peer (for agents).
5486
+ Server-only. Turns contracts into MCP and AI-agent tools. MCP server surfaces
5487
+ need the `@modelcontextprotocol/server` v2 peer; MCP hosts/tests use the separate
5488
+ `@modelcontextprotocol/client` package. Agent surfaces need the `ai` peer.
5489
+
5490
+ Framework-owned MCP tools advertise the exact declared output schema and return
5491
+ the validated value unchanged as `structuredContent`, including arrays, scalars
5492
+ and `null`. Tools without an output contract advertise and return no structured
5493
+ payload.
5165
5494
 
5166
5495
  | Export | Kind | Summary |
5167
5496
  |--------|------|---------|
5168
- | `createMcpHandler` | function | a complete Streamable-HTTP MCP server — [guide](../guide/mcp-and-agents.md#mcp--createmcphandler) |
5497
+ | `createMcpHandler` | function | a stateless dual-era Streamable-HTTP MCP handler — [guide](../guide/mcp-and-agents.md#mcp--createmcphandler) |
5498
+ | `createMcpHttpRoute` | function | framework-owned `RawRoute` adapter for an MCP HTTP handler |
5169
5499
  | `createStdioMcpServer` | function | a complete stdio MCP server — [guide](../guide/mcp-and-agents.md#mcp-over-stdio--createstdiomcpserver) |
5170
5500
  | `buildMcpServer` | function | build an `McpServer` from contract/runtime surfaces — the transport-neutral core |
5171
5501
  | `mountMcp` | function | add contract tools to an existing `McpServer` — [guide](../guide/mcp-and-agents.md#mountmcp) |
@@ -5180,9 +5510,12 @@ Server-only. Turns contracts into MCP and AI-agent tools. Needs the
5180
5510
  | `resolveMedia` | function | resolve a media reference for a tool result |
5181
5511
  | `validateMcpSchemas` | function | object-shaped assertion over the exact advertised schema surface — compatibility, typed properties and portable formats ([guide](../guide/mcp-and-agents.md#mcp-schema-validation-profile)) |
5182
5512
  | `listToolNames` | function | every contract/runtime tool name with origin, identity and transports — for stable snapshots — [guide](../guide/mcp-and-agents.md#pinning-tool-names--listtoolnames) |
5183
- | `McpHandlerConfig` | _type_ | config for `createMcpHandler` |
5184
- | `McpHttpConfig` | _type_ | HTTP auth, protected-resource and session options composed into `McpHandlerConfig` |
5185
- | `McpSessionMode` | _type_ | `'stateless' \| 'stateful'`; HTTP defaults to request-isolated stateless mode |
5513
+ | `McpHandlerConfig` | _type_ | server surface plus stateless HTTP transport config |
5514
+ | `McpHttpConfig` | _type_ | HTTP auth, protected-resource, legacy-era and security options |
5515
+ | `McpHttpHandler` | _type_ | framework-owned `{ fetch(request), close() }` lifecycle |
5516
+ | `McpHttpSecurityConfig` | _type_ | Fetch-boundary Host and Origin allowlists |
5517
+ | `McpLegacyPolicy` | _type_ | `'serve' \| 'reject'` protocol-era compatibility policy |
5518
+ | `McpStdioHandle` | _type_ | closeable official stdio transport handle |
5186
5519
  | `StdioMcpServerConfig` | _type_ | config for `createStdioMcpServer` |
5187
5520
  | `McpServerBuildConfig` | _type_ | shared config for `buildMcpServer` |
5188
5521
  | `McpServerSharedConfig` | _type_ | transport-neutral options shared by direct and finite surface configs |
@@ -5210,6 +5543,15 @@ Server-only. Turns contracts into MCP and AI-agent tools. Needs the
5210
5543
  | `RuntimeToolOutput` | _type_ | output inferred from a runtime tool's optional Zod schema |
5211
5544
  | `RuntimeToolPresenters` | _type_ | optional MCP and AI SDK `toModelOutput` presentation callbacks |
5212
5545
  | `RuntimeMcpPresentation` | _type_ | MCP content/metadata result without framework-owned `structuredContent` or `isError` |
5546
+ | `RuntimeMcpInput` | _type_ | typed accepted multi-round input for an opted-in runtime tool |
5547
+ | `OAuthClientRegistrationConfig` | _type_ | deterministic pre-registered → CIMD → explicit-DCR client resolution |
5548
+ | `CimdClientMetadata` | _type_ | validated Client ID Metadata Document |
5549
+ | `CimdClientMetadataFetcher` | _type_ | injectable secure network boundary for metadata loading |
5550
+ | `CimdFetchResponse` | _type_ | bounded metadata fetch result passed across the injectable network boundary |
5551
+ | `CimdFetchPolicy` | _type_ | CIMD timeout, redirect and size limits |
5552
+ | `CimdCachePolicy` | _type_ | bounded HTTP-aware positive/negative cache policy |
5553
+ | `CimdCacheEvent` | _type_ | observable CIMD cache hit, miss, revalidation and eviction event |
5554
+ | `createSecureClientMetadataFetcher` | function | production HTTPS, DNS/IP-pinned CIMD fetcher |
5213
5555
  | `RuntimeAgentModelOutput` | _type_ | AI SDK model-facing text/JSON/content output returned by `present.agent` |
5214
5556
  | `RuntimeToolTransport` | _type_ | runtime exposure: `'MCP' \| 'AGENT'` |
5215
5557
  | `AgentMountConfig` | _type_ | config for `mountAgent` |