stitchkit 0.41.0 → 0.42.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 (51) hide show
  1. package/dist/cli.js +2 -2
  2. package/dist/contract/errors-factory.d.ts +88 -15
  3. package/dist/contract/errors-factory.d.ts.map +1 -1
  4. package/dist/contract/errors.d.ts +4 -4
  5. package/dist/contract/errors.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/contract/index.js +2 -2
  9. package/dist/{index-xax049k6.js → index-11x5dts2.js} +44 -13
  10. package/dist/{index-4e1c0hsw.js → index-181aebw8.js} +1 -1
  11. package/dist/{index-dnkefke9.js → index-310bfer5.js} +1 -1
  12. package/dist/{index-sc67e454.js → index-gex6gxhe.js} +7 -4
  13. package/dist/{index-ncqqn1bc.js → index-p9vkwns1.js} +2 -2
  14. package/dist/{index-809wc1tt.js → index-s4qsmgwe.js} +4 -1
  15. package/dist/index.js +2 -2
  16. package/dist/internal/typed.d.ts +8 -0
  17. package/dist/internal/typed.d.ts.map +1 -1
  18. package/dist/node.js +2 -2
  19. package/dist/observability/index.js +1 -1
  20. package/dist/react.js +1 -1
  21. package/dist/server/error-hook.d.ts +8 -4
  22. package/dist/server/error-hook.d.ts.map +1 -1
  23. package/dist/server/index.js +7 -6
  24. package/dist/tools/agent.d.ts +1 -1
  25. package/dist/tools/agent.d.ts.map +1 -1
  26. package/dist/tools/list-names.d.ts +8 -6
  27. package/dist/tools/list-names.d.ts.map +1 -1
  28. package/dist/tools/manifest.d.ts +13 -5
  29. package/dist/tools/manifest.d.ts.map +1 -1
  30. package/dist/tools/mcp-handler.d.ts +5 -4
  31. package/dist/tools/mcp-handler.d.ts.map +1 -1
  32. package/dist/tools/mcp-stdio.d.ts +3 -2
  33. package/dist/tools/mcp-stdio.d.ts.map +1 -1
  34. package/dist/tools/mcp.d.ts +49 -9
  35. package/dist/tools/mcp.d.ts.map +1 -1
  36. package/dist/tools/native-mcp.d.ts +4 -18
  37. package/dist/tools/native-mcp.d.ts.map +1 -1
  38. package/dist/tools/runtime-tool.d.ts +1 -1
  39. package/dist/tools/runtime-tool.d.ts.map +1 -1
  40. package/dist/tools/surface.d.ts +37 -0
  41. package/dist/tools/surface.d.ts.map +1 -0
  42. package/dist/tools/toolkit.d.ts +4 -4
  43. package/dist/tools/toolkit.d.ts.map +1 -1
  44. package/dist/tools/transports.d.ts +11 -9
  45. package/dist/tools/transports.d.ts.map +1 -1
  46. package/dist/tools/view-file.d.ts +3 -3
  47. package/dist/tools.d.ts +5 -5
  48. package/dist/tools.d.ts.map +1 -1
  49. package/dist/tools.js +292 -155
  50. package/llms-full.txt +264 -47
  51. package/package.json +1 -1
package/llms-full.txt CHANGED
@@ -1703,16 +1703,16 @@ call —
1703
1703
  ### Pinning tool names — `listToolNames`
1704
1704
 
1705
1705
  Derived tool names are part of your public surface — an MCP client config or an
1706
- agent prompt refers to them by string. `listToolNames(services)` resolves every
1707
- tool name your services expose (the exact resolver the mounts use), with its
1708
- `(service, method)` identity and transports, sorted — a stable shape to
1709
- snapshot:
1706
+ agent prompt refers to them by string. `listToolNames({ services, runtimeTools })`
1707
+ resolves the complete mixed surface (the exact resolver the mounts use), with
1708
+ its origin, `(service, method)` identity and transports, sorted — a stable shape
1709
+ to snapshot:
1710
1710
 
1711
1711
  ```ts
1712
1712
  import { listToolNames } from 'stitchkit/tools'
1713
1713
 
1714
1714
  test('tool names have not drifted', () => {
1715
- expect(listToolNames(services)).toMatchSnapshot()
1715
+ expect(listToolNames({ services, runtimeTools })).toMatchSnapshot()
1716
1716
  })
1717
1717
  ```
1718
1718
 
@@ -1806,13 +1806,16 @@ createServer({
1806
1806
  | `serverInfo` | MCP server identity — `{ name, version }` |
1807
1807
  | `auth` | `(req) => identity \| null` — `null` rejects with 401 |
1808
1808
  | `services` | the services to expose — an array, or `(auth) => ServiceDef[]` |
1809
+ | `runtimeTools` | managed pathless operations — an array, or `(auth) => RuntimeToolDefinition[]` |
1810
+ | `surfaces` | finite `{ key: { services, runtimeTools } }` registry prepared eagerly |
1811
+ | `selectSurface` | `(auth) => declaredKey` — required with `surfaces` |
1809
1812
  | `context` | `(auth) => {…}` — values merged into every tool handler's `ctx` |
1810
1813
  | `lifecycle` | `beforeHandle` / `afterHandle` — the tool-side auth gate (see below) |
1811
1814
  | `hooks` | tool-call observability hooks — `afterToolCall` fires on every result |
1812
1815
  | `extend` | extra advertised arguments resolved into handler context |
1813
1816
  | `schemaValidation` | compatibility policy, typed-property guard and portable-format guard |
1814
1817
  | `logger` | a `StitchLogger` for the `'warn'` policy |
1815
- | `nativeTools` | `({ registerTool, rawServer }, auth) => …` — protected native registration plus an explicit raw SDK escape hatch |
1818
+ | `rawTools` | `(server, auth) => …` — explicit SDK registration without framework guarantees |
1816
1819
  | `resources` | MCP Apps `ui://` resources mounted on every server |
1817
1820
  | `instructions` | a short host-facing usage hint, surfaced to MCP tool-search |
1818
1821
  | `coerceJsonArgs` | coerce JSON-stringified object/array arguments (default `true`) |
@@ -1822,14 +1825,37 @@ createServer({
1822
1825
  | `protectedResource` | RFC 9728 metadata used by HTTP `401` responses |
1823
1826
  | `sessionMode` | `'stateless'` (default) or explicit `'stateful'` session/SSE continuity |
1824
1827
 
1825
- `services`, `context` and `nativeTools` all receive the resolved identity, so a
1826
- tenant can be shown only its own tools and every handler can read `ctx.tenantId`.
1828
+ Direct `services` / `runtimeTools` factories, `context`, `selectSurface` and
1829
+ `rawTools` receive the resolved identity, so a tenant can be shown only its own
1830
+ tools and every handler can read `ctx.tenantId`.
1831
+
1832
+ Use direct factories only when the definitions are genuinely arbitrary per
1833
+ identity. For a bounded role/plan set, declare a finite registry instead:
1834
+
1835
+ ```ts
1836
+ const handleMcp = createMcpHandler({
1837
+ serverInfo,
1838
+ auth,
1839
+ surfaces: {
1840
+ admin: { services: allServices, runtimeTools: [renderPreview] },
1841
+ member: { services: memberServices, runtimeTools: [renderPreview] },
1842
+ },
1843
+ selectSurface: (identity) => identity.isAdmin ? 'admin' : 'member',
1844
+ context: (identity) => ({ identity }),
1845
+ })
1846
+ ```
1847
+
1848
+ Every declared entry is schema-validated and prepared once when the handler is
1849
+ constructed. The selected immutable descriptors are shared; the SDK server,
1850
+ transport, auth-derived context, lifecycle runner and tool-call context are
1851
+ fresh for every stateless request (or every stateful session). Unknown keys
1852
+ fail before the server connects. The registry never retains auth values.
1827
1853
 
1828
1854
  ### Stateless by default; stateful only when required
1829
1855
 
1830
1856
  The default `sessionMode: 'stateless'` creates a fresh SDK server, transport,
1831
- resolved auth/context and runner for each HTTP request. Static contract schemas
1832
- are still prepared once when the handler is constructed. There is no session
1857
+ resolved auth/context and runner for each HTTP request. Static direct and finite
1858
+ registry schemas are still prepared once when the handler is constructed. There is no session
1833
1859
  map, event store, sweep timer or `Mcp-Session-Id`, so process replacement and
1834
1860
  load balancing cannot strand a client on an in-memory session.
1835
1861
 
@@ -2006,8 +2032,8 @@ than a per-request `(req) => …`. Keep all logging on **stderr**: stdout is the
2006
2032
  JSON-RPC channel.
2007
2033
 
2008
2034
  Both transports build the server through the shared `buildMcpServer` — same
2009
- contract pipeline, same `services` / `context` / `hooks` / `nativeTools` /
2010
- `instructions`.
2035
+ contract/runtime pipeline, same surface selection, context, hooks, raw escape
2036
+ hatch and instructions.
2011
2037
 
2012
2038
  ## OAuth 2.1 — a native remote connector
2013
2039
 
@@ -2249,9 +2275,9 @@ const handleMcp = createMcpHandler({
2249
2275
  serverInfo: { name: 'my-app', version: '1.0.0' },
2250
2276
  auth,
2251
2277
  services: [service],
2278
+ runtimeTools: [renderPreview],
2252
2279
  lifecycle: { beforeHandle: authHook },
2253
2280
  hooks: audit.toolCall,
2254
- nativeTools: ({ registerTool }) => registerTool(renderPreview),
2255
2281
  })
2256
2282
 
2257
2283
  const agentTools = mountAgent([service], {
@@ -2281,7 +2307,7 @@ use the same validation, lifecycle and hook path as contract tools.
2281
2307
 
2282
2308
  ### Explicit raw SDK registration
2283
2309
 
2284
- `rawServer` is deliberately named as an escape hatch. A tool registered there
2310
+ `rawTools` is deliberately named as an escape hatch. A tool registered there
2285
2311
  does **not** receive stitchkit schema policy, lifecycle, per-call context or
2286
2312
  hooks. The built-in `mountViewFile` helper remains raw for callers that choose
2287
2313
  that boundary; it fetches media with SSRF and path-traversal defenses:
@@ -2293,14 +2319,51 @@ const handleMcp = createMcpHandler({
2293
2319
  serverInfo: { name: 'my-app', version: '1.0.0' },
2294
2320
  auth,
2295
2321
  services: [service],
2296
- nativeTools: ({ rawServer }) =>
2297
- mountViewFile(rawServer, { baseDir: '/srv/uploads' }),
2322
+ rawTools: (server) => mountViewFile(server, { baseDir: '/srv/uploads' }),
2298
2323
  })
2299
2324
  ```
2300
2325
 
2301
2326
  Use raw registration only when opting out is intentional. For a protected
2302
- `view_file`, define it through `registerTool` and call the exported
2303
- `resolveMedia` core from its handler.
2327
+ `view_file`, define it with `defineRuntimeTool`, include it in `runtimeTools`,
2328
+ and call the exported `resolveMedia` core from its handler.
2329
+
2330
+ ## Introspecting the complete tool surface
2331
+
2332
+ Deferred tool search, name snapshots and boot diagnostics accept the same
2333
+ object-shaped surface as the mounts. Contract operations are followed by
2334
+ runtime definitions, matching mount order; exposure filters and collisions are
2335
+ resolved by Stitchkit rather than by consumer code:
2336
+
2337
+ ```ts
2338
+ import {
2339
+ buildToolManifest,
2340
+ listToolNames,
2341
+ summarizeTransports,
2342
+ } from 'stitchkit/tools'
2343
+
2344
+ const surface = { services, runtimeTools: [renderPreview] }
2345
+
2346
+ const manifest = buildToolManifest({
2347
+ ...surface,
2348
+ transport: 'AGENT',
2349
+ flattenUnionInput: true,
2350
+ })
2351
+
2352
+ const names = listToolNames(surface)
2353
+ const summary = summarizeTransports(surface)
2354
+ ```
2355
+
2356
+ `buildToolManifest` returns the exact immutable presentation schema shown to
2357
+ the selected MCP or Agent transport. It does not execute validation effects and
2358
+ fails first on duplicate contract/runtime names. There is no runtime-only
2359
+ manifest helper or public mount adapter: the framework owns merging and schema
2360
+ projection.
2361
+
2362
+ `listToolNames` remains diagnostic and reports `kind: 'contract' | 'runtime'`
2363
+ for every identity. `summarizeTransports` returns `contractServices`,
2364
+ `runtimeTools`, aggregate `totals`, and a `sources` breakdown. Runtime tools
2365
+ contribute only to MCP/Agent according to their `transports`; they never inflate
2366
+ HTTP or CLI counts.
2304
2367
 
2305
2368
  ## Logging tool calls — `createToolLogger`
2306
2369
 
@@ -2320,9 +2383,9 @@ Pass `log` to redirect the line, or `onRecord` to feed a metrics sink the
2320
2383
  structured `ToolCallRecord`. That record carries `traceId` whenever an
2321
2384
  observability context is active, so a tool call made inside an HTTP request
2322
2385
  joins that request's log line on one key — see
2323
- [Observability](./observability.md). For a boot-time picture of what is exposed where,
2324
- `summarizeTransports(services)` returns per-transport operation counts (HTTP /
2325
- MCP / AGENT / CLI) for you to log.
2386
+ [Observability](./observability.md). For a boot-time picture of what is exposed
2387
+ where, `summarizeTransports({ services, runtimeTools })` returns the mixed
2388
+ per-transport counts for you to log.
2326
2389
 
2327
2390
  ## One handler, three callers
2328
2391
 
@@ -3253,27 +3316,46 @@ onError: (ctx, err) => {
3253
3316
 
3254
3317
  ## Domain errors — `defineErrors`
3255
3318
 
3256
- Declaring your app's error codes once gives you typed throwers on the server and
3257
- a code table the client matches with autocomplete — instead of reading the raw
3258
- `message` string (which breaks the moment a code expects a string but gets an
3259
- object):
3319
+ Declare each domain code, HTTP status and optional structured-details schema in
3320
+ one immutable registry. The generated functions construct typed branded
3321
+ `AppError` instances; ordinary `throw` remains explicit at the call site:
3260
3322
 
3261
3323
  ```ts
3262
- export const { errors, codes, isCode } = defineErrors({
3263
- SESSION_NOT_FOUND: 404,
3264
- QUOTA_EXCEEDED: 429,
3324
+ import { z } from 'zod'
3325
+
3326
+ export const { errors, codes, definitions, isCode } = defineErrors({
3327
+ SESSION_NOT_FOUND: { status: 404 },
3328
+ QUOTA_EXCEEDED: {
3329
+ status: 429,
3330
+ details: z.object({ retryAfterSeconds: z.number().int().positive() }),
3331
+ },
3332
+ })
3333
+
3334
+ throw errors.SESSION_NOT_FOUND({ message: 'No such session' })
3335
+ throw errors.QUOTA_EXCEEDED({
3336
+ message: 'Try later',
3337
+ details: { retryAfterSeconds: 30 },
3338
+ hint: 'Wait for the current window to expire',
3265
3339
  })
3266
3340
 
3267
- // server a typed thrower, the right HTTP status baked in
3268
- throw errors.SESSION_NOT_FOUND('no such session')
3341
+ // Construction without throwing is useful for composition or inspection.
3342
+ const error = errors.QUOTA_EXCEEDED({ details: { retryAfterSeconds: 30 } })
3343
+ definitions.QUOTA_EXCEEDED.status // 429 — same source, no copied status map
3269
3344
 
3270
3345
  // client — match the code, never a magic string
3271
3346
  if (err instanceof ApiError && err.code === codes.SESSION_NOT_FOUND) { … }
3272
3347
  ```
3273
3348
 
3274
- The `code` rides through unchanged in both the HTTP envelope and the MCP tool
3275
- result, so one vocabulary covers every transport. The codes are yours; the core
3276
- stays domain-free.
3349
+ With no `details` schema, the options object forbids `details`. A required
3350
+ `z.object` makes `details` required; `z.object(...).optional()` makes it
3351
+ optional. Supplied details are parsed when the error is constructed, before any
3352
+ transport sees them. `code` remains literal and the parsed details type is
3353
+ retained on the returned `AppError`.
3354
+
3355
+ HTTP renders the complete code/status/message/details/hint. Tool transports keep
3356
+ their established model-facing projection (code/details/hint, without HTTP
3357
+ status), while `invokeOrThrow` recovers the exact normalized `AppError`. The
3358
+ codes and schemas remain application-owned; Stitchkit stays domain-free.
3277
3359
 
3278
3360
  ## `createErrorHook`
3279
3361
 
@@ -3305,6 +3387,27 @@ createServer({ services, hooks: { onError } })
3305
3387
  Codes you threw yourself (not stitchkit's) pass through `codeMap` unchanged; the
3306
3388
  `satisfies Record<StitchErrorCode, …>` keeps the map exhaustive across upgrades.
3307
3389
 
3390
+ Both `onError` and `render` may be asynchronous and receive the matched endpoint
3391
+ as their final argument. The observer is awaited before rendering, so it can
3392
+ resolve identity or enrich the request context even for failures raised before
3393
+ `beforeHandle`:
3394
+
3395
+ ```ts
3396
+ const onError = createErrorHook({
3397
+ onError: async (_error, _info, ctx, endpoint) => {
3398
+ await attributeFailedRequest(ctx, endpoint)
3399
+ },
3400
+ render: (info, ctx) => ({
3401
+ error: { code: info.code },
3402
+ actorId: ctx.actorId,
3403
+ }),
3404
+ })
3405
+ ```
3406
+
3407
+ `endpoint` is `undefined` when the failure happened before route resolution.
3408
+ Synchronous callbacks and renderers that declare fewer parameters continue to
3409
+ work normally.
3410
+
3308
3411
  Invalid input (a `ZodError`) is classified as `VALIDATION_ERROR` 400 before it
3309
3412
  reaches `render` — a client fault is an honest 400, not a 500 — and the
3310
3413
  offending fields arrive as structured `info.details.issues`, so your `render`
@@ -3477,6 +3580,12 @@ context carries nothing yet, so your value always wins.
3477
3580
  `event.serviceName` / `event.action` are present on every event, including a
3478
3581
  pre-handler 400. Nothing to wire.
3479
3582
 
3583
+ When failure attribution itself is asynchronous, use `createErrorHook`'s
3584
+ `onError(error, info, ctx, endpoint)` observer. The framework awaits it before
3585
+ calling `render`, so identity or audit enrichment is visible to both the final
3586
+ error envelope and the request event. `endpoint` is the matched `MethodDef`, or
3587
+ `undefined` when routing failed before a method was selected.
3588
+
3480
3589
  **Domain dimensions** — attach your own tenant / project / entity id with
3481
3590
  `setRequestDimensions`. It is an opaque `Record<string, string>` the core gives no
3482
3591
  meaning to (→ ADR 0021). Resolve it cheaply from `ctx.params` / headers in
@@ -4119,6 +4228,97 @@ current one *up to* your target, and apply each snippet.
4119
4228
 
4120
4229
  ## Unreleased breaking migrations
4121
4230
 
4231
+ Tool introspection now accepts one object-shaped contract/runtime surface. Stop
4232
+ calling the internal contract collector or merging a locally converted runtime
4233
+ manifest:
4234
+
4235
+ ```ts
4236
+ // before
4237
+ buildToolManifest(services.flatMap((service) => collectTools(service, 'AGENT')))
4238
+ listToolNames(services)
4239
+ summarizeTransports(services)
4240
+
4241
+ // after
4242
+ const surface = { services, runtimeTools }
4243
+ buildToolManifest({ ...surface, transport: 'AGENT' })
4244
+ listToolNames(surface)
4245
+ summarizeTransports(surface)
4246
+ ```
4247
+
4248
+ `ToolNameEntry` adds `kind: 'contract' | 'runtime'`. `TransportSummary` is now
4249
+ `{ contractServices, runtimeTools, totals, sources }`; replace `services` and
4250
+ `perService` reads with the explicit counts and mixed-source breakdown. There
4251
+ is no positional overload and no `buildRuntimeToolManifest`: Stitchkit owns the
4252
+ combined order, transport filtering, canonical presentation schema and
4253
+ cross-origin collision checks.
4254
+
4255
+ `defineErrors` now uses one Zod-first definition object and returns constructors
4256
+ instead of positional throwers. Add explicit `throw`, move message/details/hint
4257
+ into one options object, and declare a details schema when that code carries
4258
+ structured context:
4259
+
4260
+ ```ts
4261
+ // before
4262
+ const { errors } = defineErrors({ QUOTA_EXCEEDED: 429 })
4263
+ errors.QUOTA_EXCEEDED('Try later', { retryAfterSeconds: 30 }, 'Wait')
4264
+
4265
+ // after
4266
+ const { errors, definitions } = defineErrors({
4267
+ QUOTA_EXCEEDED: {
4268
+ status: 429,
4269
+ details: z.object({ retryAfterSeconds: z.number().positive() }),
4270
+ },
4271
+ })
4272
+ throw errors.QUOTA_EXCEEDED({
4273
+ message: 'Try later',
4274
+ details: { retryAfterSeconds: 30 },
4275
+ hint: 'Wait',
4276
+ })
4277
+ ```
4278
+
4279
+ There is no positional overload. A code without `details` forbids them; use an
4280
+ optional object schema when the details object itself is optional. Read status
4281
+ and schemas from the frozen `definitions` registry instead of maintaining a
4282
+ parallel map.
4283
+
4284
+ Managed MCP runtime tools are now declared as immutable data. Move protected
4285
+ registrar calls to `runtimeTools`; rename deliberate raw SDK registration to
4286
+ `rawTools`. There is no registrar alias:
4287
+
4288
+ ```ts
4289
+ // before — protected
4290
+ createMcpHandler({
4291
+ services,
4292
+ nativeTools: ({ registerTool }) => registerTool(preview),
4293
+ })
4294
+
4295
+ // after — protected and prepared with the rest of the surface
4296
+ createMcpHandler({ services, runtimeTools: [preview] })
4297
+
4298
+ // before — deliberate raw SDK opt-out
4299
+ nativeTools: ({ rawServer }, auth) => mountRaw(rawServer, auth)
4300
+
4301
+ // after — still a deliberate raw SDK opt-out
4302
+ rawTools: (server, auth) => mountRaw(server, auth)
4303
+ ```
4304
+
4305
+ When identity selects from a bounded set, replace a repeatedly prepared
4306
+ `services(auth)` factory with a finite registry:
4307
+
4308
+ ```ts
4309
+ createMcpHandler({
4310
+ surfaces: {
4311
+ admin: { services: allServices, runtimeTools: [preview] },
4312
+ member: { services: memberServices, runtimeTools: [preview] },
4313
+ },
4314
+ selectSurface: (auth) => auth.isAdmin ? 'admin' : 'member',
4315
+ })
4316
+ ```
4317
+
4318
+ Keep direct `services(auth)` / `runtimeTools(auth)` only for genuinely
4319
+ unbounded definitions; Stitchkit intentionally does not cache arbitrary auth
4320
+ values.
4321
+
4122
4322
  Contract success bodies are now determined by the presence of `output`, not by
4123
4323
  the handler's runtime value. A nullable output returns JSON `null` with status
4124
4324
  `200`; `undefined` with a declared output and non-null data without an output
@@ -4222,7 +4422,7 @@ const preview = defineRuntimeTool({
4222
4422
  }),
4223
4423
  },
4224
4424
  })
4225
- nativeTools: ({ registerTool }) => registerTool(preview)
4425
+ runtimeTools: [preview]
4226
4426
  ```
4227
4427
 
4228
4428
  The removed `NativeMcp*` types have no aliases. Use `RuntimeToolDefinition`,
@@ -4421,7 +4621,7 @@ name derivation, diff them mechanically:
4421
4621
 
4422
4622
  ```ts
4423
4623
  import { listToolNames } from 'stitchkit/tools'
4424
- console.log(JSON.stringify(listToolNames(services), null, 2))
4624
+ console.log(JSON.stringify(listToolNames({ services, runtimeTools }), null, 2))
4425
4625
  ```
4426
4626
 
4427
4627
  `listToolNames` never throws on an illegal name — that is deliberate, so it can
@@ -4570,9 +4770,17 @@ from the root `stitchkit`.
4570
4770
  | `conflict` | function | throw `409 CONFLICT` |
4571
4771
  | `rateLimited` | function | throw `429 RATE_LIMITED` |
4572
4772
  | `appError` | function | throw an `AppError` for any code |
4573
- | `defineErrors` | function | declare domain error codes → typed throwers + a code table — [guide](../guide/auth-and-errors.md#domain-errors--defineerrors) |
4574
- | `DefinedErrors` | _type_ | the `{ errors, codes, isCode }` handle `defineErrors` returns |
4575
- | `ErrorThrower` | _type_ | one `defineErrors` thrower `(message?, details?, hint?) => never` |
4773
+ | `defineErrors` | function | declare immutable domain error definitions → typed `AppError` constructors, codes and schemas — [guide](../guide/auth-and-errors.md#domain-errors--defineerrors) |
4774
+ | `DefinedErrors` | _type_ | the `{ errors, codes, definitions, isCode }` handle `defineErrors` returns |
4775
+ | `DefinedAppError` | _type_ | literal-code error instance with schema-refined details |
4776
+ | `ErrorDefinition` | _type_ | `{ status, details? }` definition for one domain code |
4777
+ | `ErrorDefinitions` | _type_ | string-keyed domain error definition registry |
4778
+ | `ErrorDetailsSchema` | _type_ | required or optional Zod object accepted for structured details |
4779
+ | `ErrorDetailsOutput` | _type_ | parsed details inferred from one definition |
4780
+ | `ErrorFactoryArguments` | _type_ | options tuple with forbidden/required/optional details inferred per code |
4781
+ | `ErrorFactory` | _type_ | one typed `AppError` constructor |
4782
+ | `ErrorFactories` | _type_ | mapped constructor registry derived from all definitions |
4783
+ | `FrozenErrorDefinitions` | _type_ | read-only definition registry returned to consumers |
4576
4784
  | `STITCH_ERROR_STATUS` | const | `code → HTTP status` map for stitchkit's own error codes — [guide](../guide/auth-and-errors.md#stitch-codes-vs-your-codes) |
4577
4785
  | `StitchErrorCode` | _type_ | a code stitchkit itself emits (`keyof STITCH_ERROR_STATUS`) |
4578
4786
  | `isStitchErrorCode` | function | type guard — is a code one of stitchkit's own? |
@@ -4640,8 +4848,8 @@ Also re-exports the error helpers from `stitchkit/contract`.
4640
4848
  | Export | Kind | Summary |
4641
4849
  |--------|------|---------|
4642
4850
  | `createAuthHook` | function | a scope-enforcing `beforeHandle` hook — [guide](../guide/auth-and-errors.md#createauthhook) |
4643
- | `createErrorHook` | function | an `onError` hook from a code map + envelope renderer — [guide](../guide/auth-and-errors.md#createerrorhook) |
4644
- | `ErrorHookConfig` | _type_ | config for `createErrorHook` |
4851
+ | `createErrorHook` | function | an async-capable, endpoint-aware `onError` hook from a code map + envelope renderer — [guide](../guide/auth-and-errors.md#createerrorhook) |
4852
+ | `ErrorHookConfig` | _type_ | async observer/renderer config for `createErrorHook` |
4645
4853
  | `ResolvedError` | _type_ | the normalised error handed to `createErrorHook`'s `render` |
4646
4854
  | `createBearerResolver` | function | a bearer-token identity resolver |
4647
4855
  | `signJwt` | function | sign an HS256 JWT |
@@ -4790,7 +4998,7 @@ Server-only. Turns contracts into MCP and AI-agent tools. Needs the
4790
4998
  |--------|------|---------|
4791
4999
  | `createMcpHandler` | function | a complete Streamable-HTTP MCP server — [guide](../guide/mcp-and-agents.md#mcp--createmcphandler) |
4792
5000
  | `createStdioMcpServer` | function | a complete stdio MCP server — [guide](../guide/mcp-and-agents.md#mcp-over-stdio--createstdiomcpserver) |
4793
- | `buildMcpServer` | function | build an `McpServer` from contracts — the transport-neutral core |
5001
+ | `buildMcpServer` | function | build an `McpServer` from contract/runtime surfaces — the transport-neutral core |
4794
5002
  | `mountMcp` | function | add contract tools to an existing `McpServer` — [guide](../guide/mcp-and-agents.md#mountmcp) |
4795
5003
  | `implementRemote` | function | bind a contract to a remote HTTP API — [guide](../guide/mcp-and-agents.md#proxying-a-remote-api--implementremote) |
4796
5004
  | `mountAgent` | function | a Vercel AI SDK `ToolSet` from a service — [guide](../guide/mcp-and-agents.md#ai-agents--mountagent) |
@@ -4801,16 +5009,22 @@ Server-only. Turns contracts into MCP and AI-agent tools. Needs the
4801
5009
  | `mountViewFile` | function | a native multimodal "view file" MCP tool |
4802
5010
  | `resolveMedia` | function | resolve a media reference for a tool result |
4803
5011
  | `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)) |
4804
- | `listToolNames` | function | every mounted tool name with its `(service, method)` identity — for name-baseline snapshots — [guide](../guide/mcp-and-agents.md#pinning-tool-names--listtoolnames) |
5012
+ | `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) |
4805
5013
  | `McpHandlerConfig` | _type_ | config for `createMcpHandler` |
5014
+ | `McpHttpConfig` | _type_ | HTTP auth, protected-resource and session options composed into `McpHandlerConfig` |
4806
5015
  | `McpSessionMode` | _type_ | `'stateless' \| 'stateful'`; HTTP defaults to request-isolated stateless mode |
4807
5016
  | `StdioMcpServerConfig` | _type_ | config for `createStdioMcpServer` |
4808
5017
  | `McpServerBuildConfig` | _type_ | shared config for `buildMcpServer` |
5018
+ | `McpServerSharedConfig` | _type_ | transport-neutral options shared by direct and finite surface configs |
5019
+ | `DirectMcpSurfaceConfig` | _type_ | static or identity-dynamic `services` / `runtimeTools` source |
5020
+ | `FiniteMcpSurfaceConfig` | _type_ | bounded `surfaces` registry plus typed selector |
5021
+ | `McpSurfaceDefinition` | _type_ | one immutable `{ services, runtimeTools }` MCP surface |
5022
+ | `McpSurfaceRegistry` | _type_ | finite keyed surface registry for eager preparation |
5023
+ | `StdioAuthConfig` | _type_ | startup identity composed into `StdioMcpServerConfig` |
4809
5024
  | `ImplementRemoteOptions` | _type_ | options for `implementRemote` |
4810
5025
  | `McpMountConfig` | _type_ | config for `mountMcp` |
4811
5026
  | `McpSchemaValidationConfig` | _type_ | shared `{ policy, requireTypedProperties, allowUntyped, requirePortableFormats, allowFormats }` profile |
4812
5027
  | `ValidateMcpSchemasConfig` | _type_ | standalone validation profile plus `services`, `extend`, flattening and logger |
4813
- | `NativeMcpRegistrar` | _type_ | protected `registerTool` plus explicit unprotected `rawServer` access |
4814
5028
  | `RuntimeToolDefinition` | _type_ | transport-neutral pathless operation with identity, schemas, handler and optional presenters |
4815
5029
  | `RuntimeToolDefinitionBase` | _type_ | common name, identity, input, exposure and MCP metadata fields |
4816
5030
  | `RuntimeToolDefinitionWithOutput` | _type_ | runtime definition whose handler and presenters share a validated output type |
@@ -4850,7 +5064,7 @@ Server-only. Turns contracts into MCP and AI-agent tools. Needs the
4850
5064
  | `findNonPortableFormats` | function | deep finder for formats outside the portable MCP/AJV baseline |
4851
5065
  | `NonPortableFormat` | _type_ | one `{ path, format }` portability finding |
4852
5066
  | `PORTABLE_JSON_SCHEMA_FORMATS` | constant | portable-format baseline used by MCP validation |
4853
- | `ToolNameEntry` | _type_ | one `listToolNames` row — `{ name, service, method, transports }` |
5067
+ | `ToolNameEntry` | _type_ | one `listToolNames` row — `{ kind, name, service, method, transports }` |
4854
5068
  | `IncompatibleSchemaPolicy` | _type_ | `'throw' \| 'skip' \| 'warn'` |
4855
5069
  | `McpMediaContent` | _type_ | a multimodal MCP content item |
4856
5070
 
@@ -4909,12 +5123,15 @@ Advanced building blocks — the shared machinery the mounts are built on.
4909
5123
  |--------|------|---------|
4910
5124
  | `collectTools` | function | resolve a service's methods to mountable tools (the shared resolver) |
4911
5125
  | `createToolLogger` | function | a ready `afterToolCall` that logs every tool call — [guide](../guide/mcp-and-agents.md#logging-tool-calls--createtoollogger) |
4912
- | `summarizeTransports` | function | per-transport operation counts for a boot-time summary |
4913
- | `buildToolManifest` | function | a searchable `{ name, description, inputSchema }` manifest for a `tool_search` tool |
5126
+ | `summarizeTransports` | function | mixed contract/runtime operation counts and per-source breakdown for a boot-time summary |
5127
+ | `buildToolManifest` | function | transport-aware searchable `{ name, description, inputSchema }` rows from a mixed surface |
4914
5128
  | `ToolLoggerConfig` | _type_ | config for `createToolLogger` |
4915
5129
  | `ToolCallRecord` | _type_ | the structured record `createToolLogger` passes to `onRecord` |
4916
- | `TransportSummary` | _type_ | the result of `summarizeTransports` |
5130
+ | `TransportSummary` | _type_ | `{ contractServices, runtimeTools, totals, sources }` from `summarizeTransports` |
4917
5131
  | `TransportCounts` | _type_ | per-transport counts (`{ HTTP, MCP, AGENT, CLI }`) |
5132
+ | `ToolSurfaceDefinition` | _type_ | shared object-shaped `{ services?, runtimeTools? }` introspection surface |
5133
+ | `ToolSurfaceTransport` | _type_ | tool collector transport: `'MCP' \| 'AGENT' \| 'CLI'` |
5134
+ | `ToolManifestConfig` | _type_ | mixed surface plus required model-facing `transport` and presentation options |
4918
5135
  | `coerceJsonArgs` | function | coerce JSON-stringified array/object tool arguments |
4919
5136
  | `flattenToolJsonSchema` | function | project structurally identifiable discriminated unions in a JSON Schema document into conservative object joins; never executes validation |
4920
5137
  | `ToolPresentationSchema` | _type_ | immutable model-facing JSON Schema document shared by tool transports |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.41.0",
3
+ "version": "0.42.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",