@cyanheads/mcp-ts-core 0.8.13 → 0.8.14

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.13
3
+ **Package:** `@cyanheads/mcp-ts-core` · **Version:** 0.8.14
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.
@@ -378,6 +378,8 @@ async handler(input, ctx) {
378
378
 
379
379
  **`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.
380
380
 
381
+ **Declare contracts inline on each tool, even when they look similar across tools.** The contract is part of the tool's documented public surface — reading one tool definition file should give the full picture (input, output, errors, handler, format). Don't extract a shared `errors[]` constant or contract module to deduplicate; per-tool repetition is the intended cost of locality, and dynamic `recovery` hints often need tool-specific context anyway.
382
+
381
383
  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).
382
384
 
383
385
  **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.13-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.14-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
 
@@ -110,6 +110,19 @@ It also works on Cloudflare Workers with `createWorkerHandler()` — same defini
110
110
  - **Tiered dependencies** — parsers, OTEL SDK, Supabase, and OpenAI are optional peers. Install what you use.
111
111
  - **Agent-first DX** — ships `CLAUDE.md` with the full exports catalog so AI agents ramp up without prompting.
112
112
 
113
+ ### Storage Behavior Snapshot
114
+
115
+ Provider behavior is intentionally normalized at the interface, but backend limits still matter:
116
+
117
+ | Provider | Delete count accuracy | List TTL filtering | Notes |
118
+ |:---------|:----------------------|:-------------------|:------|
119
+ | `in-memory` | Exact | Exact | Volatile process memory |
120
+ | `filesystem` | Exact | Exact | Node/Bun only |
121
+ | `supabase` | Exact | Exact | Requires configured Supabase client |
122
+ | `cloudflare-d1` | Exact | Exact | Workers D1 binding |
123
+ | `cloudflare-kv` | Idempotent API success | Native/eventual | Delete cannot prove prior existence |
124
+ | `cloudflare-r2` | Idempotent API success | Not applied during list | Expired envelopes are removed on read |
125
+
113
126
  ## Server structure
114
127
 
115
128
  ```text
@@ -0,0 +1,33 @@
1
+ ---
2
+ summary: "disabledTool/DisabledMetadata re-exported from package root (#109); new tool-defs-analysis skill (#111); Worker-runtime test harness via @cloudflare/vitest-pool-workers; example definitions polished; storage provider behavior table in README."
3
+ breaking: false
4
+ ---
5
+
6
+ # 0.8.14 — 2026-05-04
7
+
8
+ Polish release. The `disabledTool` re-export gap promised by the 0.8.11 changelog is closed; a new audit skill walks the LLM-facing language across every definition in a server; example tool/resource definitions get a top-to-bottom title/description/recovery scrub so scaffolded servers ship with prose worth keeping; and a Workers-runtime test harness now exercises `createWorkerHandler` inside Miniflare instead of mocked stubs.
9
+
10
+ ## Added
11
+
12
+ - **`disabledTool` and `DisabledMetadata` re-exported from the package root** ([#109](https://github.com/cyanheads/mcp-ts-core/issues/109)) — the 0.8.11 changelog claimed this surface but only the deep import path worked. `import { disabledTool, type DisabledMetadata } from '@cyanheads/mcp-ts-core'` now resolves; deep imports remain unchanged.
13
+ - **`tool-defs-analysis` skill** ([#111](https://github.com/cyanheads/mcp-ts-core/issues/111)) — read-only audit of MCP definition language across an existing surface. Walks every tool/resource/prompt and checks 10 categories the LLM reads to decide whether and how to call (voice, internal leaks, audience leaks, defaults, recovery hints, output descriptions, cross-references, sparsity, examples, structure). Produces grouped findings with `file:line` citations and a numbered options list. Complements `field-test` (behavior testing) and `security-pass` (security audit).
14
+ - **Storage Behavior Snapshot table in `README.md`** — six providers × two columns (delete-count accuracy, list-TTL filtering) so consumers can see the trade-offs at a glance without reading provider source.
15
+ - **Worker-runtime test harness** — new `vitest.worker.ts` config, `tests/fixtures/worker-runtime.fixture.ts`, and `tests/worker/create-worker-handler.worker.test.ts` exercise `createWorkerHandler` inside Miniflare via `@cloudflare/vitest-pool-workers`. Pinned to the system Node binary because the pool's loader hooks don't run under bun-node. Wired into `bun run test:worker` and the `test:all` chain.
16
+ - **`tests/integration/package-consumer.int.test.ts`** — verifies that a downstream project consuming `@cyanheads/mcp-ts-core` from `node_modules` resolves every documented subpath export and the package-root re-exports actually exist at runtime.
17
+ - **`tests/unit/linter/server-json-rules.test.ts`** — closes the test gap on `src/linter/rules/server-json-rules.ts`; the file is now pinned at 100% line/statement and 90% branch coverage.
18
+
19
+ ## Changed
20
+
21
+ - **Example tool/resource/prompt definitions polished across the board** — titles trimmed of "Template" prefix (`Template Echo Message` → `Echo Message`, `Template Cat Fact` → `Random Cat Fact`, `Async Countdown (Task Demo)` → `Async Countdown`); descriptions rewritten in active voice with concrete behavior (`"Demonstrates the MCP Tasks API with a countdown timer"` → `"Run a countdown timer for the requested number of seconds, reporting progress as it ticks. Cancellable mid-run."`); ad-hoc `throw new Error(...)` replaced with `serviceUnavailable`/`validationError` factories carrying contract-style `recovery.hint`s. The same tightening flows through `templates/` so `npx @cyanheads/mcp-ts-core init` scaffolds servers that already meet `tool-defs-analysis` review standards.
22
+ - **JSDoc on `IStorageProvider` and `StorageOptions.ttl` documents per-provider behavior** — exact vs. idempotent delete counts, exact vs. native/eventual list-TTL filtering, and the `cloudflare-r2` `list()` opt-out. `cloudflare-d1` row appears alongside the existing providers; `in-memory` description corrected to "timers and lazy filtering on get/list" instead of "setTimeout for proactive deletion."
23
+ - **"Declare contracts inline on each tool" guidance** added to `AGENTS.md`/`CLAUDE.md`, `templates/AGENTS.md`/`templates/CLAUDE.md`, `skills/add-tool` (v2.6→2.7), and `skills/api-errors` (v1.4→1.5). Per-tool repetition of the `errors[]` contract is the intended cost of locality; a code-cleanup pass that suggests consolidating contracts into a shared module should be declined.
24
+ - **`skills/api-workers` (v1.2→1.3)** — Workers storage allowlist documentation tightened: `filesystem` and unknown provider types throw `ConfigurationError` in serverless environments instead of silently falling back to `in-memory`. The "silent force to in-memory" claim was outdated.
25
+ - **Coverage thresholds raised** in `vitest.config.ts` — global `lines 80→89`, `functions 75→90`, `branches 70→79`, `statements 80→88`. Per-file pins added: `src/linter/rules/server-json-rules.ts` at 100/100/90/100 and `src/storage/providers/supabase/supabaseProvider.ts` at 95/100/60/85 to lock in the new test coverage.
26
+ - **`@cloudflare/vitest-pool-workers ^0.15.2`** added to `devDependencies`; corresponding type packages added to `tsconfig.json` and `tsconfig.test.json` `types` arrays.
27
+ - **`devcheck.config.json`** — `cloudflare:*` added to depcheck `ignoreMatches` so `cloudflare:test` and `cloudflare:workers` virtual specifiers stop tripping the unused-deps check.
28
+ - **Stale `docs/audit/` and `docs/server-ideas/` removed** along with `docs/conformance-test-plan.md` and `docs/resource-notifications.md` — historical scaffolding that was no longer referenced from anywhere.
29
+ - **Dependency bumps** — `zod ^4.4.2 → ^4.4.3` (peer), `@cloudflare/workers-types ^4.20260503.1 → ^4.20260505.1`, `@supabase/supabase-js ^2.105.1 → ^2.105.3`, `openai ^6.35.0 → ^6.36.0`. `zod` removed from `dependencies` (it was already declared as a peer dep; the duplicate listing is gone).
30
+
31
+ ## Fixed
32
+
33
+ - **Package-root export of `disabledTool`/`DisabledMetadata`** ([#109](https://github.com/cyanheads/mcp-ts-core/issues/109)) — see Added.
@@ -18,8 +18,8 @@ export type { AnyResourceDefinition, ResourceDefinition, } from '../mcp-server/r
18
18
  export { resource } from '../mcp-server/resources/utils/resourceDefinition.js';
19
19
  /** Union of all accepted tool definition shapes (standard + task). */
20
20
  export type { AnyToolDef } from '../mcp-server/tools/tool-registration.js';
21
- export type { AnyToolDefinition, ToolAnnotations, ToolDefinition, } from '../mcp-server/tools/utils/toolDefinition.js';
22
- export { tool } from '../mcp-server/tools/utils/toolDefinition.js';
21
+ export type { AnyToolDefinition, DisabledMetadata, ToolAnnotations, ToolDefinition, } from '../mcp-server/tools/utils/toolDefinition.js';
22
+ export { disabledTool, tool } from '../mcp-server/tools/utils/toolDefinition.js';
23
23
  export type { LintDefinitionType, LintDiagnostic, LintInput, LintReport, LintSeverity, } from '../linter/types.js';
24
24
  export { validateDefinitions } from '../linter/validate.js';
25
25
  export type { CallToolResult, ContentBlock, CreateMessageResult, ElicitResult, ModelPreferences, PromptMessage, SamplingMessage, } from '@modelcontextprotocol/sdk/types.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,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"}
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,gBAAgB,EAChB,eAAe,EACf,cAAc,GACf,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,4CAA4C,CAAC;AAMhF,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"}
@@ -17,6 +17,6 @@ export { createFail, createRecoveryFor } from '../core/context.js';
17
17
  export { APP_RESOURCE_MIME_TYPE, appResource, appTool } from '../mcp-server/apps/appBuilders.js';
18
18
  export { prompt } from '../mcp-server/prompts/utils/promptDefinition.js';
19
19
  export { resource } from '../mcp-server/resources/utils/resourceDefinition.js';
20
- export { tool } from '../mcp-server/tools/utils/toolDefinition.js';
20
+ export { disabledTool, tool } from '../mcp-server/tools/utils/toolDefinition.js';
21
21
  export { validateDefinitions } from '../linter/validate.js';
22
22
  //# sourceMappingURL=index.js.map
@@ -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;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
+ {"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;AAS9E,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,4CAA4C,CAAC;AAahF,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -1,4 +1,4 @@
1
- {"level":50,"time":1777795711727,"env":"testing","version":"0.0.0-test","pid":4050,"requestId":"60L97-GTRZT","timestamp":"2026-05-03T08:08:31.726Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"a1b4457ff0e3dff4a5aee4cbbf29fd1082f49f2fa876a03879869d4435197481","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"errorData":{"sessionId":"a1b4457ff0e3dff4a5aee4cbbf29fd1082f49f2fa876a03879869d4435197481","toolName":"scoped_echo","requestId":"60L97-GTRZT","timestamp":"2026-05-03T08:08:31.726Z","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:170: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":1777795712041,"env":"testing","version":"0.8.13","pid":4052,"requestId":"ORW4B-XHO3Z","timestamp":"2026-05-03T08:08:32.041Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"ORW4B-XHO3Z","timestamp":"2026-05-03T08:08:32.041Z","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:170: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":1777795712056,"env":"testing","version":"0.8.13","pid":4052,"requestId":"UB134-4WCWE","timestamp":"2026-05-03T08:08:32.056Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"UB134-4WCWE","timestamp":"2026-05-03T08:08:32.056Z","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:170: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":1777795712061,"env":"testing","version":"0.8.13","pid":4052,"requestId":"4811D-03SA6","timestamp":"2026-05-03T08:08:32.061Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"GET","errorData":{"path":"/mcp","method":"GET","requestId":"4811D-03SA6","timestamp":"2026-05-03T08:08:32.061Z","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:170: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":1777951813776,"env":"testing","version":"0.0.0-test","pid":78802,"requestId":"YWARH-YU16V","timestamp":"2026-05-05T03:30:13.775Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"b11ce8ac42cc79a568d733f5492c79a67c721454d5279c972e95823d70a7dfa9","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"errorData":{"sessionId":"b11ce8ac42cc79a568d733f5492c79a67c721454d5279c972e95823d70a7dfa9","toolName":"scoped_echo","requestId":"YWARH-YU16V","timestamp":"2026-05-05T03:30:13.775Z","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:170: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":1777951814581,"env":"testing","version":"0.8.14","pid":78808,"requestId":"L8YC7-Z170O","timestamp":"2026-05-05T03:30:14.580Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"L8YC7-Z170O","timestamp":"2026-05-05T03:30:14.580Z","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:170: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":1777951814596,"env":"testing","version":"0.8.14","pid":78808,"requestId":"NJ6A4-VFNQU","timestamp":"2026-05-05T03:30:14.596Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"NJ6A4-VFNQU","timestamp":"2026-05-05T03:30:14.596Z","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:170: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":1777951814600,"env":"testing","version":"0.8.14","pid":78808,"requestId":"BFMOM-T87EN","timestamp":"2026-05-05T03:30:14.599Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"GET","errorData":{"path":"/mcp","method":"GET","requestId":"BFMOM-T87EN","timestamp":"2026-05-05T03:30:14.599Z","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:170: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":1777795711727,"env":"testing","version":"0.0.0-test","pid":4050,"requestId":"60L97-GTRZT","timestamp":"2026-05-03T08:08:31.726Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"a1b4457ff0e3dff4a5aee4cbbf29fd1082f49f2fa876a03879869d4435197481","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"errorData":{"sessionId":"a1b4457ff0e3dff4a5aee4cbbf29fd1082f49f2fa876a03879869d4435197481","toolName":"scoped_echo","requestId":"60L97-GTRZT","timestamp":"2026-05-03T08:08:31.726Z","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:170: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":1777795712041,"env":"testing","version":"0.8.13","pid":4052,"requestId":"ORW4B-XHO3Z","timestamp":"2026-05-03T08:08:32.041Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"ORW4B-XHO3Z","timestamp":"2026-05-03T08:08:32.041Z","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:170: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":1777795712056,"env":"testing","version":"0.8.13","pid":4052,"requestId":"UB134-4WCWE","timestamp":"2026-05-03T08:08:32.056Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"UB134-4WCWE","timestamp":"2026-05-03T08:08:32.056Z","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:170: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":1777795712061,"env":"testing","version":"0.8.13","pid":4052,"requestId":"4811D-03SA6","timestamp":"2026-05-03T08:08:32.061Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"GET","errorData":{"path":"/mcp","method":"GET","requestId":"4811D-03SA6","timestamp":"2026-05-03T08:08:32.061Z","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:170: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":1777951813776,"env":"testing","version":"0.0.0-test","pid":78802,"requestId":"YWARH-YU16V","timestamp":"2026-05-05T03:30:13.775Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"b11ce8ac42cc79a568d733f5492c79a67c721454d5279c972e95823d70a7dfa9","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"errorData":{"sessionId":"b11ce8ac42cc79a568d733f5492c79a67c721454d5279c972e95823d70a7dfa9","toolName":"scoped_echo","requestId":"YWARH-YU16V","timestamp":"2026-05-05T03:30:13.775Z","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:170: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":1777951814581,"env":"testing","version":"0.8.14","pid":78808,"requestId":"L8YC7-Z170O","timestamp":"2026-05-05T03:30:14.580Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"L8YC7-Z170O","timestamp":"2026-05-05T03:30:14.580Z","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:170: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":1777951814596,"env":"testing","version":"0.8.14","pid":78808,"requestId":"NJ6A4-VFNQU","timestamp":"2026-05-05T03:30:14.596Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"NJ6A4-VFNQU","timestamp":"2026-05-05T03:30:14.596Z","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:170: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":1777951814600,"env":"testing","version":"0.8.14","pid":78808,"requestId":"BFMOM-T87EN","timestamp":"2026-05-05T03:30:14.599Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"GET","errorData":{"path":"/mcp","method":"GET","requestId":"BFMOM-T87EN","timestamp":"2026-05-05T03:30:14.599Z","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:170: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."}
@@ -10,10 +10,11 @@ import type { RequestContext } from '../../utils/internal/requestContext.js';
10
10
  *
11
11
  * @property ttl - Time-to-live for the stored item, in seconds. If not provided, the item is stored indefinitely.
12
12
  * Provider-specific behaviors:
13
- * - `in-memory`: TTL enforced by `setTimeout` for proactive deletion.
13
+ * - `in-memory`: TTL enforced by timers and lazy filtering on get/list.
14
14
  * - `filesystem`: TTL stored in a metadata envelope; expired items are filtered on `get()` and `list()`.
15
- * - `supabase`: TTL managed via an `expires_at` column; expired rows are handled by database queries.
16
- * - `cloudflare-kv`: TTL is a native feature of the KV store.
15
+ * - `supabase`: TTL managed via an `expires_at` column; expired rows are filtered in list queries and lazily removed on reads.
16
+ * - `cloudflare-d1`: TTL managed via an `expires_at` column; expired rows are filtered in list queries and lazily removed on reads.
17
+ * - `cloudflare-kv`: TTL is a native feature of the KV store. KV propagation is eventually consistent.
17
18
  * - `cloudflare-r2`: TTL stored in a metadata envelope; expired items are filtered on `get()`. `list()` does not filter expired items due to performance cost.
18
19
  */
19
20
  export interface StorageOptions {
@@ -67,6 +68,12 @@ export interface ListResult {
67
68
  /**
68
69
  * Defines the contract for a generic storage provider.
69
70
  * All methods must be asynchronous and accept a RequestContext for tracing and logging.
71
+ *
72
+ * Current provider-status notes:
73
+ * - `in-memory`, `filesystem`, `supabase`, and `cloudflare-d1` report precise delete/deleteMany counts.
74
+ * - `cloudflare-kv` and `cloudflare-r2` expose idempotent deletes only; delete() returns true after a successful backend call even when the key may not have existed, and deleteMany() counts attempted successful deletes.
75
+ * - List TTL filtering is exact for `in-memory`, `filesystem`, `supabase`, and `cloudflare-d1`; native/eventual for `cloudflare-kv`; and not applied for `cloudflare-r2` because listing every object body would be too expensive.
76
+ * - `values` on ListResult is opportunistic. Consumers must treat it as a cache hint and call getMany() when absent.
70
77
  */
71
78
  export interface IStorageProvider {
72
79
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"IStorageProvider.d.ts","sourceRoot":"","sources":["../../../src/storage/core/IStorageProvider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE;;;;;;;;;;GAUG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,IAAI,EAAE,MAAM,EAAE,CAAC;IACf;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;OAOG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAElE;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEjF;;;;;;;;;;;;;;OAcG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvF;;;;;;OAMG;IACH,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAElF;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IAE/F;;;;;;;;;OASG;IACH,IAAI,CACF,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,UAAU,CAAC,CAAC;IAEvB;;;;;;;OAOG;IACH,GAAG,CACD,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CACL,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB"}
1
+ {"version":3,"file":"IStorageProvider.d.ts","sourceRoot":"","sources":["../../../src/storage/core/IStorageProvider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,IAAI,EAAE,MAAM,EAAE,CAAC;IACf;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;OAOG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAElE;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEjF;;;;;;;;;;;;;;OAcG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvF;;;;;;OAMG;IACH,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAElF;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IAE/F;;;;;;;;;OASG;IACH,IAAI,CACF,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,UAAU,CAAC,CAAC;IAEvB;;;;;;;OAOG;IACH,GAAG,CACD,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CACL,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyanheads/mcp-ts-core",
3
- "version": "0.8.13",
3
+ "version": "0.8.14",
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",
@@ -138,12 +138,13 @@
138
138
  "tree": "bun run scripts/tree.ts",
139
139
  "fetch-spec": "bun run scripts/fetch-openapi-spec.ts",
140
140
  "test": "bunx vitest run",
141
- "test:all": "bun run test && bun run test:integration",
141
+ "test:all": "bun run test && bun run test:worker && bun run test:integration",
142
142
  "test:integration": "bunx vitest run --config vitest.integration.ts",
143
143
  "test:unit": "bunx vitest run --project unit",
144
144
  "test:smoke": "bunx vitest run --project smoke",
145
145
  "test:fuzz": "bunx vitest run --project fuzz",
146
146
  "test:compliance": "bunx vitest run --project compliance",
147
+ "test:worker": "sh -c 'NODE_BIN=$(which -a node | grep -v /bun-node- | head -n 1); \"$NODE_BIN\" ./node_modules/vitest/vitest.mjs run --config vitest.worker.ts'",
147
148
  "test:ui": "bunx vitest --ui",
148
149
  "test:coverage": "bunx vitest run --coverage",
149
150
  "audit": "bun audit",
@@ -160,12 +161,12 @@
160
161
  "path-to-regexp": "8.4.2",
161
162
  "picomatch": "2.3.2",
162
163
  "protobufjs": "7.5.5",
163
- "yaml": "1.10.3",
164
- "zod": "4.4.2"
164
+ "yaml": "1.10.3"
165
165
  },
166
166
  "devDependencies": {
167
167
  "@biomejs/biome": "2.4.14",
168
- "@cloudflare/workers-types": "^4.20260503.1",
168
+ "@cloudflare/vitest-pool-workers": "^0.15.2",
169
+ "@cloudflare/workers-types": "^4.20260505.1",
169
170
  "@duckdb/node-api": "^1.5.2-r.1",
170
171
  "@hono/otel": "^1.1.1",
171
172
  "@opentelemetry/exporter-metrics-otlp-http": "^0.216.0",
@@ -177,7 +178,7 @@
177
178
  "@opentelemetry/sdk-node": "^0.216.0",
178
179
  "@opentelemetry/sdk-trace-node": "^2.7.1",
179
180
  "@opentelemetry/semantic-conventions": "^1.40.0",
180
- "@supabase/supabase-js": "^2.105.1",
181
+ "@supabase/supabase-js": "^2.105.3",
181
182
  "@types/bun": "^1.3.13",
182
183
  "@types/js-yaml": "^4.0.9",
183
184
  "@types/node": "^25.6.0",
@@ -198,7 +199,7 @@
198
199
  "js-yaml": "^4.1.1",
199
200
  "linkedom": "^0.18.12",
200
201
  "node-cron": "^4.2.1",
201
- "openai": "^6.35.0",
202
+ "openai": "^6.36.0",
202
203
  "papaparse": "^5.5.3",
203
204
  "partial-json": "^0.1.7",
204
205
  "pdf-lib": "^1.17.1",
@@ -271,7 +272,7 @@
271
272
  "hono": "^4.12.16",
272
273
  "jose": "^6.2.3",
273
274
  "pino": "^10.3.1",
274
- "zod": "^4.4.2"
275
+ "zod": "^4.4.3"
275
276
  },
276
277
  "peerDependencies": {
277
278
  "@duckdb/node-api": "^1.5.0",
@@ -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.6"
7
+ version: "2.7"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -66,6 +66,11 @@ export const {{TOOL_EXPORT}} = tool('{{tool_name}}', {
66
66
  // SerializationError) bubble freely — only declare domain-specific reasons.
67
67
  // Delete this block if no domain failures apply.
68
68
  //
69
+ // Keep contracts inline on this tool, even when other tools have similar
70
+ // entries. The contract is part of the tool's documented public surface —
71
+ // don't extract a shared `errors[]` constant; per-tool repetition is the
72
+ // intended cost of self-contained tool defs.
73
+ //
69
74
  // `recovery` is required (≥ 5 words) — it's the agent's next move when this
70
75
  // failure fires. Forcing function for thoughtful guidance: placeholders like
71
76
  // "Try again." get flagged by the linter. The contract `recovery` is the
@@ -517,6 +522,7 @@ Large payloads burn the agent's context window. Default to curated summaries; of
517
522
  - [ ] If wrapping external API: output schema and `format()` preserve uncertainty from sparse upstream payloads instead of inventing concrete values
518
523
  - [ ] `auth` scopes declared if the tool needs authorization
519
524
  - [ ] `errors: [...]` contract declared for the tool's domain-specific failure modes — or block deleted if no domain failures apply (baseline codes bubble freely)
525
+ - [ ] Error contract declared inline on this tool — not imported from a shared module, even when other tools have near-identical entries
520
526
  - [ ] `task: true` added if the tool is long-running
521
527
  - [ ] Registered in the project's existing `createApp()` tool list (directly or via barrel)
522
528
  - [ ] `bun run devcheck` passes
@@ -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.4"
7
+ version: "1.5"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -120,6 +120,8 @@ throw ctx.fail('no_match', `No item ${id}`, {
120
120
 
121
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.
122
122
 
123
+ > **Declare contracts inline on each tool, even when similar across tools.** The contract is part of the tool's documented public surface — reading one tool definition file should give the full picture (input, output, errors, handler, format). Don't extract a shared `errors[]` constant or contract module to deduplicate near-identical entries; per-tool repetition is the intended cost of locality, and dynamic `recovery` hints often need tool-specific runtime context anyway. If a code-cleanup pass suggests consolidating contracts, decline — the duplication is load-bearing for tool-def readability.
124
+
123
125
  > **Limits of the conformance lint.** The conformance and prefer-fail rules scan the handler's source text for `throw` statements. Errors thrown from called services (e.g. `await myService.fetch()` raising `RateLimited` internally) are invisible — the lint only sees what's lexically in the handler. Treat the contract as the *advertised* failure surface; bubbled-up codes still reach the client correctly via the auto-classifier, just without lint enforcement.
124
126
 
125
127
  ### Carrying contract `reason` from services
@@ -4,7 +4,7 @@ description: >
4
4
  Cloudflare Workers deployment using `createWorkerHandler` from `@cyanheads/mcp-ts-core/worker`. Covers the full handler signature, binding types, CloudflareBindings extensibility, runtime compatibility guards, and wrangler.toml requirements.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.2"
7
+ version: "1.3"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -126,10 +126,10 @@ In Workers, only these storage providers are allowed:
126
126
  | `cloudflare-r2` | R2 bucket binding — object storage |
127
127
  | `cloudflare-d1` | D1 database binding — SQLite-compatible |
128
128
 
129
- `filesystem` and `supabase` are not on the whitelist and behave differently:
129
+ `filesystem`, `supabase`, and unknown provider types are not on the whitelist:
130
130
 
131
- - **`filesystem`** and other unknown types are **silently forced to `in-memory`** (a warning is logged) in a serverless environment.
132
- - **`supabase`** does **not** silently fall back. The framework attempts to connect and throws `ConfigurationError` if credentials (`SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`) are missing or the client cannot be constructed. Do not set `STORAGE_PROVIDER_TYPE=supabase` in a Worker.
131
+ - **`filesystem`** and unknown types throw `ConfigurationError` in serverless environments.
132
+ - **`supabase`** does **not** silently fall back. The framework may validate Supabase credentials first, but Worker startup still fails with `ConfigurationError` because Supabase storage is not a supported serverless provider. Do not set `STORAGE_PROVIDER_TYPE=supabase` in a Worker.
133
133
 
134
134
  Set `STORAGE_PROVIDER_TYPE` to one of the four whitelisted values to avoid unexpected behavior.
135
135
 
@@ -0,0 +1,209 @@
1
+ ---
2
+ name: tool-defs-analysis
3
+ description: >
4
+ Read-only audit of MCP definition language across an existing surface — tools, resources, prompts. Walks every definition file and checks 10 categories the LLM reads to decide whether and how to call: voice & tense, internal leaks, audience leaks, defaults, recovery hints, output descriptions, cross-references, sparsity, examples, structure. Produces grouped findings with file:line citations and a numbered options list. Use during polish, after a refactor, or before a release. Complements `field-test` (behavior testing) and `security-pass` (security audit).
5
+ metadata:
6
+ author: cyanheads
7
+ version: "1.0"
8
+ audience: external
9
+ type: audit
10
+ ---
11
+
12
+ ## Context
13
+
14
+ Every string in a tool/resource/prompt definition is part of an LLM-facing API contract. The model reads the description, every parameter `.describe()`, the output schema, the recovery hints — and decides what to call and how. Definition language drifts: an internal mapping leaks into a parameter doc during a fix, a self-referential output description survives a refactor, a default that suited the developer at scaffold time stays after the typical call shape changes.
15
+
16
+ This skill is the **review-time pass** for that drift. Read each definition the way a mid-tier model with no project context would — can it pick the tool, fill the fields, and recover from errors using only the rendered schema?
17
+
18
+ | Skill | Lens |
19
+ |:---|:---|
20
+ | `design-mcp-server` | Authoring rules at write-time |
21
+ | `field-test` | Behavior testing + a narrow 3-category leak audit |
22
+ | `security-pass` | Injection, scopes, input sinks |
23
+ | `tool-defs-analysis` (this) | LLM-facing language across the existing surface |
24
+
25
+ `field-test` already audits descriptions for implementation leaks, meta-coaching, and consumer-aware phrasing during its catalog step — that's a fast shallow pass alongside live tool calls. This skill is the deeper review: 10 categories, every field, every recovery hint, every default value, with file:line citations.
26
+
27
+ **Read-only.** This skill produces a report; the maintainer applies fixes. While running it, do not run git, do not stage or commit, do not update the changelog, do not run `devcheck`, do not invoke wrapup or release workflows. Fixes flow through the normal authoring path (edit the definition, then re-run this skill if you want to verify).
28
+
29
+ ## When to Use
30
+
31
+ - After a polish session or refactor that touched definitions
32
+ - Before a release, alongside `polish-docs-meta` and `security-pass`
33
+ - When the user says "review my tool definitions", "audit descriptions", "are my tool descriptions any good"
34
+ - After scaffolding a new server but before it ships
35
+
36
+ Skip during initial authoring — `add-tool` and `design-mcp-server` cover that. Skip diff-only review — read each file in full so drift across the whole definition surfaces.
37
+
38
+ ## Inputs
39
+
40
+ Gather before starting. Ask if unclear:
41
+
42
+ 1. **Scope** — whole server, specific definitions, or a single directory?
43
+ 2. **Severity floor** — all findings (default), or skip nits?
44
+ 3. **Known concerns** — anything the user already wants emphasized?
45
+
46
+ ## Steps
47
+
48
+ ### 1. Build the inventory
49
+
50
+ ```bash
51
+ find src/mcp-server/tools/definitions -type f -name "*tool.ts" 2>/dev/null | sort
52
+ find src/mcp-server/resources/definitions -type f -name "*resource.ts" 2>/dev/null | sort
53
+ find src/mcp-server/prompts/definitions -type f -name "*.prompt.ts" 2>/dev/null | sort
54
+ ```
55
+
56
+ The `*tool.ts` / `*resource.ts` patterns also catch `*.app-tool.ts` / `*.app-resource.ts`. If the server's definitions live elsewhere (`examples/`, a packages workspace, …), audit those paths too.
57
+
58
+ Use `TaskCreate` — one task per file. Mark each complete after its findings are captured.
59
+
60
+ ### 2. Walk the 10 categories per file
61
+
62
+ Read each definition file in full. Apply every category — most files trip more than one. Capture each hit with `file:line`, the offending excerpt, and a one-line fix.
63
+
64
+ #### 1. Voice & tense
65
+
66
+ **Look in:** tool / resource / prompt `description`.
67
+
68
+ **Check:** imperative present-tense. "Search for trials" beats "Searches for trials" or "This tool will search trials".
69
+
70
+ **Smell:** "Allows you to…", "This tool…", "Provides functionality to…", "Searches for…", "Fetches…", "Will return…".
71
+
72
+ (Parameter `.describe()` text describes the *value*, not the tool — it doesn't need imperative voice.)
73
+
74
+ #### 2. Internal leaks
75
+
76
+ **Look in:** every `description` and `.describe()`.
77
+
78
+ **Check:** internal API routes, endpoint paths, API call counts, internal parameter mappings, sibling service names, version notes, TODOs.
79
+
80
+ **Smell:** "/api/v2/by-state", "Adds a second API call", "API requires `two_year_period`", "(deprecated; use bar_v2)", "TODO: support batch mode", "Used internally by FooService".
81
+
82
+ Prior art: #25.
83
+
84
+ #### 3. Audience leaks
85
+
86
+ **Look in:** every `description` and `.describe()`.
87
+
88
+ **Check:** reader-naming or meta-coaching directed at the LLM rather than describing the tool.
89
+
90
+ **Smell:** "suitable for LLM consumption", "Treat the returned ID as the canonical Y", "Agents should…", "Callers should…", "When you call this tool…", any reference to "LLM", "agent", "Claude", "the model".
91
+
92
+ Prior art: #74. Field-test catches this in its leak audit; this skill is the more thorough pass.
93
+
94
+ #### 4. Defaults
95
+
96
+ **Look in:** every `.default(...)` call in input schemas.
97
+
98
+ **Check:** the default matches the typical caller's case. A default that suited the developer at scaffold time often skews real calls — `limit: 1` makes default-args searches useless, `verbose: true` floods context, `dryRun: false` on a destructive op invites an irreversible accident.
99
+
100
+ **Smell:** dev-convenience values that survived the schema's first draft, dangerous defaults on destructive operations, defaults that contradict the description's framing of typical use.
101
+
102
+ #### 5. Recovery hints
103
+
104
+ **Look in:** `errors: [{ recovery: '…' }]` arrays, `data.recovery.hint` at throw sites in handler bodies.
105
+
106
+ **Check:** the hint directs the *agent* to its next action, not the developer to debugging. "Call `pubmed_search` with a narrower query" beats "Verify the configuration is correct" or "Internal error".
107
+
108
+ **Smell:** "Check the logs", "See documentation", "Contact admin", "Try again later" (with no condition), generic non-actionable text, hints that name internal classes, files, or env vars.
109
+
110
+ #### 6. Output descriptions
111
+
112
+ **Look in:** every field `.describe()` inside `output: z.object({ ... })`.
113
+
114
+ **Check:** the description tells the agent what the *value* is — not just the field name restated, not silent on dynamic shapes.
115
+
116
+ **Smell:**
117
+
118
+ - `name: z.string().describe('Name')` — tautology
119
+ - `description: z.string().describe('Description.')` — tautology
120
+ - `metadata: z.record(z.string(), z.unknown()).describe('Metadata')` — opaque dynamic shape with no hint about keys/values
121
+ - Optional fields with no note on when they're absent
122
+ - Enum fields with no `.describe()` on the variants
123
+
124
+ #### 7. Cross-references
125
+
126
+ **Look in:** tool descriptions, prompt content, recovery hints.
127
+
128
+ **Check:** when one tool/resource is mentioned, *when* to reach for it is explained — and the references cover the relevant siblings, not a partial sample.
129
+
130
+ **Smell:** "Use `foo_search` to find IDs" (no when); a prompt naming 3 of 7 landscape-relevant tools; a tool description listing one sibling but not the others that fit the same workflow.
131
+
132
+ #### 8. Sparsity
133
+
134
+ **Look in:** `output` schemas (especially fields wrapping external API data), `format()` rendering.
135
+
136
+ **Check:** optional upstream fields are acknowledged as such — not implied to always be present. `format()` doesn't print fabricated values for missing fields.
137
+
138
+ **Smell:**
139
+
140
+ - `pmid: z.string().describe('PubMed ID')` when only ~60% of records have one (should be `.optional()` and noted)
141
+ - `format()` printing `**PMID:** undefined`
142
+ - A required field in `output` for an upstream value the API doesn't always return
143
+
144
+ #### 9. Examples
145
+
146
+ **Look in:** parameter `.describe()` text containing "e.g.,", "(e.g. ...)", `.example(...)` calls.
147
+
148
+ **Check:** examples are domain-realistic — real-shaped IDs, real query strings, real values from the upstream domain. One example is usually enough.
149
+
150
+ **Smell:** `.describe('Item ID (e.g., "abc123")')` when real IDs have structure (`NCT12345678`); toy values ("foo", "bar"); padding multiple toy examples instead of one realistic one.
151
+
152
+ #### 10. Structure
153
+
154
+ **Look in:** tool / resource / prompt `description`.
155
+
156
+ **Check:** single cohesive paragraph. No bullet lists, no blank-line-separated sections, no markdown headers inside the description.
157
+
158
+ **Smell:** blank lines (`\n\n`) inside a description string, `- bullet` lines, `## Header` lines, "Operations:\n- foo: …" duplicating an enum's `.describe()` text.
159
+
160
+ Prior art: #33.
161
+
162
+ ### 3. Report
163
+
164
+ Three sections.
165
+
166
+ #### Summary (1 paragraph)
167
+
168
+ Definitions reviewed, categories with findings, total finding count. One sentence on the single most material finding.
169
+
170
+ #### Findings
171
+
172
+ Group by category. Within each category, list each finding:
173
+
174
+ ```
175
+ **<file>:<line> — <category> — (material|nit)**
176
+ Excerpt: `<the offending text>`
177
+ Issue: <one line: what's wrong>
178
+ Fix: <one line: what to change to>
179
+ ```
180
+
181
+ Two-level severity:
182
+
183
+ - **material** — affects agent decisions (will mis-select tool, mis-fill input, mis-handle output, swallow an irrecoverable error)
184
+ - **nit** — polish (style, voice consistency, minor phrasing)
185
+
186
+ Skip categories with no findings — don't list empty headers.
187
+
188
+ #### Options
189
+
190
+ Numbered, cherry-pickable. Map each item to a concrete change in a single file.
191
+
192
+ ```
193
+ 1. Tighten `metadata` description in `pubmed_fetch.tool.ts:42` — explain the dynamic shape (finding #3, material)
194
+ 2. Drop bullet list from `clinicaltrials_get_field_definitions.tool.ts:18` description — single paragraph (finding #5, material)
195
+ 3. Replace toy "abc123" example in `inventory_search.tool.ts:27` with real shape (finding #8, nit)
196
+ ```
197
+
198
+ End with:
199
+
200
+ > Pick by number (e.g. "do 1, 3, 5" or "expand on 2").
201
+
202
+ ## Checklist
203
+
204
+ - [ ] Scope confirmed (whole server / module / specific files)
205
+ - [ ] Inventory built — every `*.tool.ts`, `*.app-tool.ts`, `*.resource.ts`, `*.app-resource.ts`, `*.prompt.ts` listed
206
+ - [ ] Each file walked through all 10 categories (per-file, not 10 separate passes)
207
+ - [ ] **Read-only:** no git, no commits, no changelog edits, no `devcheck`, no wrapup invoked during the audit
208
+ - [ ] Findings carry file:line citation, excerpt, issue, fix
209
+ - [ ] Report: summary → grouped-by-category findings → numbered options
@@ -183,6 +183,8 @@ async handler(input, ctx) {
183
183
  }
184
184
  ```
185
185
 
186
+ **Declare contracts inline on each tool, even when similar across tools.** The contract is part of the tool's documented public surface — reading one tool definition file should give the full picture. Don't extract a shared `errors[]` constant or contract module to deduplicate; per-tool repetition is the intended cost of locality.
187
+
186
188
  **Fallback (no contract entry fits):** throw via factories or plain `Error`.
187
189
 
188
190
  ```ts
@@ -183,6 +183,8 @@ async handler(input, ctx) {
183
183
  }
184
184
  ```
185
185
 
186
+ **Declare contracts inline on each tool, even when similar across tools.** The contract is part of the tool's documented public surface — reading one tool definition file should give the full picture. Don't extract a shared `errors[]` constant or contract module to deduplicate; per-tool repetition is the intended cost of locality.
187
+
186
188
  **Fallback (no contract entry fits):** throw via factories or plain `Error`.
187
189
 
188
190
  ```ts