@cyanheads/mcp-ts-core 0.8.4 → 0.8.5

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.
package/CLAUDE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Agent Protocol
2
2
 
3
- **Package:** `@cyanheads/mcp-ts-core` · **Version:** 0.8.4
3
+ **Package:** `@cyanheads/mcp-ts-core` · **Version:** 0.8.5
4
4
  **npm:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) · **Docker:** [ghcr.io/cyanheads/mcp-ts-core](https://ghcr.io/cyanheads/mcp-ts-core)
5
5
 
6
6
  > **Developer note:** Never assume. Read related files and docs before making changes. Read full file content for context. Never edit a file before reading it.
@@ -23,7 +23,7 @@
23
23
 
24
24
  | Subpath | Key Exports | Purpose |
25
25
  |:--------|:------------|:--------|
26
- | `@cyanheads/mcp-ts-core` | `createApp`, `tool`, `resource`, `prompt`, `appTool`, `appResource`, `APP_RESOURCE_MIME_TYPE`, `Context`, `createFail`, `TypedFail`, `ReasonOf`, `HandlerContext`, `z` | Main entry point |
26
+ | `@cyanheads/mcp-ts-core` | `createApp`, `tool`, `resource`, `prompt`, `appTool`, `appResource`, `APP_RESOURCE_MIME_TYPE`, `Context`, `createFail`, `createRecoveryFor`, `TypedFail`, `TypedRecoveryFor`, `ReasonOf`, `HandlerContext`, `z` | Main entry point |
27
27
  | `/worker` | `createWorkerHandler`, `CloudflareBindings` | Cloudflare Workers entry |
28
28
  | `/tools` | `ToolDefinition`, `AnyToolDefinition`, `ToolAnnotations` | Tool definition types |
29
29
  | `/resources` | `ResourceDefinition`, `AnyResourceDefinition` | Resource definition types |
@@ -299,6 +299,7 @@ interface Context {
299
299
  readonly signal: AbortSignal; // cancellation
300
300
  readonly progress?: ContextProgress; // present when task: true
301
301
  readonly uri?: URL; // present for resource handlers
302
+ recoveryFor(reason: string): { recovery: { hint: string } } | {}; // opt-in contract resolver
302
303
  }
303
304
  ```
304
305
 
@@ -320,7 +321,13 @@ const values = await ctx.state.getMany<Item>(['item:1', 'item:2']); // Map<strin
320
321
  const page = await ctx.state.list('item:', { cursor, limit: 20 }); // { items, cursor? }
321
322
  ```
322
323
 
323
- Throws `McpError(InvalidRequest)` if `tenantId` missing. Tenant ID from JWT `'tid'` claim (HTTP) or `'default'` (stdio).
324
+ Throws `McpError(InvalidRequest)` if `tenantId` missing. Tenant ID resolution:
325
+
326
+ | Mode | `tenantId` source |
327
+ |:-----|:------------------|
328
+ | stdio (any auth) | `'default'` |
329
+ | HTTP + `MCP_AUTH_MODE=none` | `'default'` (single-tenant by design) |
330
+ | HTTP + `MCP_AUTH_MODE=jwt`/`oauth` | JWT `'tid'` claim — fail-closed if absent |
324
331
 
325
332
  ### `ctx.elicit` / `ctx.sample`
326
333
 
@@ -345,7 +352,7 @@ See `api-context` skill for full details.
345
352
 
346
353
  ## Error Handling
347
354
 
348
- **Recommended path: declare a typed error contract.** Add `errors: [{ reason, code, when, recovery, retryable? }]` to `tool()` / `resource()`. The handler then receives `ctx.fail(reason, msg?, data?)` typed against the reason union — `ctx.fail('typo')` is a TypeScript error. The runtime auto-populates `data.reason` for observability and the linter enforces conformance against the handler body. The `recovery` field is required, descriptive metadata for the agent's next move; for the wire payload's `data.recovery.hint` (which the framework mirrors into `content[]` text), pass it explicitly at the throw site when dynamic context matters: `ctx.fail('reason', msg, { recovery: { hint: '...' } })`.
355
+ **Recommended path: declare a typed error contract.** Add `errors: [{ reason, code, when, recovery, retryable? }]` to `tool()` / `resource()`. The handler then receives `ctx.fail(reason, msg?, data?)` typed against the reason union — `ctx.fail('typo')` is a TypeScript error. The runtime auto-populates `data.reason` for observability and the linter enforces conformance against the handler body. The `recovery` field is required (≥5 words, lint-validated) it's the single source of truth for the recovery hint that flows to the wire. Spread `ctx.recoveryFor('reason')` into `data` to opt the contract recovery onto the wire (the framework mirrors `data.recovery.hint` into `content[]` text). Override with explicit `{ recovery: { hint: '...' } }` when runtime context matters.
349
356
 
350
357
  ```ts
351
358
  errors: [
@@ -357,11 +364,18 @@ errors: [
357
364
  recovery: 'Wait 30 seconds before retrying or reduce batch size.' },
358
365
  ],
359
366
  async handler(input, ctx) {
360
- if (queue.full()) throw ctx.fail('queue_full');
361
- // ...
367
+ // Static recovery — pulled from the contract via ctx.recoveryFor.
368
+ if (queue.full()) throw ctx.fail('queue_full', undefined, { ...ctx.recoveryFor('queue_full') });
369
+ // Dynamic recovery — interpolate runtime context, override the contract default.
370
+ if (!matched) throw ctx.fail('no_match', `No data for ${input.pmids.length} PMIDs`, {
371
+ pmids: input.pmids,
372
+ recovery: { hint: `Use pubmed_search_articles to discover valid PMIDs.` },
373
+ });
362
374
  }
363
375
  ```
364
376
 
377
+ **`ctx.recoveryFor(reason)`** is the first member of a planned family of opt-in resolution helpers (future: `troubleshootingFor`, `userMessageFor`, …). Always present on `Context` (returns `{}` when no contract is attached or the reason is unknown — spread-safe), strictly typed on `HandlerContext<R>` against the declared reason union. The same resolver works in services that accept `ctx`: `throw validationError(msg, { reason: 'X', ...ctx.recoveryFor('X') })`. No auto-population — author opts in by typing the helper.
378
+
365
379
  The contract describes the **public failure surface** — declare domain-specific failures only. **Baseline codes** (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble from anywhere and are auto-allowed by the conformance lint, so you don't need to enumerate them per-tool. The conformance lint scans handler source text only — failures thrown from called services aren't visible to it (still reach the client correctly via the auto-classifier, just without lint enforcement).
366
380
 
367
381
  **Fallback for ad-hoc throws** (no contract entry fits, prototype tools, service-layer code): use error factories.
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  <div align="center">
7
7
 
8
- [![Version](https://img.shields.io/badge/Version-0.8.4-blue.svg?style=flat-square)](./CHANGELOG.md) [![MCP Spec](https://img.shields.io/badge/MCP%20Spec-2025--11--25-8A2BE2.svg?style=flat-square)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-11-25/changelog.mdx) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.29.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE)
8
+ [![Version](https://img.shields.io/badge/Version-0.8.5-blue.svg?style=flat-square)](./CHANGELOG.md) [![MCP Spec](https://img.shields.io/badge/MCP%20Spec-2025--11--25-8A2BE2.svg?style=flat-square)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-11-25/changelog.mdx) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.29.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE)
9
9
 
10
10
  [![TypeScript](https://img.shields.io/badge/TypeScript-^6.0.3-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.3.2-blueviolet.svg?style=flat-square)](https://bun.sh/)
11
11
 
@@ -182,7 +182,7 @@ Handlers receive a unified `Context` object:
182
182
  | `ctx.notifyResourceListChanged` | `Function?` | Notify clients the resource list changed |
183
183
  | `ctx.progress` | `ContextProgress?` | Task progress reporting (when `task: true`) |
184
184
  | `ctx.requestId` | `string` | Unique request ID |
185
- | `ctx.tenantId` | `string?` | Tenant ID (from JWT or `'default'` for stdio) |
185
+ | `ctx.tenantId` | `string?` | Tenant ID (JWT `tid` claim, or `'default'` for stdio and HTTP+`MCP_AUTH_MODE=none`) |
186
186
 
187
187
  ### Subpath exports
188
188
 
@@ -0,0 +1,42 @@
1
+ ---
2
+ summary: "HTTP+MCP_AUTH_MODE=none defaults tenantId to 'default' so ctx.state works without minting JWTs. New ctx.recoveryFor opt-in contract resolver carries the contract recovery onto the wire — single source of truth for handler and service throws."
3
+ breaking: false
4
+ ---
5
+
6
+ # 0.8.5 — 2026-04-29
7
+
8
+ Two changes shipped together. `MCP_AUTH_MODE=none` over HTTP now behaves as the documented "single-tenant, no auth" mode (closes [#87](https://github.com/cyanheads/mcp-ts-core/issues/87)). And the contract `recovery` field gets a typed runtime resolver — `ctx.recoveryFor(reason)` — that carries the lint-validated string onto the wire without authoring duplication, while preserving the "no auto-population, no hidden transformation" stance from [#85](https://github.com/cyanheads/mcp-ts-core/issues/85) (closes [#89](https://github.com/cyanheads/mcp-ts-core/issues/89), supersedes [#88](https://github.com/cyanheads/mcp-ts-core/issues/88)).
9
+
10
+ ## Added
11
+
12
+ - **`ctx.recoveryFor(reason)`** (`src/core/context.ts`) — opt-in contract resolver. Returns the canonical wire shape `{ recovery: { hint } }` for declared reasons; returns `{}` when no contract is attached or the reason is unknown. Always present on `Context` (spread-safe with no optional chaining); strictly typed on `HandlerContext<R>` against the declared reason union. Same resolver works in services that accept `ctx`.
13
+ - **`createRecoveryFor(errors)`** (`src/core/context.ts`) — internal builder, mirrors `createFail`. Public re-export from `@cyanheads/mcp-ts-core` for advanced use.
14
+ - **`TypedRecoveryFor<R>`** type alias for the strict signature, exported alongside `TypedFail<R>`.
15
+ - **First member of a planned family of opt-in resolution helpers.** Future contract-bound fields (`troubleshootingFor`, `userMessageFor`, …) follow the same shape: single-purpose method, spreadable wire-shape return, `{}` fallback, strict variant on `HandlerContext<R>`.
16
+
17
+ ## Fixed
18
+
19
+ - **`createContext`** (`src/core/context.ts`) — `tenantId` defaults to `'default'` when `MCP_AUTH_MODE=none`, regardless of transport. Stdio behavior unchanged. HTTP+`jwt`/`oauth` without `tid` still fails closed (security invariant: distinct authenticated callers must not silently share state).
20
+ - **`ctx.state` error message** — references `MCP_AUTH_MODE` semantics instead of stale "via createApp()" wording.
21
+
22
+ ## Changed
23
+
24
+ - **`HandlerContext<R>`** — when `R` is a literal union, the type now uses `Omit<Context, 'recoveryFor'> & { fail: TypedFail<R>; recoveryFor: TypedRecoveryFor<R> }` (intersecting the loose `Context.recoveryFor` would widen `parameter(0)` back to `string` and lose typo-catching). Behavior unchanged for callers; type is strictly narrower for declared reasons.
25
+ - **`attachTypedFail`** (`src/core/context.ts`) — wires both `fail` and `recoveryFor` when the definition declares an `errors[]` contract. Bare `Context` carries a no-op `recoveryFor` so service-side spreads are always safe.
26
+
27
+ ## Docs
28
+
29
+ - `AGENTS.md` / `CLAUDE.md` / `README.md` — `tenantId` resolution table covers all three modes; error handling section and Context interface documents `ctx.recoveryFor`; exports table includes `createRecoveryFor`, `TypedRecoveryFor`.
30
+ - `skills/api-context/SKILL.md` v1.1 → v1.2 — new `ctx.recoveryFor` section with signature, behavior table, and family-pattern note; identity field table and `ctx.state` notes carry the auth-none default.
31
+ - `skills/api-errors/SKILL.md` v1.3 → v1.4 — opt-in resolution subsection replaces the const-pattern guidance; gap callout removed; service-layer pattern updated.
32
+ - `skills/api-auth/SKILL.md` — `tenantId` sources table covers HTTP+`none`.
33
+ - `skills/add-tool/SKILL.md` v2.3 → v2.4 — template demonstrates static (`...ctx.recoveryFor('reason')`) and dynamic (inline `{ recovery: { hint } }`) recovery side by side; service-layer subsection now passes `ctx` and uses the resolver.
34
+ - `skills/add-service/SKILL.md` v1.4 → v1.5 — service-side spread pattern with `...ctx.recoveryFor('reason')`.
35
+
36
+ ## Security note
37
+
38
+ HTTP+`MCP_AUTH_MODE=none` is **single-tenant by design** — all unauthenticated callers share the `'default'` tenant. Servers requiring per-caller isolation must use `MCP_AUTH_MODE=jwt` or `oauth` with tokens that carry a `tid` claim.
39
+
40
+ ## Design principle (#85 / #88 / #89)
41
+
42
+ `ctx.recoveryFor` is **opt-in resolution**, not auto-population. The framework never attaches `data.recovery.hint` without an explicit signal at the throw site — authors opt in by typing the helper, the same way `ctx.fail('reason')` opts into resolving the contract `code`. The contract is the single source of truth for the recovery hint; the resolver is a typed lookup keyed by the same reason the author already typed. No magic, no hidden transformation. The `≥5 words` lint rule on contract `recovery` becomes load-bearing — every `ctx.recoveryFor` call site benefits from the thoughtfulness the contract enforced.
@@ -96,6 +96,31 @@ export interface Context {
96
96
  readonly notifyResourceUpdated?: ((uri: string) => void) | undefined;
97
97
  /** Progress reporting for background tasks. Undefined for non-task tools. */
98
98
  readonly progress?: ContextProgress | undefined;
99
+ /**
100
+ * Resolves a contract-bound recovery hint by reason and returns it in the
101
+ * canonical wire shape `{ recovery: { hint } }` — safe to spread directly into
102
+ * factory `data` or `ctx.fail`'s data argument:
103
+ *
104
+ * ```ts
105
+ * throw ctx.fail('parse_failed', `Parse error: ${err.message}`, ctx.recoveryFor('parse_failed'));
106
+ * throw validationError('Parse failed', { reason: 'parse_failed', ...ctx.recoveryFor('parse_failed') });
107
+ * ```
108
+ *
109
+ * Returns `{}` when the calling definition has no `errors[]` contract, or the
110
+ * reason isn't declared in it. Always safe to spread — no optional chaining
111
+ * needed at call sites. The strict, typed variant (which guarantees the
112
+ * non-empty return) lives on `HandlerContext<R>` when a contract is declared.
113
+ *
114
+ * Single source of truth pattern: write the recovery once in the contract entry
115
+ * (lint-validated for ≥5 words), reference it everywhere via this resolver.
116
+ * Authors who want runtime-context recovery (interpolating input values, IDs,
117
+ * queue state) override at the throw site as today.
118
+ */
119
+ recoveryFor(reason: string): {
120
+ recovery: {
121
+ hint: string;
122
+ };
123
+ } | Record<string, never>;
99
124
  /** Unique request ID for log correlation. */
100
125
  readonly requestId: string;
101
126
  /** Request LLM completion from the client. Present when client supports sampling. */
@@ -106,7 +131,13 @@ export interface Context {
106
131
  readonly spanId?: string | undefined;
107
132
  /** Key-value state scoped to the current tenant. Throws if tenantId is missing. */
108
133
  readonly state: ContextState;
109
- /** Tenant ID — from JWT 'tid' claim (HTTP) or 'default' (stdio). */
134
+ /**
135
+ * Tenant ID. Sources, in order:
136
+ * - JWT `tid` claim — HTTP with `MCP_AUTH_MODE=jwt`/`oauth`
137
+ * - `'default'` — stdio (any auth mode) or HTTP with `MCP_AUTH_MODE=none`
138
+ * - `undefined` — HTTP with `jwt`/`oauth` when the token lacks `tid`
139
+ * (fail-closed: `ctx.state` will throw rather than silently share state)
140
+ */
110
141
  readonly tenantId?: string | undefined;
111
142
  /** ISO 8601 creation time. */
112
143
  readonly timestamp: string;
@@ -145,17 +176,39 @@ export type TypedFail<R extends string> = (reason: R, message?: string, data?: R
145
176
  export type ReasonOf<E> = E extends readonly {
146
177
  reason: infer R extends string;
147
178
  }[] ? string extends R ? never : R : never;
179
+ /**
180
+ * Strict, typed contract-resolver signature attached to `HandlerContext<R>`
181
+ * when a definition declares `errors[]`. Tightens the loose `Context.recoveryFor`
182
+ * to:
183
+ * - `reason` is constrained to the declared union (TS catches typos)
184
+ * - return is the non-empty wire shape (no fallback `{}` branch)
185
+ *
186
+ * Family of opt-in resolution helpers — future contract-bound fields
187
+ * (`troubleshootingFor`, `userMessageFor`, …) follow the same shape.
188
+ */
189
+ export type TypedRecoveryFor<R extends string> = (reason: R) => {
190
+ recovery: {
191
+ hint: string;
192
+ };
193
+ };
148
194
  /**
149
195
  * Handler context. When a definition declares `errors[]`, the handler receives
150
- * `Context & { fail: TypedFail<R> }` where `R` is the declared reason union.
151
- * When no contract is declared, the handler receives plain `Context` and must
152
- * throw `McpError` directly.
196
+ * `Context & { fail: TypedFail<R>; recoveryFor: TypedRecoveryFor<R> }` where
197
+ * `R` is the declared reason union. When no contract is declared, the handler
198
+ * receives plain `Context` (its loose `recoveryFor` is still callable but
199
+ * always returns `{}`) and must throw `McpError` directly.
153
200
  *
154
201
  * The conditional `[R] extends [never]` distinguishes "no contract declared"
155
202
  * (R = never) from "contract declared with reasons" (R = literal union).
203
+ *
204
+ * The contract branch uses `Omit<Context, 'recoveryFor'>` because intersecting
205
+ * the loose `Context.recoveryFor(string)` with the strict `TypedRecoveryFor<R>`
206
+ * would create an overload that widens `parameter(0)` back to `string` — losing
207
+ * the typo-catching benefit. Omit-and-replace narrows cleanly.
156
208
  */
157
- export type HandlerContext<R extends string = never> = [R] extends [never] ? Context : Context & {
209
+ export type HandlerContext<R extends string = never> = [R] extends [never] ? Context : Omit<Context, 'recoveryFor'> & {
158
210
  fail: TypedFail<R>;
211
+ recoveryFor: TypedRecoveryFor<R>;
159
212
  };
160
213
  /**
161
214
  * @internal
@@ -174,9 +227,27 @@ export type HandlerContext<R extends string = never> = [R] extends [never] ? Con
174
227
  */
175
228
  export declare function createFail(errors: readonly ErrorContract[]): TypedFail<string>;
176
229
  /**
177
- * Attaches a typed `fail` helper to `ctx` when the definition declares an
178
- * error contract; otherwise returns `ctx` unchanged. Used by the tool and
179
- * resource handler factories so all call sites stay identical.
230
+ * Builds a runtime `recoveryFor` resolver for a given contract. Looks up the
231
+ * entry by `reason` and returns the canonical wire shape `{ recovery: { hint } }`,
232
+ * safe to spread into factory `data` or `ctx.fail`'s data argument.
233
+ *
234
+ * Returns `{}` when the reason isn't declared — at runtime this protects JS
235
+ * callers and stale contracts; at compile time `TypedRecoveryFor<R>` constrains
236
+ * the reason to declared values. The `{}` fallback also keeps the bare-Context
237
+ * resolver (no contract attached) safe to spread without optional chaining.
238
+ *
239
+ * @internal
240
+ */
241
+ export declare function createRecoveryFor(errors: readonly ErrorContract[]): (reason: string) => {
242
+ recovery: {
243
+ hint: string;
244
+ };
245
+ } | Record<string, never>;
246
+ /**
247
+ * Attaches a typed `fail` and `recoveryFor` helper to `ctx` when the definition
248
+ * declares an error contract. The bare `Context.recoveryFor` (always present
249
+ * via `createContext`) is overwritten with a contract-aware resolver. Used by
250
+ * the tool and resource handler factories so all call sites stay identical.
180
251
  *
181
252
  * @internal
182
253
  */
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AACrF,OAAO,KAAK,EACV,mBAAmB,EACnB,YAAY,EACZ,gBAAgB,EAChB,eAAe,EAChB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAEtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EACL,KAAK,aAAa,EAGlB,QAAQ,EACT,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAGtF,YAAY,EAAE,WAAW,EAAE,CAAC;AAM5B;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACzD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1D,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC5D;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,oBAAoB;IACpB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,gEAAgE;IAChE,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,qDAAqD;IACrD,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACjD,iFAAiF;IACjF,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC3D,4EAA4E;IAC5E,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9D,2CAA2C;IAC3C,IAAI,CACF,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GACzC,OAAO,CAAC;QACT,KAAK,EAAE,KAAK,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;QAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IACH,qDAAqD;IACrD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,6BAA6B;IAC7B,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChF;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,uDAAuD;IACvD,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,4CAA4C;IAC5C,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,mCAAmC;IACnC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC;AAED,qCAAqC;AACrC,MAAM,WAAW,YAAY;IAC3B,cAAc,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,YAAY,CAAC;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAMD;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAGxC,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,CAAC,EACZ,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC,GAChF,SAAS,CAAC;IAGd,iFAAiF;IACjF,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC;IAG5B,mFAAmF;IACnF,QAAQ,CAAC,yBAAyB,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAC9D,kEAAkE;IAClE,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAGrE,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IAEhD,6CAA6C;IAC7C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,qFAAqF;IACrF,QAAQ,CAAC,MAAM,CAAC,EACZ,CAAC,CAAC,QAAQ,EAAE,eAAe,EAAE,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC,GACpF,SAAS,CAAC;IAGd,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,6CAA6C;IAC7C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAGrC,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,8BAA8B;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAGtC,qEAAqE;IACrE,QAAQ,CAAC,GAAG,CAAC,EAAE,GAAG,GAAG,SAAS,CAAC;CAChC;AAMD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,MAAM,IAAI,CACxC,MAAM,EAAE,CAAC,EACT,OAAO,CAAC,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,KAC1B,QAAQ,CAAC;AAEd;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,SAAS;IAAE,MAAM,EAAE,MAAM,CAAC,SAAS,MAAM,CAAA;CAAE,EAAE,GAC7E,MAAM,SAAS,CAAC,GACd,KAAK,GACL,CAAC,GACH,KAAK,CAAC;AAEV;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,MAAM,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACtE,OAAO,GACP,OAAO,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;CAAE,CAAC;AAErC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,CAsB9E;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,SAAS,GAC3C,OAAO,CAGT;AAMD,gBAAgB;AAChB,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,cAAc,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,yBAAyB,CAAC,EAAE,OAAO,CAAC,2BAA2B,CAAC,CAAC;IACjE,qBAAqB,CAAC,EAAE,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACzD,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,cAAc,CAAC;IACxB,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,gBAAgB,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IAClE,GAAG,CAAC,EAAE,GAAG,GAAG,SAAS,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAoCxD"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"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"}
@@ -40,16 +40,43 @@ export function createFail(errors) {
40
40
  };
41
41
  }
42
42
  /**
43
- * Attaches a typed `fail` helper to `ctx` when the definition declares an
44
- * error contract; otherwise returns `ctx` unchanged. Used by the tool and
45
- * resource handler factories so all call sites stay identical.
43
+ * Builds a runtime `recoveryFor` resolver for a given contract. Looks up the
44
+ * entry by `reason` and returns the canonical wire shape `{ recovery: { hint } }`,
45
+ * safe to spread into factory `data` or `ctx.fail`'s data argument.
46
+ *
47
+ * Returns `{}` when the reason isn't declared — at runtime this protects JS
48
+ * callers and stale contracts; at compile time `TypedRecoveryFor<R>` constrains
49
+ * the reason to declared values. The `{}` fallback also keeps the bare-Context
50
+ * resolver (no contract attached) safe to spread without optional chaining.
51
+ *
52
+ * @internal
53
+ */
54
+ export function createRecoveryFor(errors) {
55
+ const byReason = new Map();
56
+ for (const entry of errors)
57
+ byReason.set(entry.reason, entry);
58
+ return (reason) => {
59
+ const entry = byReason.get(reason);
60
+ if (!entry)
61
+ return {};
62
+ return { recovery: { hint: entry.recovery } };
63
+ };
64
+ }
65
+ /**
66
+ * Attaches a typed `fail` and `recoveryFor` helper to `ctx` when the definition
67
+ * declares an error contract. The bare `Context.recoveryFor` (always present
68
+ * via `createContext`) is overwritten with a contract-aware resolver. Used by
69
+ * the tool and resource handler factories so all call sites stay identical.
46
70
  *
47
71
  * @internal
48
72
  */
49
73
  export function attachTypedFail(ctx, errors) {
50
74
  if (!errors || errors.length === 0)
51
75
  return ctx;
52
- return Object.assign(ctx, { fail: createFail(errors) });
76
+ return Object.assign(ctx, {
77
+ fail: createFail(errors),
78
+ recoveryFor: createRecoveryFor(errors),
79
+ });
53
80
  }
54
81
  /**
55
82
  * Constructs a Context from internal dependencies.
@@ -58,13 +85,17 @@ export function attachTypedFail(ctx, errors) {
58
85
  */
59
86
  export function createContext(deps) {
60
87
  const { appContext, logger: pinoLogger, storage, signal } = deps;
61
- // Default tenantId to 'default' only for stdio (single-client, no auth).
62
- // HTTP without auth leaves tenantId unset ctx.state will fail-closed
63
- // via requireContext() to prevent unauthenticated callers sharing state.
88
+ // Default tenantId to 'default' when no auth pipeline is expected to populate it:
89
+ // - stdio: single-client by nature, no auth middleware runs
90
+ // - HTTP + MCP_AUTH_MODE=none: single-tenant by design (auth=none means
91
+ // "no identity check, sharing is intentional")
92
+ // Preserves fail-closed for HTTP + jwt/oauth: a token missing the `tid` claim
93
+ // must NOT silently share state across distinct authenticated callers.
64
94
  const isStdio = process.env.MCP_TRANSPORT_TYPE?.toLowerCase() !== 'http';
95
+ const isAuthDisabled = (process.env.MCP_AUTH_MODE ?? 'none').toLowerCase() === 'none';
65
96
  const effectiveContext = appContext.tenantId
66
97
  ? appContext
67
- : isStdio
98
+ : isStdio || isAuthDisabled
68
99
  ? { ...appContext, tenantId: 'default' }
69
100
  : appContext;
70
101
  const log = createContextLogger(pinoLogger, effectiveContext);
@@ -88,6 +119,12 @@ export function createContext(deps) {
88
119
  notifyResourceUpdated: deps.notifyResourceUpdated,
89
120
  progress,
90
121
  uri: deps.uri,
122
+ // No-op resolver for definitions without a contract. `attachTypedFail`
123
+ // overwrites with a contract-aware resolver when `errors[]` is declared.
124
+ // Always-present so service code can spread `...ctx.recoveryFor('x')`
125
+ // without optional chaining, regardless of whether the calling tool
126
+ // declared a contract.
127
+ recoveryFor: () => ({}),
91
128
  };
92
129
  }
93
130
  // ---------------------------------------------------------------------------
@@ -127,7 +164,7 @@ function createContextLogger(appLogger, appContext) {
127
164
  function createContextState(storage, appContext, signal) {
128
165
  const requireContext = () => {
129
166
  if (!appContext.tenantId) {
130
- throw invalidRequest('tenantId required for state operations. HTTP requests must include a JWT with a "tid" claim. Stdio mode should default tenantId to "default" via createApp().');
167
+ throw invalidRequest('tenantId required for state operations. With MCP_AUTH_MODE=jwt|oauth, the token must include a "tid" claim. Stdio and HTTP+MCP_AUTH_MODE=none default tenantId to "default" automatically.');
131
168
  }
132
169
  return appContext;
133
170
  };
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/core/context.ts"],"names":[],"mappings":"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"}
@@ -9,8 +9,8 @@ export type { CoreServices, CreateAppOptions, ServerHandle } from '../core/app.j
9
9
  export { createApp } from '../core/app.js';
10
10
  export type { LandingConfig, LandingLink } from '../core/serverManifest.js';
11
11
  export { z } from 'zod';
12
- export type { AuthContext, Context, ContextLogger, ContextProgress, ContextState, HandlerContext, ReasonOf, SamplingOpts, TypedFail, } from '../core/context.js';
13
- export { createFail } from '../core/context.js';
12
+ export type { AuthContext, Context, ContextLogger, ContextProgress, ContextState, HandlerContext, ReasonOf, SamplingOpts, TypedFail, TypedRecoveryFor, } from '../core/context.js';
13
+ export { createFail, createRecoveryFor } from '../core/context.js';
14
14
  export { APP_RESOURCE_MIME_TYPE, appResource, appTool } from '../mcp-server/apps/appBuilders.js';
15
15
  export type { AnyPromptDefinition, PromptDefinition, } from '../mcp-server/prompts/utils/promptDefinition.js';
16
16
  export { prompt } from '../mcp-server/prompts/utils/promptDefinition.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClF,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAM1C,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAM3E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,YAAY,EACV,WAAW,EACX,OAAO,EACP,aAAa,EACb,eAAe,EACf,YAAY,EACZ,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,SAAS,GACV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAM/C,OAAO,EAAE,sBAAsB,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAChG,YAAY,EACV,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,gDAAgD,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,gDAAgD,CAAC;AACxE,YAAY,EACV,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,oDAAoD,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,oDAAoD,CAAC;AAC9E,sEAAsE;AACtE,YAAY,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAC;AAC1E,YAAY,EACV,iBAAiB,EACjB,eAAe,EACf,cAAc,GACf,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,4CAA4C,CAAC;AAMlE,YAAY,EACV,kBAAkB,EAClB,cAAc,EACd,SAAS,EACT,UAAU,EACV,YAAY,GACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAM3D,YAAY,EACV,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,eAAe,GAChB,MAAM,oCAAoC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClF,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAM1C,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAM3E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,YAAY,EACV,WAAW,EACX,OAAO,EACP,aAAa,EACb,eAAe,EACf,YAAY,EACZ,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAMlE,OAAO,EAAE,sBAAsB,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAChG,YAAY,EACV,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,gDAAgD,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,gDAAgD,CAAC;AACxE,YAAY,EACV,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,oDAAoD,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,oDAAoD,CAAC;AAC9E,sEAAsE;AACtE,YAAY,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAC;AAC1E,YAAY,EACV,iBAAiB,EACjB,eAAe,EACf,cAAc,GACf,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,4CAA4C,CAAC;AAMlE,YAAY,EACV,kBAAkB,EAClB,cAAc,EACd,SAAS,EACT,UAAU,EACV,YAAY,GACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAM3D,YAAY,EACV,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,eAAe,GAChB,MAAM,oCAAoC,CAAC"}
@@ -10,7 +10,7 @@ export { createApp } from '../core/app.js';
10
10
  // Zod re-export (consumers use the framework's copy, no separate zod dep)
11
11
  // ---------------------------------------------------------------------------
12
12
  export { z } from 'zod';
13
- export { createFail } from '../core/context.js';
13
+ export { createFail, createRecoveryFor } from '../core/context.js';
14
14
  // ---------------------------------------------------------------------------
15
15
  // Definition builders & types
16
16
  // ---------------------------------------------------------------------------
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAQ1C,8EAA8E;AAC9E,0EAA0E;AAC1E,8EAA8E;AAE9E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiBxB,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,8EAA8E;AAC9E,8BAA8B;AAC9B,8EAA8E;AAE9E,OAAO,EAAE,sBAAsB,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAKhG,OAAO,EAAE,MAAM,EAAE,MAAM,gDAAgD,CAAC;AAKxE,OAAO,EAAE,QAAQ,EAAE,MAAM,oDAAoD,CAAC;AAQ9E,OAAO,EAAE,IAAI,EAAE,MAAM,4CAA4C,CAAC;AAalE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAQ1C,8EAA8E;AAC9E,0EAA0E;AAC1E,8EAA8E;AAE9E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAkBxB,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAElE,8EAA8E;AAC9E,8BAA8B;AAC9B,8EAA8E;AAE9E,OAAO,EAAE,sBAAsB,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAKhG,OAAO,EAAE,MAAM,EAAE,MAAM,gDAAgD,CAAC;AAKxE,OAAO,EAAE,QAAQ,EAAE,MAAM,oDAAoD,CAAC;AAQ9E,OAAO,EAAE,IAAI,EAAE,MAAM,4CAA4C,CAAC;AAalE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -1,4 +1,4 @@
1
- {"level":50,"time":1777494206031,"env":"testing","version":"0.0.0-test","pid":62030,"requestId":"2DXYH-6OXOG","timestamp":"2026-04-29T20:23:26.030Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"5ab0ea1c5b4e8d62e734a3ffd5bd6a3f01332ecc57a6395af28d53fec5ab98d6","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"errorData":{"sessionId":"5ab0ea1c5b4e8d62e734a3ffd5bd6a3f01332ecc57a6395af28d53fec5ab98d6","toolName":"scoped_echo","requestId":"2DXYH-6OXOG","timestamp":"2026-04-29T20:23:26.030Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:61:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:133:17)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Insufficient permissions.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:168:26)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
2
- {"level":50,"time":1777494206349,"env":"testing","version":"0.8.4","pid":62034,"requestId":"XIJQG-DQNFH","timestamp":"2026-04-29T20:23:26.348Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"XIJQG-DQNFH","timestamp":"2026-04-29T20:23:26.348Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Missing or invalid Authorization header. Bearer scheme required.","originalStack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at authMiddleware (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/authMiddleware.js:64:19)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpTransport.js:232:22)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at cors2 (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/middleware/cors/index.js:82:11)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Missing or invalid Authorization header. Bearer scheme required."}
3
- {"level":50,"time":1777494206364,"env":"testing","version":"0.8.4","pid":62034,"requestId":"8G1K9-6UPKW","timestamp":"2026-04-29T20:23:26.364Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"8G1K9-6UPKW","timestamp":"2026-04-29T20:23:26.364Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Token has expired.","originalStack":"McpError: Token has expired.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at handleJoseVerifyError (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/claimParser.js:56:11)\n at verify (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/strategies/jwtStrategy.js:91:13)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Token has expired.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Token has expired."}
4
- {"level":50,"time":1777494206368,"env":"testing","version":"0.8.4","pid":62034,"requestId":"IXG6T-KFPFJ","timestamp":"2026-04-29T20:23:26.368Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"GET","errorData":{"path":"/mcp","method":"GET","requestId":"IXG6T-KFPFJ","timestamp":"2026-04-29T20:23:26.368Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Missing or invalid Authorization header. Bearer scheme required.","originalStack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at authMiddleware (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/authMiddleware.js:64:19)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpTransport.js:232:22)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at cors2 (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/middleware/cors/index.js:82:11)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Missing or invalid Authorization header. Bearer scheme required."}
1
+ {"level":50,"time":1777499104535,"env":"testing","version":"0.0.0-test","pid":49575,"requestId":"FOF4T-UOFA7","timestamp":"2026-04-29T21:45:04.534Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"271390f99e5e41f6f6fd906f7f8b64a4df90de625cdb0780923a007a32aa3955","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"errorData":{"sessionId":"271390f99e5e41f6f6fd906f7f8b64a4df90de625cdb0780923a007a32aa3955","toolName":"scoped_echo","requestId":"FOF4T-UOFA7","timestamp":"2026-04-29T21:45:04.534Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:61:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:133:17)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Insufficient permissions.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:168:26)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
2
+ {"level":50,"time":1777499104848,"env":"testing","version":"0.8.5","pid":49577,"requestId":"V1TLT-X9GIK","timestamp":"2026-04-29T21:45:04.847Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"V1TLT-X9GIK","timestamp":"2026-04-29T21:45:04.847Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Missing or invalid Authorization header. Bearer scheme required.","originalStack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at authMiddleware (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/authMiddleware.js:64:19)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpTransport.js:232:22)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at cors2 (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/middleware/cors/index.js:82:11)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Missing or invalid Authorization header. Bearer scheme required."}
3
+ {"level":50,"time":1777499104862,"env":"testing","version":"0.8.5","pid":49577,"requestId":"7VXTK-81LEG","timestamp":"2026-04-29T21:45:04.862Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"7VXTK-81LEG","timestamp":"2026-04-29T21:45:04.862Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Token has expired.","originalStack":"McpError: Token has expired.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at handleJoseVerifyError (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/claimParser.js:56:11)\n at verify (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/strategies/jwtStrategy.js:91:13)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Token has expired.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Token has expired."}
4
+ {"level":50,"time":1777499104867,"env":"testing","version":"0.8.5","pid":49577,"requestId":"X8Y7T-RF36X","timestamp":"2026-04-29T21:45:04.866Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"GET","errorData":{"path":"/mcp","method":"GET","requestId":"X8Y7T-RF36X","timestamp":"2026-04-29T21:45:04.866Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Missing or invalid Authorization header. Bearer scheme required.","originalStack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at authMiddleware (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/authMiddleware.js:64:19)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpTransport.js:232:22)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at cors2 (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/middleware/cors/index.js:82:11)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Missing or invalid Authorization header. Bearer scheme required."}
@@ -1,4 +1,4 @@
1
- {"level":50,"time":1777494206031,"env":"testing","version":"0.0.0-test","pid":62030,"requestId":"2DXYH-6OXOG","timestamp":"2026-04-29T20:23:26.030Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"5ab0ea1c5b4e8d62e734a3ffd5bd6a3f01332ecc57a6395af28d53fec5ab98d6","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"errorData":{"sessionId":"5ab0ea1c5b4e8d62e734a3ffd5bd6a3f01332ecc57a6395af28d53fec5ab98d6","toolName":"scoped_echo","requestId":"2DXYH-6OXOG","timestamp":"2026-04-29T20:23:26.030Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:61:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:133:17)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Insufficient permissions.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:168:26)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
2
- {"level":50,"time":1777494206349,"env":"testing","version":"0.8.4","pid":62034,"requestId":"XIJQG-DQNFH","timestamp":"2026-04-29T20:23:26.348Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"XIJQG-DQNFH","timestamp":"2026-04-29T20:23:26.348Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Missing or invalid Authorization header. Bearer scheme required.","originalStack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at authMiddleware (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/authMiddleware.js:64:19)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpTransport.js:232:22)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at cors2 (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/middleware/cors/index.js:82:11)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Missing or invalid Authorization header. Bearer scheme required."}
3
- {"level":50,"time":1777494206364,"env":"testing","version":"0.8.4","pid":62034,"requestId":"8G1K9-6UPKW","timestamp":"2026-04-29T20:23:26.364Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"8G1K9-6UPKW","timestamp":"2026-04-29T20:23:26.364Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Token has expired.","originalStack":"McpError: Token has expired.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at handleJoseVerifyError (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/claimParser.js:56:11)\n at verify (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/strategies/jwtStrategy.js:91:13)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Token has expired.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Token has expired."}
4
- {"level":50,"time":1777494206368,"env":"testing","version":"0.8.4","pid":62034,"requestId":"IXG6T-KFPFJ","timestamp":"2026-04-29T20:23:26.368Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"GET","errorData":{"path":"/mcp","method":"GET","requestId":"IXG6T-KFPFJ","timestamp":"2026-04-29T20:23:26.368Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Missing or invalid Authorization header. Bearer scheme required.","originalStack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at authMiddleware (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/authMiddleware.js:64:19)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpTransport.js:232:22)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at cors2 (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/middleware/cors/index.js:82:11)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Missing or invalid Authorization header. Bearer scheme required."}
1
+ {"level":50,"time":1777499104535,"env":"testing","version":"0.0.0-test","pid":49575,"requestId":"FOF4T-UOFA7","timestamp":"2026-04-29T21:45:04.534Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"271390f99e5e41f6f6fd906f7f8b64a4df90de625cdb0780923a007a32aa3955","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"errorData":{"sessionId":"271390f99e5e41f6f6fd906f7f8b64a4df90de625cdb0780923a007a32aa3955","toolName":"scoped_echo","requestId":"FOF4T-UOFA7","timestamp":"2026-04-29T21:45:04.534Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:61:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:133:17)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Insufficient permissions.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:168:26)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
2
+ {"level":50,"time":1777499104848,"env":"testing","version":"0.8.5","pid":49577,"requestId":"V1TLT-X9GIK","timestamp":"2026-04-29T21:45:04.847Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"V1TLT-X9GIK","timestamp":"2026-04-29T21:45:04.847Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Missing or invalid Authorization header. Bearer scheme required.","originalStack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at authMiddleware (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/authMiddleware.js:64:19)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpTransport.js:232:22)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at cors2 (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/middleware/cors/index.js:82:11)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Missing or invalid Authorization header. Bearer scheme required."}
3
+ {"level":50,"time":1777499104862,"env":"testing","version":"0.8.5","pid":49577,"requestId":"7VXTK-81LEG","timestamp":"2026-04-29T21:45:04.862Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"7VXTK-81LEG","timestamp":"2026-04-29T21:45:04.862Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Token has expired.","originalStack":"McpError: Token has expired.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at handleJoseVerifyError (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/claimParser.js:56:11)\n at verify (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/strategies/jwtStrategy.js:91:13)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Token has expired.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Token has expired."}
4
+ {"level":50,"time":1777499104867,"env":"testing","version":"0.8.5","pid":49577,"requestId":"X8Y7T-RF36X","timestamp":"2026-04-29T21:45:04.866Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"GET","errorData":{"path":"/mcp","method":"GET","requestId":"X8Y7T-RF36X","timestamp":"2026-04-29T21:45:04.866Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Missing or invalid Authorization header. Bearer scheme required.","originalStack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at authMiddleware (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/authMiddleware.js:64:19)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpTransport.js:232:22)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at cors2 (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/middleware/cors/index.js:82:11)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Missing or invalid Authorization header. Bearer scheme required."}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,mBAAmB,EACnB,YAAY,EACZ,eAAe,EAChB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAW,CAAC,EAAE,MAAM,KAAK,CAAC;AACtC,OAAO,KAAK,EACV,WAAW,EACX,OAAO,EACP,aAAa,EAGb,YAAY,EACb,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,kDAAkD,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAM9D,MAAM,WAAW,kBAAkB;IACjC,oBAAoB;IACpB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,gCAAgC;IAChC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IACxF;;;;;OAKG;IACH,MAAM,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IAClC,2CAA2C;IAC3C,yBAAyB,CAAC,EAAE,MAAM,IAAI,CAAC;IACvC,sCAAsC;IACtC,qBAAqB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,eAAe,EAAE,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC5F,wEAAwE;IACxE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,GAAG,CAAC,EAAE,GAAG,CAAC;CACX;AAMD,iFAAiF;AACjF,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG;IAC9C,mFAAmF;IACnF,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC9D,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,IAAI,iBAAiB,CAiBpD;AAwGD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAwB3E;AAMD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,uBAAuB,GAAG,cAAc,CAEvF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,mBAAmB,EACnB,YAAY,EACZ,eAAe,EAChB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAW,CAAC,EAAE,MAAM,KAAK,CAAC;AACtC,OAAO,KAAK,EACV,WAAW,EACX,OAAO,EACP,aAAa,EAGb,YAAY,EACb,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,kDAAkD,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAM9D,MAAM,WAAW,kBAAkB;IACjC,oBAAoB;IACpB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,gCAAgC;IAChC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IACxF;;;;;OAKG;IACH,MAAM,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IAClC,2CAA2C;IAC3C,yBAAyB,CAAC,EAAE,MAAM,IAAI,CAAC;IACvC,sCAAsC;IACtC,qBAAqB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,eAAe,EAAE,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC5F,wEAAwE;IACxE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,GAAG,CAAC,EAAE,GAAG,CAAC;CACX;AAMD,iFAAiF;AACjF,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG;IAC9C,mFAAmF;IACnF,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC9D,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,IAAI,iBAAiB,CAiBpD;AAwGD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CA4B3E;AAMD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,uBAAuB,GAAG,cAAc,CAEvF"}
@@ -171,9 +171,13 @@ export function createMockContext(options = {}) {
171
171
  notifyResourceUpdated: options.notifyResourceUpdated,
172
172
  progress,
173
173
  uri: options.uri,
174
+ // No-op resolver for definitions without a contract. `attachTypedFail` below
175
+ // overwrites it with a contract-aware resolver when `options.errors` is set.
176
+ recoveryFor: () => ({}),
174
177
  };
175
178
  // Mirror the production handler factory: when a contract is declared, attach
176
- // a typed `fail` keyed by the contract's reasons. Empty contracts no-op.
179
+ // a typed `fail` and `recoveryFor` keyed by the contract's reasons. Empty
180
+ // contracts leave the no-op resolver in place.
177
181
  return attachTypedFail(ctx, options.errors);
178
182
  }
179
183
  // ---------------------------------------------------------------------------
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyanheads/mcp-ts-core",
3
- "version": "0.8.4",
3
+ "version": "0.8.5",
4
4
  "mcpName": "io.github.cyanheads/mcp-ts-core",
5
5
  "description": "Agent-native TypeScript framework for building MCP servers. Declarative definitions with auth, multi-backend storage, OpenTelemetry, and first-class support for Bun/Node/Cloudflare Workers.",
6
6
  "main": "dist/core/index.js",
@@ -4,7 +4,7 @@ description: >
4
4
  Scaffold a new service integration. Use when the user asks to add a service, integrate an external API, or create a reusable domain module with its own initialization and state.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.4"
7
+ version: "1.5"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -237,10 +237,21 @@ Services don't declare `errors: [...]` contracts and don't have `ctx.fail` — t
237
237
  - **Carry contract `reason` via `data: { reason }`** when the calling tool declares an `errors[]` contract entry for this failure mode. Services can't call `ctx.fail`, but passing the reason in `data` flows through the auto-classifier untouched, so clients see the same `error.data.reason` they'd see from `ctx.fail` — no handler-side catch-and-rethrow needed:
238
238
 
239
239
  ```ts
240
- // tool declares: errors: [{ reason: 'empty_expression', code: JsonRpcErrorCode.ValidationError, when: '…' }]
240
+ // tool declares: errors: [{ reason: 'empty_expression', code: JsonRpcErrorCode.ValidationError, when: '…', recovery: '…' }]
241
241
  throw validationError('Expression cannot be empty.', { reason: 'empty_expression' });
242
242
  ```
243
243
 
244
+ - **Resolve contract `recovery` via `ctx.recoveryFor`** to land the contract's recovery hint on the wire without duplicating the string. Always-present on `Context`, returns `{}` when the calling tool has no matching reason — spread-safe regardless:
245
+
246
+ ```ts
247
+ throw validationError('Parse failed: ' + err.message, {
248
+ reason: 'parse_failed',
249
+ ...ctx.recoveryFor('parse_failed'), // resolves from caller's contract
250
+ });
251
+ ```
252
+
253
+ The contract `recovery` (validated ≥5 words at lint time) is the single source of truth. Services that opt in via the resolver carry the same hint to the wire that handler-level `ctx.fail` callers do — no drift, no auto-population. For dynamic recovery (interpolating runtime values into the hint), pass an explicit `{ recovery: { hint: '…' } }` instead.
254
+
244
255
  ## API Efficiency
245
256
 
246
257
  When a service wraps an external API, design methods to minimize upstream calls. These patterns compound — a tool calling 3 service methods that each make N requests is 3N calls; batching drops it to 3.
@@ -4,7 +4,7 @@ description: >
4
4
  Scaffold a new MCP tool definition. Use when the user asks to add a tool, create a new tool, or implement a new capability for the server.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "2.2"
7
+ version: "2.4"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -68,10 +68,9 @@ export const {{TOOL_EXPORT}} = tool('{{tool_name}}', {
68
68
  //
69
69
  // `recovery` is required (≥ 5 words) — it's the agent's next move when this
70
70
  // failure fires. Forcing function for thoughtful guidance: placeholders like
71
- // "Try again." get flagged by the linter. Contract-level `recovery` is
72
- // descriptive metadata; for the wire payload's `data.recovery.hint` (which
73
- // the framework mirrors into content[] text), pass it explicitly at the
74
- // throw site when dynamic context matters.
71
+ // "Try again." get flagged by the linter. The contract `recovery` is the
72
+ // single source of truth for what flows to the wire opt in at the throw
73
+ // site by spreading `ctx.recoveryFor('reason')` into the `data` arg.
75
74
  errors: [
76
75
  { reason: 'no_match', code: JsonRpcErrorCode.NotFound,
77
76
  when: 'No items matched the query.',
@@ -87,7 +86,18 @@ export const {{TOOL_EXPORT}} = tool('{{tool_name}}', {
87
86
  // With an `errors[]` contract: `throw ctx.fail('reason_id', message?, data?)`.
88
87
  // Without: throw via factories (`notFound`, `validationError`, …) or plain `Error`.
89
88
  const items = await search(input);
90
- if (items.length === 0) throw ctx.fail('no_match', `No items matched "${input.query}"`);
89
+ if (items.length === 0) {
90
+ // Dynamic recovery — interpolate runtime context, override the contract default.
91
+ throw ctx.fail('no_match', `No items matched "${input.query}"`, {
92
+ recovery: { hint: `Try a broader query than "${input.query}", or check the spelling.` },
93
+ });
94
+ }
95
+ if (queue.full()) {
96
+ // Static recovery — resolve from the contract via ctx.recoveryFor('reason').
97
+ // Single source of truth: the string lives in errors[] above; this spread
98
+ // pulls it onto the wire so format()-only clients see the recovery hint.
99
+ throw ctx.fail('queue_full', undefined, { ...ctx.recoveryFor('queue_full') });
100
+ }
91
101
  return { items };
92
102
  },
93
103
 
@@ -297,39 +307,50 @@ export const fetchArticles = tool('fetch_articles', {
297
307
  input: z.object({ pmids: z.array(z.string()).describe('PMIDs to fetch') }),
298
308
  output: z.object({ articles: z.array(ArticleSchema).describe('Resolved articles') }),
299
309
  async handler(input, ctx) {
300
- if (queue.full()) throw ctx.fail('queue_full');
310
+ // Static recovery ctx.recoveryFor pulls the contract recovery onto the wire.
311
+ // The contract is the single source of truth; this spread surfaces it on the
312
+ // wire so format()-only clients see the hint mirrored into content[] text.
313
+ if (queue.full()) throw ctx.fail('queue_full', undefined, { ...ctx.recoveryFor('queue_full') });
314
+
301
315
  const articles = await fetch(input.pmids);
302
316
  if (articles.length === 0) {
303
- throw ctx.fail('no_pmid_match', `No data for ${input.pmids.length} PMIDs`, { pmids: input.pmids });
317
+ // Dynamic recovery interpolate runtime context, override the contract default.
318
+ throw ctx.fail('no_pmid_match', `No data for ${input.pmids.length} PMIDs`, {
319
+ pmids: input.pmids,
320
+ recovery: { hint: `Use pubmed_search_articles to discover valid PMIDs.` },
321
+ });
304
322
  }
305
323
  return { articles };
306
324
  },
307
325
  });
308
326
  ```
309
327
 
328
+ **`ctx.recoveryFor(reason)`** resolves the contract's `recovery` string into the wire shape `{ recovery: { hint } }` — safe to spread into `data` so format()-only clients see the same recovery hint that structuredContent clients read. Always available on `Context` (no-op `{}` when no contract), strictly typed on `HandlerContext<R>` against the declared reasons. Use it for static recovery; pass `{ recovery: { hint: \`…${dynamic}…\` } }` directly when you need runtime context. The contract is the single source of truth — write the recovery once, lint validates it ≥5 words, the resolver carries it to every throw site.
329
+
310
330
  **Baseline codes** (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring. Wire-level behavior is identical when the contract is omitted, but you lose the type-checked `ctx.fail`, the `tools/list` advertisement, and conformance lint coverage — declare a contract whenever the tool has a domain-specific failure mode.
311
331
 
312
332
  `ctx.fail` accepts an optional 4th `options` argument for ES2022 cause chaining: `throw ctx.fail('upstream_error', 'Upstream returned 500', { url }, { cause: e })`.
313
333
 
314
334
  #### Service-layer throws
315
335
 
316
- API-wrapping tools usually delegate to a service: `const data = await ncbi.fetch(input)`. The throw lives in the service, not the handler and services don't receive `ctx`, so `ctx.fail` is unreachable from there. The fix is to pass `data: { reason: 'X' }` to the factory in the service. The framework's auto-classifier preserves `data` on the wire, so clients see the same `error.data.reason` they would have seen from `ctx.fail`. The handler doesn't catch — it just bubbles.
336
+ API-wrapping tools usually delegate to a service: `await ncbi.fetch(input, ctx)`. The throw lives in the service, not the handler. Services accept `ctx` (the unified Context) so they can call `ctx.log`, `ctx.recoveryFor`, etc. The handler doesn't catch it just bubbles, and the framework's auto-classifier preserves `data` on the wire.
317
337
 
318
- The contract entry on the tool and the `data: { reason }` on the service throw need to use the **same reason string** so the two sides line up.
338
+ The contract entry on the tool and the `data: { reason }` on the service throw need to use the **same reason string** so the two sides line up. `ctx.recoveryFor('reason')` resolves the contract recovery from the calling tool's `errors[]` — same single-source-of-truth pattern that works in handlers.
319
339
 
320
340
  ```typescript
321
- // service — passes data.reason to match the consuming tool's contract
341
+ // service — receives ctx; passes data.reason and spreads ctx.recoveryFor
342
+ import type { Context } from '@cyanheads/mcp-ts-core';
322
343
  import { serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
323
344
 
324
345
  export class NcbiService {
325
- async fetch(pmids: string[]) {
346
+ async fetch(pmids: string[], ctx: Context) {
326
347
  const response = await fetchWithRetry(...);
327
348
  if (!response.ok) {
328
- throw serviceUnavailable(
329
- `NCBI returned HTTP ${response.status}`,
330
- { reason: 'ncbi_unreachable', status: response.status }, // ← matches contract entry
331
- { cause: undefined },
332
- );
349
+ throw serviceUnavailable(`NCBI returned HTTP ${response.status}`, {
350
+ reason: 'ncbi_unreachable',
351
+ status: response.status,
352
+ ...ctx.recoveryFor('ncbi_unreachable'), // resolves from caller's contract
353
+ });
333
354
  }
334
355
  return response.json();
335
356
  }
@@ -343,11 +364,13 @@ export const fetchArticles = tool('fetch_articles', {
343
364
  recovery: 'NCBI is degraded; retry in a few minutes.' },
344
365
  ],
345
366
  async handler(input, ctx) {
346
- return { articles: await ncbi.fetch(input.pmids) }; // throws bubble unchanged
367
+ return { articles: await ncbi.fetch(input.pmids, ctx) }; // throws bubble unchanged
347
368
  },
348
369
  });
349
370
  ```
350
371
 
372
+ `ctx.recoveryFor` returns `{}` when the calling tool has no contract or the reason isn't declared, so the spread is always safe — services don't have to know which tool called them.
373
+
351
374
  See `add-service` for the full pattern.
352
375
 
353
376
  #### Ad-hoc factory throws (fallback)
@@ -121,10 +121,11 @@ Set via `MCP_AUTH_MODE` environment variable.
121
121
 
122
122
  ### tenantId sources
123
123
 
124
- | Transport | Source | Value |
125
- |:----------|:-------|:------|
126
- | HTTP with auth | JWT `tid` claim | Auto-propagated from token |
127
- | Stdio | Hardcoded default | `'default'` |
124
+ | Mode | Source | Value |
125
+ |:-----|:-------|:------|
126
+ | Stdio (any auth mode) | Hardcoded default | `'default'` |
127
+ | HTTP + `MCP_AUTH_MODE=none` | Hardcoded default | `'default'` (single-tenant by design) |
128
+ | HTTP + `MCP_AUTH_MODE=jwt`/`oauth` | JWT `tid` claim | Auto-propagated from token; `undefined` if absent (fail-closed) |
128
129
 
129
130
  ### Tenant ID validation rules
130
131
 
@@ -148,7 +149,7 @@ handler: async (input, ctx) => {
148
149
  },
149
150
  ```
150
151
 
151
- `ctx.state` throws `McpError(InvalidRequest)` if `tenantId` is missing. In stdio mode, `tenantId` defaults to `'default'` so `ctx.state` works without auth.
152
+ `ctx.state` throws `McpError(InvalidRequest)` if `tenantId` is missing. Stdio (any auth mode) and HTTP+`MCP_AUTH_MODE=none` default `tenantId` to `'default'` so `ctx.state` works without forcing operators to mint tokens. HTTP+`jwt`/`oauth` deliberately fails closed when the token lacks a `tid` claim — distinct authenticated callers must not silently share state.
152
153
 
153
154
  ---
154
155
 
@@ -4,7 +4,7 @@ description: >
4
4
  Canonical reference for the unified `Context` object passed to every tool and resource handler in `@cyanheads/mcp-ts-core`. Covers the full interface, all sub-APIs (`ctx.log`, `ctx.state`, `ctx.elicit`, `ctx.sample`, `ctx.progress`), and when to use each.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.1"
7
+ version: "1.2"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -26,7 +26,7 @@ interface Context {
26
26
  // Identity & tracing
27
27
  readonly requestId: string; // Unique per request, auto-generated
28
28
  readonly timestamp: string; // ISO 8601 request start time
29
- readonly tenantId?: string; // From JWT 'tid' claim; 'default' in stdio mode
29
+ readonly tenantId?: string; // JWT 'tid' claim; 'default' for stdio and HTTP+MCP_AUTH_MODE=none
30
30
  readonly traceId?: string; // OTEL trace ID (present when OTEL enabled)
31
31
  readonly spanId?: string; // OTEL span ID (present when OTEL enabled)
32
32
  readonly auth?: AuthContext; // Parsed auth claims (clientId, scopes, sub)
@@ -53,10 +53,14 @@ interface Context {
53
53
 
54
54
  // Raw URI — present only for resource handlers
55
55
  readonly uri?: URL;
56
+
57
+ // Opt-in contract resolver — always present (returns {} when no contract is attached
58
+ // or the reason is unknown), strictly typed on HandlerContext<R> against declared reasons.
59
+ recoveryFor(reason: string): { recovery: { hint: string } } | {};
56
60
  }
57
61
  ```
58
62
 
59
- > **`ctx.fail` is on `HandlerContext<R>`, not `Context`.** When a definition declares `errors: [...]`, the handler receives `HandlerContext<R> = Context & { fail: TypedFail<R> }` — the typed `fail` lives on the intersection, not on bare `Context`. See [`ctx.fail`](#ctxfail) below.
63
+ > **`ctx.fail` is on `HandlerContext<R>`, not `Context`.** When a definition declares `errors: [...]`, the handler receives `HandlerContext<R> = Context & { fail: TypedFail<R>; recoveryFor: TypedRecoveryFor<R> }` — both the typed `fail` and the strictly-typed `recoveryFor` live on the intersection. The bare `Context.recoveryFor` is the loose, always-present resolver. See [`ctx.fail`](#ctxfail) and [`ctx.recoveryFor`](#ctxrecoveryfor) below.
60
64
 
61
65
  ### Identity fields
62
66
 
@@ -64,7 +68,7 @@ interface Context {
64
68
  |:------|:--------------|:-------|
65
69
  | `requestId` | Yes | Auto-generated UUID per request |
66
70
  | `timestamp` | Yes | ISO 8601, request start |
67
- | `tenantId` | In stdio (as `'default'`); from JWT `tid` claim in HTTP | JWT / stdio default |
71
+ | `tenantId` | Stdio and HTTP+`MCP_AUTH_MODE=none` (as `'default'`); JWT `tid` claim in HTTP+`jwt`/`oauth` | JWT / single-tenant default |
68
72
  | `traceId` | When OTEL enabled | OTEL trace context |
69
73
  | `spanId` | When OTEL enabled | OTEL trace context |
70
74
  | `auth` | When auth enabled | Parsed JWT claims |
@@ -158,7 +162,7 @@ if (page.cursor) { /* more pages available */ }
158
162
 
159
163
  ### Behavior notes
160
164
 
161
- - Throws `McpError(InvalidRequest)` if `tenantId` is missing (won't happen in stdio mode — defaults to `'default'`).
165
+ - Throws `McpError(InvalidRequest)` if `tenantId` is missing. Won't happen in stdio (any auth mode) or HTTP+`MCP_AUTH_MODE=none` both default to `'default'`. Can happen in HTTP+`MCP_AUTH_MODE=jwt`/`oauth` when the token lacks a `tid` claim (intentional fail-closed: distinct authenticated callers must not silently share state).
162
166
  - Keys are tenant-prefixed internally; handlers never need to namespace manually.
163
167
  - **Workers persistence:** The `in-memory` provider loses data on cold starts. Use `cloudflare-kv`, `cloudflare-r2`, or `cloudflare-d1` for durable storage in Workers.
164
168
 
@@ -391,13 +395,57 @@ The contract is opt-in. See `skills/api-errors/SKILL.md` for the full type-drive
391
395
 
392
396
  ---
393
397
 
398
+ ## `ctx.recoveryFor`
399
+
400
+ Always present on `Context`. Resolves the contract `recovery` for a given reason and returns the canonical wire shape `{ recovery: { hint } }`, ready to spread into `data`. The first member of a planned **family of opt-in resolution helpers** (future: `troubleshootingFor`, `userMessageFor`, …).
401
+
402
+ ```ts
403
+ async handler(input, ctx) {
404
+ // Static recovery — pulled from the contract entry, no string duplication.
405
+ if (queue.full()) throw ctx.fail('queue_full', undefined, { ...ctx.recoveryFor('queue_full') });
406
+
407
+ // Dynamic recovery — interpolate runtime context, override the contract default.
408
+ if (!matched) throw ctx.fail('no_match', `No items for "${input.query}"`, {
409
+ recovery: { hint: `Try a broader query than "${input.query}", or check spelling.` },
410
+ });
411
+ }
412
+ ```
413
+
414
+ ### Signature
415
+
416
+ ```ts
417
+ // Loose (always present on Context — works without a contract attached):
418
+ ctx.recoveryFor(reason: string): { recovery: { hint: string } } | {}
419
+
420
+ // Strict (HandlerContext<R> when the definition declares errors[]):
421
+ ctx.recoveryFor(reason: R): { recovery: { hint: string } }
422
+ ```
423
+
424
+ ### Behavior
425
+
426
+ | Aspect | Detail |
427
+ |:-------|:-------|
428
+ | No contract attached | Returns `{}` — spread is a no-op. Always safe. |
429
+ | Unknown reason | Returns `{}` (TS prevents this for typed callers; runtime is loose for JS / stale contracts). |
430
+ | Declared reason | Returns `{ recovery: { hint: <contract.recovery> } }` — spread into `data`. |
431
+ | Override | Caller can override by spreading `recoveryFor` first then writing `recovery: { hint: '...' }` after — last write wins. |
432
+ | Service usage | Services that accept `ctx: Context` can spread `ctx.recoveryFor('reason')` directly; the no-op fallback means they don't need to know which tool called them. |
433
+
434
+ ### Why opt-in resolution, not auto-population
435
+
436
+ The framework never injects `data.recovery.hint` without an explicit signal at the throw site. Authors opt in by typing `ctx.recoveryFor('reason')` — the same way `ctx.fail('reason')` opts into resolving the contract `code`. The contract is the single source of truth for the recovery hint; the resolver is a typed lookup keyed by the same reason the author already typed. No magic, no hidden transformation.
437
+
438
+ The `≥5 words` lint rule on contract `recovery` (validated at lint time) makes this load-bearing — every `ctx.recoveryFor` call site benefits from the thoughtfulness the contract enforced.
439
+
440
+ ---
441
+
394
442
  ## Quick reference
395
443
 
396
444
  | Property | Type | Present when |
397
445
  |:---------|:-----|:-------------|
398
446
  | `ctx.requestId` | `string` | Always |
399
447
  | `ctx.timestamp` | `string` | Always |
400
- | `ctx.tenantId` | `string \| undefined` | Always in stdio (`'default'`); HTTP with auth |
448
+ | `ctx.tenantId` | `string \| undefined` | Stdio (`'default'`); HTTP+`MCP_AUTH_MODE=none` (`'default'`); HTTP+`jwt`/`oauth` (JWT `tid` claim — undefined if absent) |
401
449
  | `ctx.traceId` | `string \| undefined` | OTEL enabled |
402
450
  | `ctx.spanId` | `string \| undefined` | OTEL enabled |
403
451
  | `ctx.auth` | `AuthContext \| undefined` | Auth enabled |
@@ -411,3 +459,4 @@ The contract is opt-in. See `skills/api-errors/SKILL.md` for the full type-drive
411
459
  | `ctx.progress` | `ContextProgress \| undefined` | Tool defined with `task: true` |
412
460
  | `ctx.uri` | `URL \| undefined` | Resource handlers only |
413
461
  | `ctx.fail` | `(reason, msg?, data?, opts?) => McpError` | Definition declares `errors[]` contract |
462
+ | `ctx.recoveryFor` | `(reason) => { recovery: { hint } } \| {}` | Always (no-op when no contract); strictly typed on `HandlerContext<R>` |
@@ -4,7 +4,7 @@ description: >
4
4
  McpError constructor, JsonRpcErrorCode reference, and error handling patterns for `@cyanheads/mcp-ts-core`. Use when looking up error codes, understanding where errors should be thrown vs. caught, or using ErrorHandler.tryCatch in services.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.2"
7
+ version: "1.4"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -67,7 +67,56 @@ export const fetchTool = tool('fetch_articles', {
67
67
  | Lint (startup) | Each `code` validated against `JsonRpcErrorCode`. Reasons validated as snake_case + unique within contract. `recovery` validated as non-empty and ≥ 5 words. |
68
68
  | Lint (conformance) | If the handler `throw new McpError(JsonRpcErrorCode.X)` outside `ctx.fail`, conformance check warns when X isn't declared. |
69
69
 
70
- > **`recovery` is descriptive, not auto-injected.** The contract `recovery` is required metadata documenting the agent's next move when this failure mode fires (a forcing function for thoughtful guidance — placeholders like "Try again." get flagged by the linter). It is **not** auto-populated into runtime `data.recovery.hint`. The wire payload's recovery hint which the framework mirrors into `content[]` text per the [error-path parity](#error-path-parity) invariant — is populated separately at the throw site, where dynamic context (input values, attempted IDs, queue state) is available: `ctx.fail('reason', msg, { recovery: { hint: '...' } })`. The two fields can carry the same string when no dynamic context is needed; they're decoupled by design — what the author writes at the throw site is what flows to the wire, with no hidden transformation.
70
+ > **`recovery` is opt-in resolution, not auto-population.** The contract `recovery` is required metadata documenting the agent's next move when this failure mode fires (a forcing function for thoughtful guidance — placeholders like "Try again." get flagged by the linter). It does **not** automatically appear in runtime `data.recovery.hint` — the framework never injects it without an explicit signal at the throw site. Authors opt in by spreading `ctx.recoveryFor('reason')` into the `data` argument, the same way `ctx.fail('reason')` opts into resolving the contract `code`. What the author types at the throw site is what flows to the wire, with no hidden transformation; the resolver is just a typed lookup keyed by the same `reason` the author already typed.
71
+
72
+ #### `ctx.recoveryFor` — opt-in contract resolution
73
+
74
+ `ctx.recoveryFor(reason)` returns `{ recovery: { hint: <contract.recovery> } }` for a declared reason, ready to spread into `data`. Always available on `Context` (returns `{}` when no contract is attached or the reason is unknown — spread-safe with no optional chaining). On `HandlerContext<R>` it tightens to a typed signature constrained to the declared reason union.
75
+
76
+ ```ts
77
+ export const calculateTool = tool('calculate', {
78
+ // ...
79
+ errors: [
80
+ { reason: 'empty_expression', code: JsonRpcErrorCode.ValidationError,
81
+ when: 'Expression is empty or whitespace-only.',
82
+ recovery: 'Provide a non-empty mathematical expression to evaluate.' },
83
+ ],
84
+ handler(input, ctx) {
85
+ if (!input.expression.trim()) {
86
+ // Static recovery — resolve from the contract.
87
+ throw ctx.fail('empty_expression', undefined, { ...ctx.recoveryFor('empty_expression') });
88
+ }
89
+ // ...
90
+ },
91
+ });
92
+ ```
93
+
94
+ Same pattern works inside services that accept `ctx`:
95
+
96
+ ```ts
97
+ export class MathService {
98
+ parse(expr: string, ctx: Context) {
99
+ try {
100
+ return mathjs.parse(expr);
101
+ } catch (err) {
102
+ throw validationError(`Parse failed: ${err.message}`, {
103
+ reason: 'parse_failed',
104
+ ...ctx.recoveryFor('parse_failed'), // {} if calling tool has no matching reason
105
+ });
106
+ }
107
+ }
108
+ }
109
+ ```
110
+
111
+ The contract is the single source of truth — write the recovery once, lint validates ≥5 words, the resolver carries it to every throw site that opts in. For runtime-context recovery (interpolating input values, attempted IDs, queue state), override at the throw site:
112
+
113
+ ```ts
114
+ throw ctx.fail('no_match', `No item ${id}`, {
115
+ recovery: { hint: `No item ${id}; try IDs 1-100 instead.` },
116
+ });
117
+ ```
118
+
119
+ `ctx.recoveryFor` is the first member of a planned **family of opt-in resolution helpers**. Future contract-bound fields (`troubleshootingFor`, `userMessageFor`, …) follow the same shape: single-purpose, spreadable wire-shape, `{}` fallback when not applicable.
71
120
 
72
121
  **Skip the contract** for one-off internal tools or quick prototypes — `ctx` is plain `Context` (no `fail`) and you throw via [factories](#error-factories-fallback) directly. Behavior is identical at the wire; the contract just adds compile-time safety.
73
122
 
@@ -97,6 +146,17 @@ errors: [
97
146
 
98
147
  The handler doesn't catch and re-throw — letting service errors bubble unchanged keeps "logic throws, framework catches" intact. The wire payload still carries `code` + `data.reason`, and clients can switch on reason without parsing message text. What's lost is lint-time enforcement that every reason is reachable; compensate with one wire-shape test per reason.
99
148
 
149
+ To carry the contract `recovery` from a service throw, accept `ctx` and spread the resolver:
150
+
151
+ ```ts
152
+ throw validationError(message, {
153
+ reason: 'parse_failed',
154
+ ...ctx.recoveryFor('parse_failed'), // {} when calling tool has no matching reason
155
+ });
156
+ ```
157
+
158
+ `ctx.recoveryFor` is always present on `Context` (no-op when no contract), so services don't need to know which tool called them — the spread is safe either way.
159
+
100
160
  ---
101
161
 
102
162
  ## Error Factories (fallback)