@cyanheads/mcp-ts-core 0.6.14 → 0.6.16

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.6.14
3
+ **Package:** `@cyanheads/mcp-ts-core` · **Version:** 0.6.16
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.
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.6.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)
8
+ [![Version](https://img.shields.io/badge/Version-0.6.16-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
 
@@ -0,0 +1,26 @@
1
+ ---
2
+ summary: Scaffolded devcheck passes green on a fresh `init` (depcheck wired up); security-pass skill v1.1 expands coverage to resources, prompts, HTTP deployment surface, sampling, roots, telemetry, and schema strictness
3
+ breaking: false
4
+ ---
5
+
6
+ # 0.6.15 — 2026-04-23
7
+
8
+ Fixes a broken first-run experience in scaffolded servers and ships a substantial expansion of the `security-pass` audit skill.
9
+
10
+ ## Fixed
11
+
12
+ - **`templates/package.json`** — added `"depcheck": "^1.4.7"` to `devDependencies`. `scripts/devcheck.ts` (copied verbatim into scaffolds) invokes `node_modules/.bin/depcheck` directly, but the template never declared the dependency — so `bun install && bun run devcheck` on a fresh `@cyanheads/mcp-ts-core init` project failed the Unused Dependencies step with `ENOENT`.
13
+ - **`templates/devcheck.config.json`** — appended `"depcheck"` to `depcheck.ignores`. `depcheck` is CLI-only (never `import`ed), so once added to `devDependencies` it self-flags as unused unless ignored. Resolves [#49](https://github.com/cyanheads/mcp-ts-core/issues/49).
14
+
15
+ ## Changed
16
+
17
+ - **`skills/security-pass/`** — bumped from v1.0 → v1.1. Broadened injection-surface coverage, added HTTP deployment surface as a first-class section, and strengthened several axes:
18
+ - **Axis 1 (injection vector)** now covers resource content, prompt templates, and definition metadata (`description`, `title`, `annotations`, `inputSchema` field descriptions). Adds checks for templated descriptions ("tool poisoning") and mid-session description mutation ("rug-pull").
19
+ - **New deployment-surface section** under "Build the map" for HTTP/SSE transports: bind address, Origin allowlist (DNS rebinding), session ID source and auth binding, unauthenticated route leakage, MCP Authorization spec compliance (PKCE, token `aud`, resource indicators).
20
+ - **Axis 3 (destructive ops)** — requires elicit responses to be schema-validated (the returned payload is LLM-mediated, not user-direct) and consent scoped to a concrete target, not generic.
21
+ - **Axis 5 (input sinks)** — adds sampling responses (`ctx.sample` result is untrusted input), roots-derived paths, schema strictness (`.strict()` on inputs, no `.passthrough()` / `.catchall()` on outputs), and ReDoS-safe regex/glob bounds.
22
+ - **Axis 7 (leakage back)** — broadened beyond `ctx.log` to `console.*`, OpenTelemetry span attributes, Sentry breadcrumbs, and constant-time comparison requirements for secret / token / HMAC equality checks.
23
+ - **Axis 8 (resource bounds)** — adds `JSON.parse` / Zod parse size+depth limits, per-tenant per-tool rate limits, and concurrency caps on long-running tools.
24
+ - **Quick sanity pass** — adds npm provenance check for new security-critical deps.
25
+ - **`fuzzTool`** guidance moved to Step 1 so it runs in parallel with manual axis walks, feeding Axis 5 / Axis 8 triage.
26
+ - Mirrored to `.agents/skills/security-pass/` and `.claude/skills/security-pass/` to clear the devcheck Skills Sync warning introduced with the sync check in 0.6.14.
@@ -0,0 +1,22 @@
1
+ ---
2
+ summary: Linter `describe-on-fields` recurses into nested objects, array elements, and union variants and now covers resource outputs; `maintenance` skill adds Phase C to sync framework scripts from package to consumer
3
+ breaking: false
4
+ ---
5
+
6
+ # 0.6.16 — 2026-04-23
7
+
8
+ Deepens the `describe-on-fields` linter from top-level-only to a full recursive walk, extends it to resource outputs, and closes a silent-drift gap between the package's `scripts/` and consumer projects via a new `maintenance` skill phase.
9
+
10
+ ## Changed
11
+
12
+ - **`src/linter/rules/schema-rules.ts`** — `checkFieldDescriptions` now recurses into nested `z.object` shapes, array element types (skipping primitive elements, where the array-level describe is sufficient), and `z.union` / `z.discriminatedUnion` variants. Diagnostic path syntax: `.key` for object properties, `[]` for array elements, `|<i>` for union variant at index `i`. Each nesting level is evaluated independently — a described container does **not** suppress checks on its children, because MCP clients read the flattened JSON Schema. Optional/nullable/default/readonly/nonoptional wrappers are transparently unwrapped when walking.
13
+ - **`src/linter/rules/resource-rules.ts`** — resource `output` schemas now run through `checkFieldDescriptions` in addition to the existing structural + serializability checks. Previously the rule only applied to tool `input` / `output` and resource `params`, so resources could ship JSON fields without descriptions and pass the linter.
14
+ - **`skills/maintenance/`** — section 5 expanded from "Sync project skills" to "Sync project skills **and scripts**", with a new **Phase C**: compare `node_modules/@cyanheads/mcp-ts-core/scripts/<fixed-list>` against project `scripts/` by content hash and overwrite on mismatch (`build-changelog`, `build`, `check-docs-sync`, `check-skills-sync`, `clean`, `devcheck`, `lint-mcp`, `tree`). Project-specific scripts outside the list are left untouched. Checklist at section-end now requires running the sync and reviewing `git diff scripts/` before committing.
15
+ - **`skills/add-tool/`** + **`skills/design-mcp-server/`** — added **"seed orientation context"** guidance: when a tool's call position makes the agent's next moves predictable (session open/close, state-changing verbs post-confirmation, new-scope entry points), piggyback a compact snapshot of relevant state — recent activity, tracked state, a couple of reference items — alongside the primary result. Cuts a predictable follow-up call *and* primes the LLM on project conventions (recent commits teach commit-message style; recent tags teach versioning format). Gather sub-operations with `Promise.allSettled` so a single sub-failure degrades to a warning on the outer call.
16
+ - **`skills/setup/`** + **`skills/polish-docs-meta/`** — added explicit guardrails against editing `skills/*/SKILL.md` or `skills/*/references/*`. These are synced from `@cyanheads/mcp-ts-core` and overwritten on the next `maintenance` refresh — project-specific agent context belongs in `CLAUDE.md` / `AGENTS.md`.
17
+ - **`examples/mcp-server/tools/definitions/template-data-explorer.app-tool.ts`** — added `.describe()` to the `SaleRowSchema` container so the bundled example stays lint-clean under the new recursive check; without this, consumers scaffolding from the template would see a spurious `describe-on-fields` warning on startup.
18
+ - **`@cloudflare/workers-types`** — `^4.20260423.1` → `^4.20260424.1` (devDependency patch bump).
19
+
20
+ ## Added
21
+
22
+ - **`tests/unit/linter/validate.test.ts`** — five new cases covering the expanded `describe-on-fields` rule: nested object fields, array element fields, primitive array elements (asserts **no** recursion), `z.discriminatedUnion` variant fields (with path `|<i>` assertion), and resource output schemas.
@@ -1 +1 @@
1
- {"version":3,"file":"resource-rules.d.ts","sourceRoot":"","sources":["../../../src/linter/rules/resource-rules.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AASlD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,EAAE,CAiGrE"}
1
+ {"version":3,"file":"resource-rules.d.ts","sourceRoot":"","sources":["../../../src/linter/rules/resource-rules.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AASlD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,EAAE,CAkGrE"}
@@ -96,6 +96,7 @@ export function lintResourceDefinition(def) {
96
96
  diagnostics.push(outputCheck);
97
97
  }
98
98
  else {
99
+ diagnostics.push(...checkFieldDescriptions(d.output, 'output', 'resource', displayName));
99
100
  const outputSerial = checkSchemaSerializable(d.output, 'output', 'resource', displayName);
100
101
  if (outputSerial)
101
102
  diagnostics.push(outputSerial);
@@ -1 +1 @@
1
- {"version":3,"file":"resource-rules.js","sourceRoot":"","sources":["../../../src/linter/rules/resource-rules.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAY;IACjD,MAAM,WAAW,GAAqB,EAAE,CAAC;IACzC,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,MAAM,WAAW,GAAG,OAAO,CAAC,EAAE,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5E,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC;IAChE,MAAM,WAAW,GAAG,IAAI,IAAI,WAAW,CAAC;IAExC,2BAA2B;IAC3B,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,WAAW,CAAC,IAAI,CAAC;YACf,IAAI,EAAE,uBAAuB;YAC7B,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,aAAa,WAAW,6BAA6B;YAC9D,cAAc,EAAE,UAAU;YAC1B,cAAc,EAAE,WAAW;SAC5B,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,sDAAsD;QACtD,MAAM,aAAa,GAAG,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QAC1D,IAAI,aAAa;YAAE,WAAW,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACrD,CAAC;IAED,wBAAwB;IACxB,IAAI,CAAC,CAAC,EAAE,IAAI,IAAI,WAAW,EAAE,CAAC;QAC5B,WAAW,CAAC,IAAI,CAAC;YACf,IAAI,EAAE,uBAAuB;YAC7B,QAAQ,EAAE,SAAS;YACnB,OAAO,EACL,aAAa,WAAW,4EAA4E;gBACpG,2DAA2D;YAC7D,cAAc,EAAE,UAAU;YAC1B,cAAc,EAAE,WAAW;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,6CAA6C;IAC7C,IAAI,CAAC,EAAE,IAAI,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,iBAAiB,CAAC,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;QAC5D,IAAI,OAAO;YAAE,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,cAAc;IACd,IAAI,OAAO,CAAC,EAAE,WAAW,KAAK,QAAQ,IAAI,CAAC,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrE,WAAW,CAAC,IAAI,CAAC;YACf,IAAI,EAAE,sBAAsB;YAC5B,QAAQ,EAAE,SAAS;YACnB,OAAO,EAAE,aAAa,WAAW,uBAAuB;YACxD,cAAc,EAAE,UAAU;YAC1B,cAAc,EAAE,WAAW;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,UAAU;IACV,IAAI,OAAO,CAAC,EAAE,OAAO,KAAK,UAAU,EAAE,CAAC;QACrC,WAAW,CAAC,IAAI,CAAC;YACf,IAAI,EAAE,kBAAkB;YACxB,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,aAAa,WAAW,kCAAkC;YACnE,cAAc,EAAE,UAAU;YAC1B,cAAc,EAAE,WAAW;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,gBAAgB,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;QAClF,IAAI,WAAW,EAAE,CAAC;YAChB,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,WAAW,CAAC,IAAI,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;YACzF,MAAM,YAAY,GAAG,uBAAuB,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;YAC1F,IAAI,YAAY;gBAAE,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAEjD,oEAAoE;YACpE,IAAI,WAAW,EAAE,CAAC;gBAChB,WAAW,CAAC,IAAI,CAAC,GAAG,4BAA4B,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;YACxF,CAAC;QACH,CAAC;IACH,CAAC;IAED,yBAAyB;IACzB,IAAI,CAAC,EAAE,IAAI,KAAK,SAAS,EAAE,CAAC;QAC1B,WAAW,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;IACvE,CAAC;IAED,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,gBAAgB,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;QAClF,IAAI,WAAW,EAAE,CAAC;YAChB,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,MAAM,YAAY,GAAG,uBAAuB,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;YAC1F,IAAI,YAAY;gBAAE,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,sGAAsG;AACtG,SAAS,wBAAwB,CAAC,QAAgB;IAChD,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,QAAQ,CAAC,4BAA4B,CAAC,EAAE,CAAC;QACpE,yFAAyF;QACzF,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/B,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YACtD,IAAI,IAAI;gBAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,SAAS,4BAA4B,CACnC,QAAgB,EAChB,MAAe,EACf,YAAoB;IAEpB,MAAM,YAAY,GAAG,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IACxD,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEzC,MAAM,KAAK,GAAI,MAAiC,CAAC,KAAK,CAAC;IACvD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAE/C,MAAM,WAAW,GAAqB,EAAE,CAAC;IACzC,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;QACnC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,WAAW,CAAC,IAAI,CAAC;gBACf,IAAI,EAAE,uBAAuB;gBAC7B,QAAQ,EAAE,OAAO;gBACjB,OAAO,EACL,aAAa,YAAY,6BAA6B,OAAO,2CAA2C;oBACxG,uBAAuB,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,iDAAiD;gBACpG,cAAc,EAAE,UAAU;gBAC1B,cAAc,EAAE,YAAY;aAC7B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,QAAgB,EAAE,IAAY;IACtD,8BAA8B;IAC9B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,IAAI,KAAK,GAAG;YAAE,KAAK,EAAE,CAAC;QAC1B,IAAI,IAAI,KAAK,GAAG;YAAE,KAAK,EAAE,CAAC;QAC1B,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACd,OAAO;gBACL,IAAI,EAAE,oBAAoB;gBAC1B,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,aAAa,IAAI,wDAAwD,QAAQ,IAAI;gBAC9F,cAAc,EAAE,UAAU;gBAC1B,cAAc,EAAE,IAAI;aACrB,CAAC;QACJ,CAAC;IACH,CAAC;IACD,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAChB,OAAO;YACL,IAAI,EAAE,oBAAoB;YAC1B,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,aAAa,IAAI,qDAAqD,QAAQ,IAAI;YAC3F,cAAc,EAAE,UAAU;YAC1B,cAAc,EAAE,IAAI;SACrB,CAAC;IACJ,CAAC;IAED,yCAAyC;IACzC,IAAI,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,OAAO;YACL,IAAI,EAAE,oBAAoB;YAC1B,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,aAAa,IAAI,0DAA0D,QAAQ,IAAI;YAChG,cAAc,EAAE,UAAU;YAC1B,cAAc,EAAE,IAAI;SACrB,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"resource-rules.js","sourceRoot":"","sources":["../../../src/linter/rules/resource-rules.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAY;IACjD,MAAM,WAAW,GAAqB,EAAE,CAAC;IACzC,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,MAAM,WAAW,GAAG,OAAO,CAAC,EAAE,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5E,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC;IAChE,MAAM,WAAW,GAAG,IAAI,IAAI,WAAW,CAAC;IAExC,2BAA2B;IAC3B,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,WAAW,CAAC,IAAI,CAAC;YACf,IAAI,EAAE,uBAAuB;YAC7B,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,aAAa,WAAW,6BAA6B;YAC9D,cAAc,EAAE,UAAU;YAC1B,cAAc,EAAE,WAAW;SAC5B,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,sDAAsD;QACtD,MAAM,aAAa,GAAG,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QAC1D,IAAI,aAAa;YAAE,WAAW,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACrD,CAAC;IAED,wBAAwB;IACxB,IAAI,CAAC,CAAC,EAAE,IAAI,IAAI,WAAW,EAAE,CAAC;QAC5B,WAAW,CAAC,IAAI,CAAC;YACf,IAAI,EAAE,uBAAuB;YAC7B,QAAQ,EAAE,SAAS;YACnB,OAAO,EACL,aAAa,WAAW,4EAA4E;gBACpG,2DAA2D;YAC7D,cAAc,EAAE,UAAU;YAC1B,cAAc,EAAE,WAAW;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,6CAA6C;IAC7C,IAAI,CAAC,EAAE,IAAI,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,iBAAiB,CAAC,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;QAC5D,IAAI,OAAO;YAAE,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,cAAc;IACd,IAAI,OAAO,CAAC,EAAE,WAAW,KAAK,QAAQ,IAAI,CAAC,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrE,WAAW,CAAC,IAAI,CAAC;YACf,IAAI,EAAE,sBAAsB;YAC5B,QAAQ,EAAE,SAAS;YACnB,OAAO,EAAE,aAAa,WAAW,uBAAuB;YACxD,cAAc,EAAE,UAAU;YAC1B,cAAc,EAAE,WAAW;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,UAAU;IACV,IAAI,OAAO,CAAC,EAAE,OAAO,KAAK,UAAU,EAAE,CAAC;QACrC,WAAW,CAAC,IAAI,CAAC;YACf,IAAI,EAAE,kBAAkB;YACxB,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,aAAa,WAAW,kCAAkC;YACnE,cAAc,EAAE,UAAU;YAC1B,cAAc,EAAE,WAAW;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,gBAAgB,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;QAClF,IAAI,WAAW,EAAE,CAAC;YAChB,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,WAAW,CAAC,IAAI,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;YACzF,MAAM,YAAY,GAAG,uBAAuB,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;YAC1F,IAAI,YAAY;gBAAE,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAEjD,oEAAoE;YACpE,IAAI,WAAW,EAAE,CAAC;gBAChB,WAAW,CAAC,IAAI,CAAC,GAAG,4BAA4B,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;YACxF,CAAC;QACH,CAAC;IACH,CAAC;IAED,yBAAyB;IACzB,IAAI,CAAC,EAAE,IAAI,KAAK,SAAS,EAAE,CAAC;QAC1B,WAAW,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;IACvE,CAAC;IAED,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,gBAAgB,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;QAClF,IAAI,WAAW,EAAE,CAAC;YAChB,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,WAAW,CAAC,IAAI,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;YACzF,MAAM,YAAY,GAAG,uBAAuB,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;YAC1F,IAAI,YAAY;gBAAE,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,sGAAsG;AACtG,SAAS,wBAAwB,CAAC,QAAgB;IAChD,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,QAAQ,CAAC,4BAA4B,CAAC,EAAE,CAAC;QACpE,yFAAyF;QACzF,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/B,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YACtD,IAAI,IAAI;gBAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,SAAS,4BAA4B,CACnC,QAAgB,EAChB,MAAe,EACf,YAAoB;IAEpB,MAAM,YAAY,GAAG,wBAAwB,CAAC,QAAQ,CAAC,CAAC;IACxD,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEzC,MAAM,KAAK,GAAI,MAAiC,CAAC,KAAK,CAAC;IACvD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAE/C,MAAM,WAAW,GAAqB,EAAE,CAAC;IACzC,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;QACnC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,WAAW,CAAC,IAAI,CAAC;gBACf,IAAI,EAAE,uBAAuB;gBAC7B,QAAQ,EAAE,OAAO;gBACjB,OAAO,EACL,aAAa,YAAY,6BAA6B,OAAO,2CAA2C;oBACxG,uBAAuB,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,iDAAiD;gBACpG,cAAc,EAAE,UAAU;gBAC1B,cAAc,EAAE,YAAY;aAC7B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,QAAgB,EAAE,IAAY;IACtD,8BAA8B;IAC9B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,IAAI,KAAK,GAAG;YAAE,KAAK,EAAE,CAAC;QAC1B,IAAI,IAAI,KAAK,GAAG;YAAE,KAAK,EAAE,CAAC;QAC1B,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACd,OAAO;gBACL,IAAI,EAAE,oBAAoB;gBAC1B,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,aAAa,IAAI,wDAAwD,QAAQ,IAAI;gBAC9F,cAAc,EAAE,UAAU;gBAC1B,cAAc,EAAE,IAAI;aACrB,CAAC;QACJ,CAAC;IACH,CAAC;IACD,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAChB,OAAO;YACL,IAAI,EAAE,oBAAoB;YAC1B,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,aAAa,IAAI,qDAAqD,QAAQ,IAAI;YAC3F,cAAc,EAAE,UAAU;YAC1B,cAAc,EAAE,IAAI;SACrB,CAAC;IACJ,CAAC;IAED,yCAAyC;IACzC,IAAI,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,OAAO;YACL,IAAI,EAAE,oBAAoB;YAC1B,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,aAAa,IAAI,0DAA0D,QAAQ,IAAI;YAChG,cAAc,EAAE,UAAU;YAC1B,cAAc,EAAE,IAAI;SACrB,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -11,8 +11,14 @@ import type { LintDiagnostic } from '../types.js';
11
11
  */
12
12
  export declare function checkIsZodObject(schema: unknown, fieldName: string, definitionType: LintDiagnostic['definitionType'], definitionName: string): LintDiagnostic | null;
13
13
  /**
14
- * Checks that all top-level fields in a ZodObject have `.describe()` set.
15
- * Framework convention: all fields need `.describe()` for LLM discoverability.
14
+ * Checks that all fields in a ZodObject have `.describe()` set, recursing into
15
+ * nested objects, array element types, and union/discriminatedUnion variants.
16
+ * Framework convention: every field the LLM reads should carry a description.
17
+ *
18
+ * Path syntax in diagnostic messages:
19
+ * - `.key` for object properties
20
+ * - `[]` for array element types
21
+ * - `|<i>` for union / discriminatedUnion variant at index i
16
22
  */
17
23
  export declare function checkFieldDescriptions(schema: unknown, fieldName: string, definitionType: LintDiagnostic['definitionType'], definitionName: string): LintDiagnostic[];
18
24
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"schema-rules.d.ts","sourceRoot":"","sources":["../../../src/linter/rules/schema-rules.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,cAAc,CAAC,gBAAgB,CAAC,EAChD,cAAc,EAAE,MAAM,GACrB,cAAc,GAAG,IAAI,CAavB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,cAAc,CAAC,gBAAgB,CAAC,EAChD,cAAc,EAAE,MAAM,GACrB,cAAc,EAAE,CAwBlB;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,cAAc,CAAC,gBAAgB,CAAC,EAChD,cAAc,EAAE,MAAM,GACrB,cAAc,GAAG,IAAI,CAkBvB"}
1
+ {"version":3,"file":"schema-rules.d.ts","sourceRoot":"","sources":["../../../src/linter/rules/schema-rules.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,cAAc,CAAC,gBAAgB,CAAC,EAChD,cAAc,EAAE,MAAM,GACrB,cAAc,GAAG,IAAI,CAavB;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,cAAc,CAAC,gBAAgB,CAAC,EAChD,cAAc,EAAE,MAAM,GACrB,cAAc,EAAE,CAWlB;AA+FD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,cAAc,CAAC,gBAAgB,CAAC,EAChD,cAAc,EAAE,MAAM,GACrB,cAAc,GAAG,IAAI,CAkBvB"}
@@ -23,8 +23,14 @@ export function checkIsZodObject(schema, fieldName, definitionType, definitionNa
23
23
  return null;
24
24
  }
25
25
  /**
26
- * Checks that all top-level fields in a ZodObject have `.describe()` set.
27
- * Framework convention: all fields need `.describe()` for LLM discoverability.
26
+ * Checks that all fields in a ZodObject have `.describe()` set, recursing into
27
+ * nested objects, array element types, and union/discriminatedUnion variants.
28
+ * Framework convention: every field the LLM reads should carry a description.
29
+ *
30
+ * Path syntax in diagnostic messages:
31
+ * - `.key` for object properties
32
+ * - `[]` for array element types
33
+ * - `|<i>` for union / discriminatedUnion variant at index i
28
34
  */
29
35
  export function checkFieldDescriptions(schema, fieldName, definitionType, definitionName) {
30
36
  if (!isZodObject(schema))
@@ -32,20 +38,85 @@ export function checkFieldDescriptions(schema, fieldName, definitionType, defini
32
38
  const diagnostics = [];
33
39
  const shape = schema.shape;
34
40
  for (const [key, field] of Object.entries(shape)) {
35
- const zodField = field;
36
- // Walk through optional/nullable/default wrappers to find the inner description
37
- if (!hasDescription(zodField)) {
38
- diagnostics.push({
39
- rule: 'describe-on-fields',
40
- severity: 'warning',
41
- message: `${definitionType} '${definitionName}' ${fieldName}.${key} is missing .describe(). ` +
42
- 'Add .describe() to improve LLM tool-use quality.',
43
- definitionType,
44
- definitionName,
41
+ walkField(field, `${fieldName}.${key}`, diagnostics, definitionType, definitionName);
42
+ }
43
+ return diagnostics;
44
+ }
45
+ /**
46
+ * Emits a diagnostic when the field lacks a description, then recurses into
47
+ * compound types (object, array, union) so inner fields get the same check.
48
+ * A described container does NOT suppress checks on its children — each level
49
+ * is evaluated independently because LLMs read the flattened JSON Schema.
50
+ */
51
+ function walkField(field, path, diagnostics, definitionType, definitionName) {
52
+ if (!hasDescription(field)) {
53
+ diagnostics.push({
54
+ rule: 'describe-on-fields',
55
+ severity: 'warning',
56
+ message: `${definitionType} '${definitionName}' ${path} is missing .describe(). ` +
57
+ 'Add .describe() to improve LLM tool-use quality.',
58
+ definitionType,
59
+ definitionName,
60
+ });
61
+ }
62
+ recurseIntoCompound(field, path, diagnostics, definitionType, definitionName);
63
+ }
64
+ /**
65
+ * Strips optional/nullable/default/readonly/nonoptional wrappers to find the
66
+ * core type, then recurses into object shapes, array elements, and union
67
+ * options. Non-compound cores (primitives, literals) terminate recursion.
68
+ * Primitive array elements are skipped — array-level describe is sufficient.
69
+ */
70
+ function recurseIntoCompound(field, path, diagnostics, definitionType, definitionName) {
71
+ const core = unwrapWrappers(field);
72
+ if (!core || typeof core !== 'object')
73
+ return;
74
+ const def = core._zod?.def;
75
+ if (!def)
76
+ return;
77
+ if (def.type === 'object') {
78
+ const shape = core.shape;
79
+ for (const [key, inner] of Object.entries(shape)) {
80
+ walkField(inner, `${path}.${key}`, diagnostics, definitionType, definitionName);
81
+ }
82
+ return;
83
+ }
84
+ if (def.type === 'array') {
85
+ const element = def.element;
86
+ if (element && isCompound(element)) {
87
+ walkField(element, `${path}[]`, diagnostics, definitionType, definitionName);
88
+ }
89
+ return;
90
+ }
91
+ if (def.type === 'union') {
92
+ const options = def.options;
93
+ if (Array.isArray(options)) {
94
+ options.forEach((option, i) => {
95
+ walkField(option, `${path}|${i}`, diagnostics, definitionType, definitionName);
45
96
  });
46
97
  }
47
98
  }
48
- return diagnostics;
99
+ }
100
+ /** Recursively strips optional/nullable/default/readonly/nonoptional wrappers. */
101
+ function unwrapWrappers(field) {
102
+ if (!field || typeof field !== 'object')
103
+ return field;
104
+ const def = field._zod?.def;
105
+ if (!def)
106
+ return field;
107
+ const wrapperTypes = new Set(['optional', 'nullable', 'default', 'readonly', 'nonoptional']);
108
+ if (def.type && wrapperTypes.has(def.type) && def.innerType) {
109
+ return unwrapWrappers(def.innerType);
110
+ }
111
+ return field;
112
+ }
113
+ /** True if the (unwrapped) field is an object, array, or union — a compound type worth recursing into. */
114
+ function isCompound(field) {
115
+ const core = unwrapWrappers(field);
116
+ if (!core || typeof core !== 'object')
117
+ return false;
118
+ const type = core._zod?.def?.type;
119
+ return type === 'object' || type === 'array' || type === 'union';
49
120
  }
50
121
  /**
51
122
  * Checks that a Zod schema can be converted to JSON Schema.
@@ -1 +1 @@
1
- {"version":3,"file":"schema-rules.js","sourceRoot":"","sources":["../../../src/linter/rules/schema-rules.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema-rules.js","sourceRoot":"","sources":["../../../src/linter/rules/schema-rules.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- {"level":50,"time":1776980769962,"env":"testing","version":"0.0.0-test","pid":37808,"requestId":"OVT1K-CNGIB","timestamp":"2026-04-23T21:46:09.961Z","operation":"HandleToolRequest","input":{"message":"blocked"},"critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"0a31a16b8e1ef00f4861dc55307c97ca3c475ea7e0c056a72f9d9abc9e666565","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"errorData":{"sessionId":"0a31a16b8e1ef00f4861dc55307c97ca3c475ea7e0c056a72f9d9abc9e666565","toolName":"scoped_echo","input":{"message":"blocked"},"requestId":"OVT1K-CNGIB","timestamp":"2026-04-23T21:46:09.961Z","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:68: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:168:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:101:42)\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":1776980770606,"env":"testing","version":"0.6.14","pid":37812,"requestId":"VSJZ5-S5BH9","timestamp":"2026-04-23T21:46:10.606Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"VSJZ5-S5BH9","timestamp":"2026-04-23T21:46:10.606Z","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:119: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:168: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":1776980770622,"env":"testing","version":"0.6.14","pid":37812,"requestId":"9YIAG-QSYTE","timestamp":"2026-04-23T21:46:10.622Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"9YIAG-QSYTE","timestamp":"2026-04-23T21:46:10.622Z","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:168: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":1776980770626,"env":"testing","version":"0.6.14","pid":37812,"requestId":"V19S7-NN43O","timestamp":"2026-04-23T21:46:10.626Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"GET","errorData":{"path":"/mcp","method":"GET","requestId":"V19S7-NN43O","timestamp":"2026-04-23T21:46:10.626Z","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:119: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:168: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":1776999021032,"env":"testing","version":"0.0.0-test","pid":86369,"requestId":"5A3IA-UDJMH","timestamp":"2026-04-24T02:50:21.031Z","operation":"HandleToolRequest","input":{"message":"blocked"},"critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"23bfa95f73c6306b49af5af611c211f5627953a27b2672c47a26914b29a3f59a","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"errorData":{"sessionId":"23bfa95f73c6306b49af5af611c211f5627953a27b2672c47a26914b29a3f59a","toolName":"scoped_echo","input":{"message":"blocked"},"requestId":"5A3IA-UDJMH","timestamp":"2026-04-24T02:50:21.031Z","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:68: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:168:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:101:42)\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":1776999021988,"env":"testing","version":"0.6.16","pid":86376,"requestId":"FYH8P-9160M","timestamp":"2026-04-24T02:50:21.987Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"FYH8P-9160M","timestamp":"2026-04-24T02:50:21.987Z","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:119: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:168: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":1776999022001,"env":"testing","version":"0.6.16","pid":86376,"requestId":"81W3G-Q7S7S","timestamp":"2026-04-24T02:50:22.001Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"81W3G-Q7S7S","timestamp":"2026-04-24T02:50:22.001Z","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:168: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":1776999022005,"env":"testing","version":"0.6.16","pid":86376,"requestId":"LU31O-DL3UV","timestamp":"2026-04-24T02:50:22.005Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"GET","errorData":{"path":"/mcp","method":"GET","requestId":"LU31O-DL3UV","timestamp":"2026-04-24T02:50:22.005Z","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:119: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:168: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":1776980769962,"env":"testing","version":"0.0.0-test","pid":37808,"requestId":"OVT1K-CNGIB","timestamp":"2026-04-23T21:46:09.961Z","operation":"HandleToolRequest","input":{"message":"blocked"},"critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"0a31a16b8e1ef00f4861dc55307c97ca3c475ea7e0c056a72f9d9abc9e666565","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"errorData":{"sessionId":"0a31a16b8e1ef00f4861dc55307c97ca3c475ea7e0c056a72f9d9abc9e666565","toolName":"scoped_echo","input":{"message":"blocked"},"requestId":"OVT1K-CNGIB","timestamp":"2026-04-23T21:46:09.961Z","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:68: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:168:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:101:42)\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":1776980770606,"env":"testing","version":"0.6.14","pid":37812,"requestId":"VSJZ5-S5BH9","timestamp":"2026-04-23T21:46:10.606Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"VSJZ5-S5BH9","timestamp":"2026-04-23T21:46:10.606Z","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:119: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:168: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":1776980770622,"env":"testing","version":"0.6.14","pid":37812,"requestId":"9YIAG-QSYTE","timestamp":"2026-04-23T21:46:10.622Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"9YIAG-QSYTE","timestamp":"2026-04-23T21:46:10.622Z","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:168: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":1776980770626,"env":"testing","version":"0.6.14","pid":37812,"requestId":"V19S7-NN43O","timestamp":"2026-04-23T21:46:10.626Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"GET","errorData":{"path":"/mcp","method":"GET","requestId":"V19S7-NN43O","timestamp":"2026-04-23T21:46:10.626Z","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:119: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:168: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":1776999021032,"env":"testing","version":"0.0.0-test","pid":86369,"requestId":"5A3IA-UDJMH","timestamp":"2026-04-24T02:50:21.031Z","operation":"HandleToolRequest","input":{"message":"blocked"},"critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"23bfa95f73c6306b49af5af611c211f5627953a27b2672c47a26914b29a3f59a","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"errorData":{"sessionId":"23bfa95f73c6306b49af5af611c211f5627953a27b2672c47a26914b29a3f59a","toolName":"scoped_echo","input":{"message":"blocked"},"requestId":"5A3IA-UDJMH","timestamp":"2026-04-24T02:50:21.031Z","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:68: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:168:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:101:42)\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":1776999021988,"env":"testing","version":"0.6.16","pid":86376,"requestId":"FYH8P-9160M","timestamp":"2026-04-24T02:50:21.987Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"FYH8P-9160M","timestamp":"2026-04-24T02:50:21.987Z","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:119: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:168: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":1776999022001,"env":"testing","version":"0.6.16","pid":86376,"requestId":"81W3G-Q7S7S","timestamp":"2026-04-24T02:50:22.001Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"81W3G-Q7S7S","timestamp":"2026-04-24T02:50:22.001Z","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:168: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":1776999022005,"env":"testing","version":"0.6.16","pid":86376,"requestId":"LU31O-DL3UV","timestamp":"2026-04-24T02:50:22.005Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"GET","errorData":{"path":"/mcp","method":"GET","requestId":"LU31O-DL3UV","timestamp":"2026-04-24T02:50:22.005Z","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:119: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:168: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."}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyanheads/mcp-ts-core",
3
- "version": "0.6.14",
3
+ "version": "0.6.16",
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",
@@ -161,7 +161,7 @@
161
161
  },
162
162
  "devDependencies": {
163
163
  "@biomejs/biome": "2.4.13",
164
- "@cloudflare/workers-types": "^4.20260423.1",
164
+ "@cloudflare/workers-types": "^4.20260424.1",
165
165
  "@hono/otel": "^1.1.1",
166
166
  "@opentelemetry/exporter-metrics-otlp-http": "^0.215.0",
167
167
  "@opentelemetry/exporter-trace-otlp-http": "^0.215.0",
@@ -277,6 +277,8 @@ return {
277
277
  };
278
278
  ```
279
279
 
280
+ **Seed orientation context when the next moves are predictable.** Piggybacking a compact snapshot alongside the primary result — recent activity, tracked state, a few reference items — does two things: cuts a predictable follow-up call *and* primes the LLM on the project's conventions (recent commits teach the commit-message style the agent should match; recent tags teach the versioning format; reference records teach the naming format). Natural fits include session open/close tools, state-changing verbs where post-action confirmation helps, and entry points that drop the agent into a new scope. Gather sub-operations with `Promise.allSettled` and surface per-component failures as a warnings array rather than failing the outer call. See `design-mcp-server`'s **Output design** for the full principle.
281
+
280
282
  ### Defend against empty values from form-based clients
281
283
 
282
284
  LLM clients (Claude, Cursor, etc.) only send populated fields. **Form-based clients** (MCP Inspector, web UIs) submit the full schema shape — optional object fields arrive with empty-string inner values instead of `undefined`. Zod's `.optional()` only rejects `undefined`, so `{ minDate: "", maxDate: "" }` passes validation and reaches the handler.
@@ -255,6 +255,7 @@ The output schema and `format` function control what the LLM reads back. Design
255
255
  - **Include IDs and references for chaining.** If the agent might act on a result, return the identifiers it needs for follow-up tool calls.
256
256
  - **Curate vs. pass-through depends on domain.** Medical/scientific data — don't trim fields that could alter correctness. CRUD responses — return what the agent needs, not the full API payload. Match fidelity to consequence.
257
257
  - **Surface what was done, not just results.** After a write operation, include the post-state so the LLM can chain without an extra round trip.
258
+ - **Seed orientation context alongside the primary result.** When a tool's call position makes the agent's next moves predictable, attaching a compact snapshot of relevant state — recent activity, tracked state, a couple of reference items — both saves round-trips *and* **primes the LLM on the project's patterns**. Surfacing recent commits teaches the commit-message style the agent should match when it later writes one; recent tags teach the versioning convention; reference records teach the naming format. Common fits: tools that open or close a session (set working dir, wrap-up), state-changing verbs where the caller wants post-action confirmation (commit, push, merge), entry points that drop the agent into a new scope (clone, checkout). Gather sub-operations in parallel with `Promise.allSettled` so a single failure degrades to a warning rather than tanking the outer call.
258
259
  - **Communicate filtering.** If the tool silently excluded content, tell the LLM what was excluded and how to get it back. The agent can't act on what it doesn't know about.
259
260
 
260
261
  ```ts
@@ -79,9 +79,9 @@ Cross-reference each finding against the server's code. Collect adoption opportu
79
79
 
80
80
  Skip the mechanical diff — consumer customizations create too much noise to filter. Instead, read end-to-end with fresh eyes, mentally comparing against the current `CLAUDE.md`. Look for: new conventions, updated skill references, expanded checklists, new callouts, clearer explanations, restructured sections. Present findings; let the user cherry-pick what to adopt. Never auto-merge — the consumer's file is theirs.
81
81
 
82
- ### 5. Sync project skills
82
+ ### 5. Sync project skills and scripts
83
83
 
84
- Skills flow in two hops: package → project `skills/` → agent directories.
84
+ Skills flow in two hops: package → project `skills/` → agent directories. Framework scripts flow in one: package → project `scripts/`. Both drift silently unless resynced.
85
85
 
86
86
  **Phase A — Package → Project `skills/`**
87
87
 
@@ -116,7 +116,27 @@ For each agent directory that exists:
116
116
 
117
117
  If no agent directory exists, skip Phase B — the project hasn't opted in to per-agent skill copies.
118
118
 
119
- **Report** which skills were added/updated in Phase A (with version deltas) and which agent directories were refreshed in Phase B. The user needs to know what new guidance is now available and where.
119
+ **Phase C Package scripts Project `scripts/`**
120
+
121
+ The `init` CLI scaffolds a fixed set of framework scripts into consumer projects — these underpin `bun run build`, `bun run devcheck`, `bun run lint:mcp`, `bun run tree`, and the changelog build. They drift silently when the framework updates them. Compare by content hash and overwrite on mismatch:
122
+
123
+ ```bash
124
+ for s in build-changelog.ts build.ts check-docs-sync.ts check-skills-sync.ts clean.ts devcheck.ts lint-mcp.ts tree.ts; do
125
+ src="node_modules/@cyanheads/mcp-ts-core/scripts/$s"
126
+ dst="scripts/$s"
127
+ [ -f "$src" ] || continue
128
+ if [ ! -f "$dst" ] || ! cmp -s "$src" "$dst"; then
129
+ cp "$src" "$dst"
130
+ echo "synced: $s"
131
+ fi
132
+ done
133
+ ```
134
+
135
+ Do not touch scripts in `scripts/` that aren't in the list above — those are project-specific (custom deploy, codegen, etc.).
136
+
137
+ If the consumer customized a framework script, the overwrite discards those changes. `git diff scripts/` surfaces the replacements immediately after the sync runs — review the diff before committing, and use `git restore scripts/<file>` if a specific local customization needs to be preserved.
138
+
139
+ **Report** which skills were added/updated in Phase A (with version deltas), which agent directories were refreshed in Phase B, and which scripts were resynced in Phase C. The user needs to know what new guidance and tooling is now in play.
120
140
 
121
141
  ### 6. Adopt changes in the codebase
122
142
 
@@ -162,6 +182,7 @@ Present a concise numbered summary to the user:
162
182
  - [ ] Adoption opportunities identified and applied
163
183
  - [ ] Project `skills/` synced from package (Phase A), with a change report
164
184
  - [ ] Agent skill directories (`.claude/skills/`, `.agents/skills/`, etc.) refreshed from project `skills/` (Phase B)
185
+ - [ ] Framework `scripts/` resynced from package via content-hash compare (Phase C), with a change report; `git diff scripts/` reviewed before committing
165
186
  - [ ] `bun run rebuild` succeeds
166
187
  - [ ] `bun run devcheck` passes (includes audit + outdated)
167
188
  - [ ] `bun run test` passes
@@ -54,7 +54,7 @@ The bold header tagline (the `<b>` text inside the first `<p>`) must match the `
54
54
 
55
55
  ### 3. Agent Protocol (CLAUDE.md / AGENTS.md)
56
56
 
57
- Update the project's agent protocol file to reflect the actual server.
57
+ Update the project's agent protocol file to reflect the actual server. Scope is the project-root `CLAUDE.md` / `AGENTS.md` only — **do not edit `skills/*/SKILL.md` or their `references/` files**. Those are external skill files synced from `@cyanheads/mcp-ts-core` and get overwritten on the next `maintenance` refresh.
58
58
 
59
59
  Read `references/agent-protocol.md` for the full update checklist, then review the current file and address what's stale or missing:
60
60
 
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: security-pass
3
3
  description: >
4
- Review an MCP server for common security gaps: tool output as LLM injection, scope blast radius, destructive ops without consent, upstream auth shape, input sinks, tenant isolation, leaked data, unbounded resources. Use before a release, after a batch of handler changes, or when the user asks for a security review, audit, or hardening pass. Produces grouped findings and a numbered options list.
4
+ Review an MCP server for common security gaps: LLM-facing surfaces as injection vector (tools, resources, prompts, descriptions), scope blast radius, destructive ops without consent, upstream auth shape, input sinks (URL / path / roots / shell / sampling / schema strictness / ReDoS), tenant isolation, leakage through errors and telemetry, unbounded resources, and HTTP-mode deployment surface. Use before a release, after a batch of handler changes, or when the user asks for a security review, audit, or hardening pass. Produces grouped findings and a numbered options list.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.0"
7
+ version: "1.1"
8
8
  audience: external
9
9
  type: audit
10
10
  ---
@@ -35,33 +35,53 @@ Gather before starting. Ask if unclear:
35
35
 
36
36
  ### 1. Build the map
37
37
 
38
- Surface what you're auditing before diving in.
38
+ Surface what you're auditing before diving in. Paths below assume the `mcp-ts-core` layout — adjust to your repo.
39
39
 
40
40
  ```bash
41
41
  find src/mcp-server/tools/definitions -name "*.tool.ts" | sort
42
42
  find src/mcp-server/resources/definitions -name "*.resource.ts" 2>/dev/null | sort
43
+ find src/mcp-server/prompts/definitions -name "*.prompt.ts" 2>/dev/null | sort
43
44
  find src/services -maxdepth 1 -mindepth 1 -type d | sort
44
45
  ```
45
46
 
46
- Note: tool count, auth mode, storage provider, upstream APIs, which tools have `destructiveHint`, which services hold module-scope state.
47
+ Note: tool / resource / prompt counts, auth mode, storage provider, upstream APIs, which tools have `destructiveHint`, which handlers use `ctx.sample` or `ctx.elicit`, which services hold module-scope state, whether the server reads `roots`.
48
+
49
+ **If transport is streamable HTTP or SSE**, also capture:
50
+
51
+ - Bind address (`127.0.0.1` for local, or `0.0.0.0` / public interface?)
52
+ - Origin allowlist (DNS rebinding mitigation) — configured, or wildcard / missing?
53
+ - Session ID source (framework CSPRNG, or builder-supplied?) and binding to auth identity
54
+ - Any unauthenticated routes (`/healthz`, `/sse`, metadata endpoints) — do they leak tool lists or tenant hints?
55
+ - MCP Authorization spec: if implemented, PKCE enforced, token audience (`aud`) checked, resource indicators used
47
56
 
48
57
  Use `TaskCreate` — one task per axis. Mark complete as you go.
49
58
 
59
+ **Run `fuzzTool` in parallel.** `@cyanheads/mcp-ts-core/testing/fuzz` catches crashes, memory leaks, and prototype pollution automatically on each tool — start it now so results are ready when you reach Axis 5.
60
+
50
61
  ### 2. Walk the eight axes
51
62
 
52
- #### Axis 1 — Tool output as LLM injection vector
63
+ #### Axis 1 — LLM-facing surfaces as injection vector
64
+
65
+ Anything the server sends to the client that reaches the LLM's context is a potential injection surface: tool output, resource content, prompt text, and the metadata the LLM reads to decide what to call. Relayed upstream content (tickets, scraped text, emails, DB rows) can carry adversarial instructions even when your code is honest.
53
66
 
54
- Tool output enters the next LLM turn. Relayed upstream content (tickets, scraped text, emails, DB rows) can contain adversarial instructions even when your code is honest.
67
+ **Look in:**
55
68
 
56
- **Look in:** every `*.tool.ts` — `output` schema + `format()`.
69
+ - Every `*.tool.ts` — `output` schema + `format()`
70
+ - Every `*.resource.ts` — content returned from `resources/read`
71
+ - Every `*.prompt.ts` — templated message content
72
+ - Every definition file — `description`, `title`, `annotations`, and `inputSchema` field descriptions (templated from untrusted data?)
57
73
 
58
74
  **Check:**
59
75
 
60
- - Handlers that return raw upstream text without structural framing?
76
+ - Handlers that return raw upstream text / DB rows without structural framing?
61
77
  - Does `format()` wrap untrusted content in delimiters (blockquote, fenced code, `<data>` tags)?
62
78
  - Output schema distinguishes "data" fields from free-form text?
79
+ - Resource content (`resources/read`) framed the same way tool output is?
80
+ - Prompt templates interpolate untrusted data without escaping — treating tenant-controlled strings as trusted instructions?
81
+ - Tool / resource / prompt **descriptions** templated from runtime data? Static strings are safer; templated descriptions enable "tool poisoning" (adversarial metadata steering the LLM toward a dangerous tool).
82
+ - Descriptions mutated mid-session? Rug-pull surface: client approved the v1 description, server now advertises v2 behavior.
63
83
 
64
- **Smell:** `return { body: await fetch(url).then(r => r.text()) }` rendered directly in `format()`.
84
+ **Smell:** `return { body: await fetch(url).then(r => r.text()) }` rendered directly in `format()`. Or: `description: \`Look up ${tenant.customLabel}\`` where `customLabel` is tenant-supplied.
65
85
 
66
86
  #### Axis 2 — Scope granularity
67
87
 
@@ -96,8 +116,10 @@ grep -rn "ctx.elicit" src/mcp-server/tools/definitions/
96
116
 
97
117
  - Each destructive handler calls `ctx.elicit` before the side effect?
98
118
  - Fallback when client doesn't support elicit — refuses, not silently proceeds?
119
+ - Elicit **response** validated against a Zod schema before use? The returned payload is LLM-mediated, not user-direct — "user confirmed" does not mean "user authored these exact fields."
120
+ - Consent is scoped to the specific target (e.g., record ID rendered in the prompt), not a generic "proceed?"
99
121
 
100
- **Smell:** `destructiveHint: true` file with no `ctx.elicit?.(...)` in it.
122
+ **Smell:** `destructiveHint: true` file with no `ctx.elicit?.(...)` in it. Or: `const { confirmed } = await ctx.elicit(...)` without a schema — `confirmed` could be anything.
101
123
 
102
124
  #### Axis 4 — Upstream auth shape
103
125
 
@@ -116,7 +138,7 @@ What credentials the server holds, and the blast radius if one leaks.
116
138
 
117
139
  #### Axis 5 — Input sinks
118
140
 
119
- LLM-supplied inputs feel internal but aren't. Classic sinks apply, amplified.
141
+ LLM-supplied inputs feel internal but aren't. Classic sinks apply, amplified. Sampling responses and roots-derived paths are MCP-specific sinks that look internal but carry LLM/client trust.
120
142
 
121
143
  **Look in:** all handlers.
122
144
 
@@ -132,17 +154,30 @@ grep -rnE "\b(exec|spawn|execSync|spawnSync)\b" src/
132
154
 
133
155
  # Merges — prototype pollution
134
156
  grep -rn "Object.assign\b\|structuredClone" src/
157
+
158
+ # Sampling — LLM-generated content flowing back into server logic
159
+ grep -rn "ctx.sample\|sampling/createMessage" src/
160
+
161
+ # Roots — client-shared filesystem
162
+ grep -rn "roots/list\|ctx.roots" src/
163
+
164
+ # Schema laxity — fields sneaking past validation
165
+ grep -rn "\.passthrough()\|\.catchall(" src/mcp-server/
135
166
  ```
136
167
 
137
168
  **Check:**
138
169
 
139
170
  - URL-taking tools block private IPs, `file://`, `ftp://`, `localhost`, DNS rebind?
140
171
  - Path-taking tools canonicalize (`path.resolve` + assert `startsWith(root + sep)`)?
172
+ - Roots-derived paths: resolved result stays within *one* declared root (iterate and assert), not assumed-safe because "the client said so"?
141
173
  - Shell-using tools use an allowlist (never string-concat)?
142
- - Regex / query / expression inputs bounded?
174
+ - Regex / glob / filter inputs bounded (length cap, complexity limits, execution timeout) — ReDoS-safe?
143
175
  - User-JSON merges reject `__proto__`, `constructor`, `prototype` keys?
176
+ - **Input schemas `.strict()`** — unknown fields rejected, not silently passed to downstream code that destructures with `...rest`?
177
+ - **Output schemas without `.passthrough()` / `.catchall()`** — no accidental exfiltration of fields your schema didn't declare?
178
+ - Sampling responses (`ctx.sample` result) treated as untrusted input — schema-validated before reaching any other sink, never concatenated into prompts, shells, or queries?
144
179
 
145
- **Smell:** `z.string().url()` with no allowlist; `readFile(input.path)` with no canonicalization.
180
+ **Smell:** `z.string().url()` with no allowlist; `readFile(input.path)` with no canonicalization; `await ctx.sample(...)` result interpolated into a shell, SQL, or URL.
146
181
 
147
182
  #### Axis 6 — Tenant isolation
148
183
 
@@ -166,13 +201,15 @@ grep -rn "^let " src/services/
166
201
 
167
202
  #### Axis 7 — Leakage back
168
203
 
169
- What accidentally reaches the LLM, user, or logs.
204
+ What accidentally reaches the LLM, user, or observability sinks.
170
205
 
171
- **Look in:** `throw new McpError(...)` sites, `McpError.data` fields, output schemas, `ctx.log.*` calls.
206
+ **Look in:** `throw new McpError(...)` sites, `McpError.data` fields, output schemas, and every logging / telemetry surface — not just `ctx.log`.
172
207
 
173
208
  ```bash
174
209
  grep -rn "new McpError" src/
175
- grep -rn "ctx.log\." src/
210
+ grep -rnE "\b(ctx\.log|console\.(log|info|warn|error|debug)|logger\.)" src/
211
+ grep -rnE "(Sentry\.|captureException|setTag|setContext|addBreadcrumb)" src/
212
+ grep -rnE "(setAttribute|setAttributes|span\.)" src/ # OpenTelemetry
176
213
  ```
177
214
 
178
215
  **Check:**
@@ -181,18 +218,22 @@ grep -rn "ctx.log\." src/
181
218
  - Output schemas include token prefixes, internal IDs, session identifiers?
182
219
  - `format()` renders fields that shouldn't leave the server?
183
220
  - `ctx.log.info(msg, body)` where `body` is the raw request (may contain secrets)?
221
+ - `console.*` calls near auth / token / request-body handling — bypasses structured redaction?
222
+ - OpenTelemetry span attributes / Sentry breadcrumbs carry tokens, PII, or full request bodies?
223
+ - Secret / token / HMAC comparisons use `===` or `==` instead of constant-time (`timingSafeEqual` / `crypto.timingSafeEqual`) — leaks length and prefix via timing?
184
224
 
185
- **Smell:** `throw new McpError(code, upstream.message, { raw: upstream.body })`.
225
+ **Smell:** `throw new McpError(code, upstream.message, { raw: upstream.body })`. Or: `if (apiKey === expected)` on a request-auth path.
186
226
 
187
227
  #### Axis 8 — Resource bounds
188
228
 
189
229
  Unbounded = DoS of self, upstream, or the LLM's context window (billing-DoS is real).
190
230
 
191
- **Look in:** handlers with loops, pagination, retries.
231
+ **Look in:** handlers with loops, pagination, retries, or inputs that feed `JSON.parse` / schema validation.
192
232
 
193
233
  ```bash
194
234
  grep -rnE "while\s*\(|for\s*\(.*of" src/mcp-server/tools/definitions/
195
235
  grep -rn "cursor\|nextPage\|paginate" src/
236
+ grep -rn "JSON.parse\b" src/
196
237
  ```
197
238
 
198
239
  **Check:**
@@ -201,8 +242,11 @@ grep -rn "cursor\|nextPage\|paginate" src/
201
242
  - Retry logic has max attempts + exponential backoff?
202
243
  - Output size proportional to input — is there a ceiling?
203
244
  - Tools callable in a loop fail-fast on degenerate input (empty string, `0`, `null`)?
245
+ - `JSON.parse` / Zod `.parse()` inputs have a size + nesting-depth limit applied before parse?
246
+ - **Per-tenant per-tool** call rate limit (a single tenant looping `delete_record` 10k/sec hits you before it hits upstream)?
247
+ - Concurrency cap on long-running tools so one tenant can't starve the event loop?
204
248
 
205
- **Smell:** `while (cursor) { results.push(...); cursor = next; }` with no max count.
249
+ **Smell:** `while (cursor) { results.push(...); cursor = next; }` with no max count. Or: `JSON.parse(await req.text())` with no `Content-Length` check upstream.
206
250
 
207
251
  ### 3. Quick sanity pass
208
252
 
@@ -210,11 +254,11 @@ Fast, sometimes high-leverage. Outside the eight axes.
210
254
 
211
255
  - `bun audit` — any direct high/critical?
212
256
  - `package.json` — `postinstall` / lifecycle scripts on added deps?
257
+ - New deps have npm provenance? `npm view <pkg> --json | jq .dist.attestations` — missing attestation on a security-critical dep is a yellow flag
213
258
  - `.env.example` — placeholder values only, never real?
214
259
  - Server-specific `ConfigSchema` — fails loudly on missing required keys (not silent defaults)?
215
260
  - Any `process.env.*` reads outside the config parser (bypasses validation)?
216
-
217
- **Automated assist.** `fuzzTool` from `@cyanheads/mcp-ts-core/testing/fuzz` catches crashes, memory leaks, and prototype pollution automatically — run it on each tool as a cheap first pass.
261
+ - Collect `fuzzTool` results from Step 1 — triage crashes / leaks as Axis 5 / Axis 8 findings.
218
262
 
219
263
  ### 4. Report
220
264
 
@@ -264,14 +308,16 @@ End with:
264
308
  ## Checklist
265
309
 
266
310
  - [ ] Scope confirmed (whole server / module / diff)
267
- - [ ] Map built: tool count, services, upstream APIs, auth mode
268
- - [ ] Axis 1 tool output framing reviewed
311
+ - [ ] Map built: tools / resources / prompts, services, upstream APIs, auth mode, sampling / elicit / roots usage
312
+ - [ ] Deployment surface reviewed (if HTTP): bind address, Origin allowlist, session ID, unauth routes, auth-spec compliance
313
+ - [ ] `fuzzTool` started in parallel
314
+ - [ ] Axis 1 — LLM-facing surfaces (tool / resource / prompt output + descriptions) framed and static
269
315
  - [ ] Axis 2 — scope granularity audited
270
- - [ ] Axis 3 — destructive ops verified to elicit
316
+ - [ ] Axis 3 — destructive ops verified to elicit, elicit response schema-validated
271
317
  - [ ] Axis 4 — upstream auth + token passthrough reviewed
272
- - [ ] Axis 5 — input sinks (URL / path / shell / proto) checked
318
+ - [ ] Axis 5 — input sinks (URL / path / roots / shell / proto / sampling / schema strictness / ReDoS) checked
273
319
  - [ ] Axis 6 — tenant isolation: module-scope state swept
274
- - [ ] Axis 7 — leakage back: errors / outputs / logs reviewed
275
- - [ ] Axis 8 — resource bounds on loops / retries / pagination
276
- - [ ] Quick sanity pass: `bun audit`, lifecycle scripts, `.env.example`, config validation
320
+ - [ ] Axis 7 — leakage back: errors / outputs / `ctx.log` / `console.*` / telemetry / constant-time comparisons
321
+ - [ ] Axis 8 — resource bounds on loops / retries / pagination / parse size+depth / per-tenant rate
322
+ - [ ] Quick sanity pass: `bun audit`, lifecycle scripts, `.env.example`, config validation, new-dep provenance
277
323
  - [ ] Report: summary → grouped findings → numbered options
@@ -112,6 +112,8 @@ See the `add-tool`, `add-app-tool`, `add-resource`, `add-prompt`, and `add-test`
112
112
 
113
113
  Copy all project skills into your agent's skill directory so they're available as context. `skills/` is the source of truth.
114
114
 
115
+ **Don't edit `skills/*/SKILL.md` or `skills/*/references/*`.** These are external skill files synced from `@cyanheads/mcp-ts-core` — the `maintenance` skill overwrites them on package updates, so local edits get lost. Project-specific agent context belongs in `CLAUDE.md` / `AGENTS.md`.
116
+
115
117
  **For Claude Code:**
116
118
 
117
119
  ```bash
@@ -5,6 +5,7 @@
5
5
  "pino-pretty",
6
6
  "typescript",
7
7
  "tsc-alias",
8
+ "depcheck",
8
9
  "@cyanheads/mcp-ts-core"
9
10
  ],
10
11
  "ignorePatterns": []
@@ -57,6 +57,7 @@
57
57
  "devDependencies": {
58
58
  "@biomejs/biome": "^2.4.7",
59
59
  "@types/node": "^25.6.0",
60
+ "depcheck": "^1.4.7",
60
61
  "ignore": "^7.0.5",
61
62
  "tsc-alias": "^1.8.16",
62
63
  "tsx": "^4.19.0",