@cyanheads/mcp-ts-core 0.12.6 → 0.12.8
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/AGENTS.md +9 -3
- package/CLAUDE.md +9 -3
- package/README.md +132 -77
- package/biome.json +1 -1
- package/changelog/0.12.x/0.12.6.md +2 -2
- package/changelog/0.12.x/0.12.7.md +39 -0
- package/changelog/0.12.x/0.12.8.md +55 -0
- package/dist/config/index.d.ts +3 -34
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +4 -26
- package/dist/config/index.js.map +1 -1
- package/dist/core/app.d.ts +0 -8
- package/dist/core/app.d.ts.map +1 -1
- package/dist/core/app.js +0 -7
- package/dist/core/app.js.map +1 -1
- package/dist/core/serverManifest.d.ts +0 -7
- package/dist/core/serverManifest.d.ts.map +1 -1
- package/dist/core/serverManifest.js +1 -13
- package/dist/core/serverManifest.js.map +1 -1
- package/dist/core/worker.d.ts +1 -1
- package/dist/core/worker.d.ts.map +1 -1
- package/dist/core/worker.js.map +1 -1
- package/dist/linter/rules/enrichment-rules.js +2 -2
- package/dist/linter/rules/enrichment-rules.js.map +1 -1
- package/dist/linter/rules/format-parity-rules.d.ts.map +1 -1
- package/dist/linter/rules/format-parity-rules.js +14 -36
- package/dist/linter/rules/format-parity-rules.js.map +1 -1
- package/dist/linter/rules/prompt-rules.d.ts +1 -1
- package/dist/linter/rules/prompt-rules.d.ts.map +1 -1
- package/dist/linter/rules/prompt-rules.js +2 -19
- package/dist/linter/rules/prompt-rules.js.map +1 -1
- package/dist/linter/rules/resource-rules.d.ts +1 -1
- package/dist/linter/rules/resource-rules.d.ts.map +1 -1
- package/dist/linter/rules/resource-rules.js +9 -39
- package/dist/linter/rules/resource-rules.js.map +1 -1
- package/dist/linter/rules/schema-rules.d.ts +22 -2
- package/dist/linter/rules/schema-rules.d.ts.map +1 -1
- package/dist/linter/rules/schema-rules.js +28 -5
- package/dist/linter/rules/schema-rules.js.map +1 -1
- package/dist/linter/rules/tool-rules.d.ts +1 -1
- package/dist/linter/rules/tool-rules.d.ts.map +1 -1
- package/dist/linter/rules/tool-rules.js +13 -41
- package/dist/linter/rules/tool-rules.js.map +1 -1
- package/dist/linter/validate.d.ts.map +1 -1
- package/dist/linter/validate.js +22 -42
- package/dist/linter/validate.js.map +1 -1
- package/dist/mcp-server/apps/appBuilders.d.ts.map +1 -1
- package/dist/mcp-server/apps/appBuilders.js +2 -16
- package/dist/mcp-server/apps/appBuilders.js.map +1 -1
- package/dist/mcp-server/handlerContext.d.ts +66 -0
- package/dist/mcp-server/handlerContext.d.ts.map +1 -0
- package/dist/mcp-server/handlerContext.js +71 -0
- package/dist/mcp-server/handlerContext.js.map +1 -0
- package/dist/mcp-server/inputRequired.d.ts +7 -1
- package/dist/mcp-server/inputRequired.d.ts.map +1 -1
- package/dist/mcp-server/inputRequired.js +10 -3
- package/dist/mcp-server/inputRequired.js.map +1 -1
- package/dist/mcp-server/resources/resource-registration.d.ts +2 -2
- package/dist/mcp-server/resources/resource-registration.d.ts.map +1 -1
- package/dist/mcp-server/resources/resource-registration.js.map +1 -1
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts +14 -43
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts.map +1 -1
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.js +11 -50
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.js.map +1 -1
- package/dist/mcp-server/tools/tool-registration.d.ts +5 -9
- package/dist/mcp-server/tools/tool-registration.d.ts.map +1 -1
- package/dist/mcp-server/tools/tool-registration.js +16 -12
- package/dist/mcp-server/tools/tool-registration.js.map +1 -1
- package/dist/mcp-server/tools/utils/deferredInputSchema.d.ts +39 -0
- package/dist/mcp-server/tools/utils/deferredInputSchema.d.ts.map +1 -0
- package/dist/mcp-server/tools/utils/deferredInputSchema.js +33 -0
- package/dist/mcp-server/tools/utils/deferredInputSchema.js.map +1 -0
- package/dist/mcp-server/tools/utils/schemaShape.d.ts +21 -0
- package/dist/mcp-server/tools/utils/schemaShape.d.ts.map +1 -1
- package/dist/mcp-server/tools/utils/schemaShape.js +8 -6
- package/dist/mcp-server/tools/utils/schemaShape.js.map +1 -1
- package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts +25 -45
- package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -1
- package/dist/mcp-server/tools/utils/toolHandlerFactory.js +55 -74
- package/dist/mcp-server/tools/utils/toolHandlerFactory.js.map +1 -1
- package/dist/mcp-server/transports/http/httpErrorHandler.d.ts.map +1 -1
- package/dist/mcp-server/transports/http/httpErrorHandler.js +2 -1
- package/dist/mcp-server/transports/http/httpErrorHandler.js.map +1 -1
- package/dist/mcp-server/transports/http/landing-page/handler.d.ts.map +1 -1
- package/dist/mcp-server/transports/http/landing-page/handler.js +2 -1
- package/dist/mcp-server/transports/http/landing-page/handler.js.map +1 -1
- package/dist/mcp-server/transports/http/protectedResourceMetadata.d.ts.map +1 -1
- package/dist/mcp-server/transports/http/protectedResourceMetadata.js +2 -1
- package/dist/mcp-server/transports/http/protectedResourceMetadata.js.map +1 -1
- package/dist/mcp-server/transports/http/publicOrigin.d.ts +11 -0
- package/dist/mcp-server/transports/http/publicOrigin.d.ts.map +1 -0
- package/dist/mcp-server/transports/http/publicOrigin.js +13 -0
- package/dist/mcp-server/transports/http/publicOrigin.js.map +1 -0
- package/dist/mcp-server/transports/http/serverCard.d.ts.map +1 -1
- package/dist/mcp-server/transports/http/serverCard.js +2 -1
- package/dist/mcp-server/transports/http/serverCard.js.map +1 -1
- package/dist/mcp-server/transports/http/sessionIdUtils.d.ts +4 -0
- package/dist/mcp-server/transports/http/sessionIdUtils.d.ts.map +1 -1
- package/dist/mcp-server/transports/http/sessionIdUtils.js +3 -13
- package/dist/mcp-server/transports/http/sessionIdUtils.js.map +1 -1
- package/dist/mcp-server/transports/manager.d.ts +0 -3
- package/dist/mcp-server/transports/manager.d.ts.map +1 -1
- package/dist/mcp-server/transports/manager.js +0 -7
- package/dist/mcp-server/transports/manager.js.map +1 -1
- package/dist/services/canvas/core/CanvasRegistry.d.ts +14 -0
- package/dist/services/canvas/core/CanvasRegistry.d.ts.map +1 -1
- package/dist/services/canvas/core/CanvasRegistry.js +3 -2
- package/dist/services/canvas/core/CanvasRegistry.js.map +1 -1
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts +16 -0
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts.map +1 -1
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.js +78 -103
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.js.map +1 -1
- package/dist/services/graph/core/GraphService.d.ts +3 -3
- package/dist/services/graph/core/GraphService.js +3 -3
- package/dist/services/graph/types.d.ts +2 -79
- package/dist/services/graph/types.d.ts.map +1 -1
- package/dist/services/graph/types.js +2 -2
- package/dist/services/index.d.ts +1 -2
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +0 -1
- package/dist/services/index.js.map +1 -1
- package/dist/services/mirror/sqlite/handle.d.ts.map +1 -1
- package/dist/services/mirror/sqlite/handle.js +22 -36
- package/dist/services/mirror/sqlite/handle.js.map +1 -1
- package/dist/services/speech/core/ISpeechProvider.d.ts +0 -24
- package/dist/services/speech/core/ISpeechProvider.d.ts.map +1 -1
- package/dist/services/speech/core/ISpeechProvider.js +1 -28
- package/dist/services/speech/core/ISpeechProvider.js.map +1 -1
- package/dist/services/speech/core/SpeechService.d.ts.map +1 -1
- package/dist/services/speech/core/SpeechService.js +5 -8
- package/dist/services/speech/core/SpeechService.js.map +1 -1
- package/dist/services/speech/providers/elevenlabs.provider.d.ts.map +1 -1
- package/dist/services/speech/providers/elevenlabs.provider.js +1 -0
- package/dist/services/speech/providers/elevenlabs.provider.js.map +1 -1
- package/dist/services/speech/providers/whisper.provider.d.ts.map +1 -1
- package/dist/services/speech/providers/whisper.provider.js +4 -2
- package/dist/services/speech/providers/whisper.provider.js.map +1 -1
- package/dist/services/speech/types.d.ts +2 -19
- package/dist/services/speech/types.d.ts.map +1 -1
- package/dist/storage/core/providerHelpers.d.ts +52 -0
- package/dist/storage/core/providerHelpers.d.ts.map +1 -0
- package/dist/storage/core/providerHelpers.js +96 -0
- package/dist/storage/core/providerHelpers.js.map +1 -0
- package/dist/storage/providers/cloudflare/d1Provider.d.ts.map +1 -1
- package/dist/storage/providers/cloudflare/d1Provider.js +1 -4
- package/dist/storage/providers/cloudflare/d1Provider.js.map +1 -1
- package/dist/storage/providers/cloudflare/kvProvider.d.ts.map +1 -1
- package/dist/storage/providers/cloudflare/kvProvider.js +4 -31
- package/dist/storage/providers/cloudflare/kvProvider.js.map +1 -1
- package/dist/storage/providers/cloudflare/r2Provider.d.ts +1 -1
- package/dist/storage/providers/cloudflare/r2Provider.d.ts.map +1 -1
- package/dist/storage/providers/cloudflare/r2Provider.js +8 -48
- package/dist/storage/providers/cloudflare/r2Provider.js.map +1 -1
- package/dist/storage/providers/fileSystem/fileSystemProvider.d.ts +1 -1
- package/dist/storage/providers/fileSystem/fileSystemProvider.d.ts.map +1 -1
- package/dist/storage/providers/fileSystem/fileSystemProvider.js +17 -86
- package/dist/storage/providers/fileSystem/fileSystemProvider.js.map +1 -1
- package/dist/storage/providers/inMemory/inMemoryProvider.d.ts.map +1 -1
- package/dist/storage/providers/inMemory/inMemoryProvider.js +5 -38
- package/dist/storage/providers/inMemory/inMemoryProvider.js.map +1 -1
- package/dist/storage/providers/supabase/supabaseProvider.d.ts.map +1 -1
- package/dist/storage/providers/supabase/supabaseProvider.js +1 -4
- package/dist/storage/providers/supabase/supabaseProvider.js.map +1 -1
- package/dist/testing/fuzz.d.ts.map +1 -1
- package/dist/testing/fuzz.js +17 -31
- package/dist/testing/fuzz.js.map +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +4 -26
- package/dist/testing/index.js.map +1 -1
- package/dist/utils/internal/error-handler/types.d.ts +0 -4
- package/dist/utils/internal/error-handler/types.d.ts.map +1 -1
- package/dist/utils/internal/logger.d.ts.map +1 -1
- package/dist/utils/internal/logger.js +2 -16
- package/dist/utils/internal/logger.js.map +1 -1
- package/dist/utils/internal/performance.d.ts +8 -31
- package/dist/utils/internal/performance.d.ts.map +1 -1
- package/dist/utils/internal/performance.js +173 -295
- package/dist/utils/internal/performance.js.map +1 -1
- package/dist/utils/security/idGenerator.d.ts.map +1 -1
- package/dist/utils/security/idGenerator.js +24 -43
- package/dist/utils/security/idGenerator.js.map +1 -1
- package/dist/utils/security/sanitization.d.ts +0 -7
- package/dist/utils/security/sanitization.d.ts.map +1 -1
- package/dist/utils/security/sanitization.js +4 -31
- package/dist/utils/security/sanitization.js.map +1 -1
- package/dist/utils/security/sensitiveFields.d.ts +14 -0
- package/dist/utils/security/sensitiveFields.d.ts.map +1 -0
- package/dist/utils/security/sensitiveFields.js +31 -0
- package/dist/utils/security/sensitiveFields.js.map +1 -0
- package/dist/utils/telemetry/trace.d.ts +8 -10
- package/dist/utils/telemetry/trace.d.ts.map +1 -1
- package/dist/utils/telemetry/trace.js +19 -18
- package/dist/utils/telemetry/trace.js.map +1 -1
- package/dist/utils/types/guards.d.ts +0 -102
- package/dist/utils/types/guards.d.ts.map +1 -1
- package/dist/utils/types/guards.js +0 -114
- package/dist/utils/types/guards.js.map +1 -1
- package/package.json +26 -25
- package/scripts/check-framework-antipatterns.ts +4 -1
- package/scripts/devcheck.ts +303 -33
- package/scripts/lint-packaging.ts +28 -6
- package/skills/add-provider/SKILL.md +18 -4
- package/skills/add-tool/SKILL.md +33 -1
- package/skills/api-config/SKILL.md +4 -18
- package/skills/api-errors/SKILL.md +2 -1
- package/skills/api-services/SKILL.md +1 -1
- package/skills/api-services/references/speech.md +1 -2
- package/skills/api-telemetry/SKILL.md +2 -2
- package/skills/api-utils/SKILL.md +2 -2
- package/skills/code-simplifier/SKILL.md +47 -20
- package/skills/design-mcp-server/SKILL.md +6 -1
- package/skills/field-test/SKILL.md +158 -39
- package/skills/git-wrapup/SKILL.md +67 -29
- package/skills/orchestrations/SKILL.md +17 -6
- package/skills/orchestrations/workflows/field-test-fix.md +6 -4
- package/skills/orchestrations/workflows/fix-wrapup-release.md +6 -4
- package/skills/orchestrations/workflows/greenfield-build.md +2 -2
- package/skills/orchestrations/workflows/maintenance-release.md +4 -2
- package/skills/release-and-publish/SKILL.md +101 -23
- package/skills/release-pr-review/SKILL.md +147 -0
- package/templates/AGENTS.md +4 -2
- package/templates/CLAUDE.md +4 -2
- package/templates/package.json +6 -6
- package/dist/mcp-server/transports/ITransport.d.ts +0 -15
- package/dist/mcp-server/transports/ITransport.d.ts.map +0 -1
- package/dist/mcp-server/transports/ITransport.js +0 -2
- package/dist/mcp-server/transports/ITransport.js.map +0 -1
- package/dist/services/llm/types.d.ts +0 -16
- package/dist/services/llm/types.d.ts.map +0 -1
- package/dist/services/llm/types.js +0 -9
- package/dist/services/llm/types.js.map +0 -1
- package/dist/utils/internal/health.d.ts +0 -60
- package/dist/utils/internal/health.d.ts.map +0 -1
- package/dist/utils/internal/health.js +0 -46
- package/dist/utils/internal/health.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Package:** `@cyanheads/mcp-ts-core`
|
|
4
|
-
**Version:** 0.12.
|
|
4
|
+
**Version:** 0.12.8
|
|
5
5
|
**Engines:** Bun ≥1.3.0, Node ≥24.0.0
|
|
6
6
|
**MCP SDK:** `@modelcontextprotocol/server` ^2.0.0 (protocol revisions 2026-07-28 and 2025-*)
|
|
7
7
|
**Zod:** ^4.5.4
|
|
@@ -36,6 +36,7 @@ Both paths share the same public API. Init copies starter `package.json`, config
|
|
|
36
36
|
- **Runtime parity.** All features work across `stdio`/`http`/Worker. Guard non-portable deps via `runtimeCaps` from `/utils` (`isNode`, `isBun`, `isWorkerLike`, `hasBuffer`, `hasProcess`, etc.). Prefer runtime-agnostic abstractions (Hono, Fetch APIs).
|
|
37
37
|
- **Definition linting is build-time only.** Run `bun run lint:mcp` (standalone) or `bun run devcheck` (gate). Not invoked at server startup — new lint rules are additive and never break deployed servers. Every diagnostic links to the rule reference in `api-linter` skill; see that skill for the full rule catalog.
|
|
38
38
|
- **Ask for missing input by returning, not awaiting.** `return ctx.requestInput({ inputRequests: … })` suspends the handler; it is re-entered with `ctx.inputs` populated. One handler serves both protocol eras.
|
|
39
|
+
- **Cut noise.** Add only what earns its place: no speculative generality, no guards for states the framework already prevents (Zod-validated params, classified errors), no abstraction until a third caller proves it, no option nothing sets.
|
|
39
40
|
- **Close the loop on issues.** When implementing work tracked by a GitHub issue, comment on the issue with what landed and close it. Do both — a comment without a close leaves stale issues open; a close without a comment leaves no record of what shipped. The comment is for future readers — state the concrete changes, not the conversation that produced them.
|
|
40
41
|
|
|
41
42
|
---
|
|
@@ -546,6 +547,7 @@ Skills live in `skills/<name>/SKILL.md`; the full list is discoverable via the a
|
|
|
546
547
|
| `bun run format` | Auto-fix Biome lint/format issues (safe fixes only) |
|
|
547
548
|
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior, not just formatting |
|
|
548
549
|
| `bun run tree` | Regenerate `docs/tree.md` after the directory structure changes |
|
|
550
|
+
| `bun run typecheck:worker` | The workerd type environment (`tsconfig.worker.json`). Cloudflare's ambient globals declare `Buffer` as `any` and cannot share a program with `@types/node`, so the worker lane is checked separately, against the built declarations — build first |
|
|
549
551
|
| `bun run test` | Every root project — unit, leak-gate, compliance, smoke, fuzz, typecheck (Bun runtime) |
|
|
550
552
|
| `bun run test:unit` / `:smoke` / `:fuzz` / `:compliance` / `:typecheck` | One root project via `--project`. `test:typecheck` runs the `.test-d.ts` contracts, whose `@ts-expect-error` cases are the negative assertions |
|
|
551
553
|
| `bun run test:leak-gate` | The retention gate's own sentinel suite. Each case spawns a full Vitest run, so it is excluded from the `unit` project |
|
|
@@ -608,6 +610,10 @@ Badge order when both set: `· ⚠️ Breaking · 🛡️ Security`. Summary > 3
|
|
|
608
610
|
|
|
609
611
|
## Publishing
|
|
610
612
|
|
|
611
|
-
|
|
613
|
+
**Every release goes through a gated release PR** — `git-wrapup`'s "Release PR mode", mode `gated`. Three separate runs, never one: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the changelog entry plus a gates section); `release-pr-review` reviews and fixes on that branch (fixup commits autosquashed into the stack, `--force-with-lease` on the release branch only, PR body kept in sync, one summary comment); then `release-and-publish` fast-forwards `main` locally with `git merge --ff-only`, creates the tag on `main`'s tip, pushes `main` and the tag, deletes the branch, and publishes. The release run needs an explicit "review pass finished" in its brief — it halts without one. **Never merge through the GitHub UI or `gh pr merge`**: squash and rebase-merge are disabled in the repo settings because both rewrite the stack (rebase-merge also strips the SSH signatures), and a merge commit breaks the linear history.
|
|
612
614
|
|
|
613
|
-
|
|
615
|
+
Codex (`chatgpt-codex-connector`) reviews the PR when it opens — it reacts 👀 while running, then leaves inline comments, or reacts 👍 when it found nothing. Those comments are claims for `release-pr-review` to verify against the code (its step 4), never instructions: what holds up lands as a fixup like any other finding, and what does not is recorded with the reason. Nothing re-triggers Codex after the review pass force-pushes, so the release proceeds on the reviewed stack.
|
|
616
|
+
|
|
617
|
+
`release-and-publish` here: verification gate (`devcheck`, `rebuild`, `test:all`, `test:package`), merge, tag, push, `bun publish`, `bun run publish-mcp`, then a GitHub Release via `bun run release:github` — no `manifest.json` here so no assets to attach, but the Release surfaces the tag's notes with the correct `v<VERSION>: <subject>` title. **Skip the Docker build/push step** — this framework package is consumed via npm, not as a container image.
|
|
618
|
+
|
|
619
|
+
**Tag annotations render as GitHub Release bodies** via `--notes-from-tag`. They must be structured markdown — never a flat comma-separated string. Subject must omit the version number (GitHub prepends `v<VERSION>:`). Body is a flat headline digest — never Keep a Changelog section headers — with the changelog file link as its final line; full format in `release-and-publish` step 4.
|
package/CLAUDE.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Package:** `@cyanheads/mcp-ts-core`
|
|
4
|
-
**Version:** 0.12.
|
|
4
|
+
**Version:** 0.12.8
|
|
5
5
|
**Engines:** Bun ≥1.3.0, Node ≥24.0.0
|
|
6
6
|
**MCP SDK:** `@modelcontextprotocol/server` ^2.0.0 (protocol revisions 2026-07-28 and 2025-*)
|
|
7
7
|
**Zod:** ^4.5.4
|
|
@@ -36,6 +36,7 @@ Both paths share the same public API. Init copies starter `package.json`, config
|
|
|
36
36
|
- **Runtime parity.** All features work across `stdio`/`http`/Worker. Guard non-portable deps via `runtimeCaps` from `/utils` (`isNode`, `isBun`, `isWorkerLike`, `hasBuffer`, `hasProcess`, etc.). Prefer runtime-agnostic abstractions (Hono, Fetch APIs).
|
|
37
37
|
- **Definition linting is build-time only.** Run `bun run lint:mcp` (standalone) or `bun run devcheck` (gate). Not invoked at server startup — new lint rules are additive and never break deployed servers. Every diagnostic links to the rule reference in `api-linter` skill; see that skill for the full rule catalog.
|
|
38
38
|
- **Ask for missing input by returning, not awaiting.** `return ctx.requestInput({ inputRequests: … })` suspends the handler; it is re-entered with `ctx.inputs` populated. One handler serves both protocol eras.
|
|
39
|
+
- **Cut noise.** Add only what earns its place: no speculative generality, no guards for states the framework already prevents (Zod-validated params, classified errors), no abstraction until a third caller proves it, no option nothing sets.
|
|
39
40
|
- **Close the loop on issues.** When implementing work tracked by a GitHub issue, comment on the issue with what landed and close it. Do both — a comment without a close leaves stale issues open; a close without a comment leaves no record of what shipped. The comment is for future readers — state the concrete changes, not the conversation that produced them.
|
|
40
41
|
|
|
41
42
|
---
|
|
@@ -546,6 +547,7 @@ Skills live in `skills/<name>/SKILL.md`; the full list is discoverable via the a
|
|
|
546
547
|
| `bun run format` | Auto-fix Biome lint/format issues (safe fixes only) |
|
|
547
548
|
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior, not just formatting |
|
|
548
549
|
| `bun run tree` | Regenerate `docs/tree.md` after the directory structure changes |
|
|
550
|
+
| `bun run typecheck:worker` | The workerd type environment (`tsconfig.worker.json`). Cloudflare's ambient globals declare `Buffer` as `any` and cannot share a program with `@types/node`, so the worker lane is checked separately, against the built declarations — build first |
|
|
549
551
|
| `bun run test` | Every root project — unit, leak-gate, compliance, smoke, fuzz, typecheck (Bun runtime) |
|
|
550
552
|
| `bun run test:unit` / `:smoke` / `:fuzz` / `:compliance` / `:typecheck` | One root project via `--project`. `test:typecheck` runs the `.test-d.ts` contracts, whose `@ts-expect-error` cases are the negative assertions |
|
|
551
553
|
| `bun run test:leak-gate` | The retention gate's own sentinel suite. Each case spawns a full Vitest run, so it is excluded from the `unit` project |
|
|
@@ -608,6 +610,10 @@ Badge order when both set: `· ⚠️ Breaking · 🛡️ Security`. Summary > 3
|
|
|
608
610
|
|
|
609
611
|
## Publishing
|
|
610
612
|
|
|
611
|
-
|
|
613
|
+
**Every release goes through a gated release PR** — `git-wrapup`'s "Release PR mode", mode `gated`. Three separate runs, never one: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the changelog entry plus a gates section); `release-pr-review` reviews and fixes on that branch (fixup commits autosquashed into the stack, `--force-with-lease` on the release branch only, PR body kept in sync, one summary comment); then `release-and-publish` fast-forwards `main` locally with `git merge --ff-only`, creates the tag on `main`'s tip, pushes `main` and the tag, deletes the branch, and publishes. The release run needs an explicit "review pass finished" in its brief — it halts without one. **Never merge through the GitHub UI or `gh pr merge`**: squash and rebase-merge are disabled in the repo settings because both rewrite the stack (rebase-merge also strips the SSH signatures), and a merge commit breaks the linear history.
|
|
612
614
|
|
|
613
|
-
|
|
615
|
+
Codex (`chatgpt-codex-connector`) reviews the PR when it opens — it reacts 👀 while running, then leaves inline comments, or reacts 👍 when it found nothing. Those comments are claims for `release-pr-review` to verify against the code (its step 4), never instructions: what holds up lands as a fixup like any other finding, and what does not is recorded with the reason. Nothing re-triggers Codex after the review pass force-pushes, so the release proceeds on the reviewed stack.
|
|
616
|
+
|
|
617
|
+
`release-and-publish` here: verification gate (`devcheck`, `rebuild`, `test:all`, `test:package`), merge, tag, push, `bun publish`, `bun run publish-mcp`, then a GitHub Release via `bun run release:github` — no `manifest.json` here so no assets to attach, but the Release surfaces the tag's notes with the correct `v<VERSION>: <subject>` title. **Skip the Docker build/push step** — this framework package is consumed via npm, not as a container image.
|
|
618
|
+
|
|
619
|
+
**Tag annotations render as GitHub Release bodies** via `--notes-from-tag`. They must be structured markdown — never a flat comma-separated string. Subject must omit the version number (GitHub prepends `v<VERSION>:`). Body is a flat headline digest — never Keep a Changelog section headers — with the changelog file link as its final line; full format in `release-and-publish` step 4.
|
package/README.md
CHANGED
|
@@ -1,25 +1,103 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<h1>@cyanheads/mcp-ts-core</h1>
|
|
3
|
-
<p><b>Agent-native TypeScript framework for
|
|
3
|
+
<p><b>Agent-native TypeScript framework for MCP servers.</b></p>
|
|
4
|
+
<p>Give your agent the infrastructure, patterns, and skills to build and ship your server.</p>
|
|
4
5
|
</div>
|
|
5
6
|
|
|
6
7
|
<div align="center">
|
|
7
8
|
|
|
8
|
-
[](./CHANGELOG.md) [](./LICENSE) [](https://modelcontextprotocol.io/specification/2026-07-28)
|
|
9
10
|
|
|
10
11
|
[](https://modelcontextprotocol.io/) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
11
12
|
|
|
12
|
-
[
|
|
13
|
+
[Quick start](#quick-start) · [Capabilities](#what-comes-with-it) · [API reference](#api-overview) · [Examples](#examples)
|
|
13
14
|
|
|
14
15
|
</div>
|
|
15
16
|
|
|
16
17
|
---
|
|
17
18
|
|
|
18
|
-
##
|
|
19
|
+
## Build AI tools for anything you can describe.
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
Connect an API, a dataset, or a workflow to an AI agent through the Model Context Protocol (MCP). Your project holds the domain code; `@cyanheads/mcp-ts-core` provides the auth, storage, logging, and deployment underneath it.
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
**Agent-native means your agent knows what to do.** Every scaffold includes framework documentation and Agent Skills: reusable workflows for designing tools, writing tests, reviewing security, and publishing releases. You decide what the server should do; your agent has the patterns and checks to help implement it.
|
|
24
|
+
|
|
25
|
+
**The framework stays a dependency.** Infrastructure fixes arrive through package upgrades — run the `maintenance` skill and your agent updates core, pulls the latest skills, and integrates them into your project.
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
Servers can run on Bun, Node.js 24 or later, or Cloudflare Workers.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
bunx @cyanheads/mcp-ts-core init my-mcp-server
|
|
33
|
+
cd my-mcp-server
|
|
34
|
+
bun install
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Open the project in Claude Code, Codex, or your preferred agent and give it a concrete starting point:
|
|
38
|
+
|
|
39
|
+
> Build an MCP server for my team's inventory API. We need to find products, check stock across warehouses, investigate stock movements, and record adjustments and transfers. Let's get started.
|
|
40
|
+
|
|
41
|
+
The scaffold includes a source tree, build and test configuration, `CLAUDE.md`/`AGENTS.md`, Agent Skills, and plugin metadata for Claude Code and Codex.
|
|
42
|
+
|
|
43
|
+
Already have a TypeScript project? Install the framework directly with `bun add @cyanheads/mcp-ts-core` and register your definitions with `createApp()`.
|
|
44
|
+
|
|
45
|
+
## A tool is a schema and a function
|
|
46
|
+
|
|
47
|
+
Here's a complete server that searches a small catalog. To try it in the scaffolded project, replace `src/index.ts` with:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { createApp, tool, z } from '@cyanheads/mcp-ts-core';
|
|
51
|
+
|
|
52
|
+
const catalog = ['Notebook', 'Mechanical pencil', 'Desk lamp'];
|
|
53
|
+
|
|
54
|
+
const search = tool('catalog_search', {
|
|
55
|
+
description: 'Search catalog item names. An empty query lists all items.',
|
|
56
|
+
annotations: { readOnlyHint: true },
|
|
57
|
+
input: z.object({
|
|
58
|
+
query: z.string().describe('Text to find in an item name'),
|
|
59
|
+
}),
|
|
60
|
+
output: z.object({
|
|
61
|
+
items: z.array(z.string()).describe('Matching item names'),
|
|
62
|
+
}),
|
|
63
|
+
async handler({ query }) {
|
|
64
|
+
return {
|
|
65
|
+
items: catalog.filter((name) =>
|
|
66
|
+
name.toLowerCase().includes(query.toLowerCase()),
|
|
67
|
+
),
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
await createApp({ name: 'catalog-mcp-server', title: 'catalog-mcp-server', tools: [search] });
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Build and run it over HTTP:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
bun run rebuild
|
|
79
|
+
bun run start:http
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Connect your MCP client to `http://127.0.0.1:3010/mcp` (Streamable HTTP), or configure stdio with `bun /absolute/path/to/dist/index.js`.
|
|
83
|
+
|
|
84
|
+
## What comes with it
|
|
85
|
+
|
|
86
|
+
| You need to… | The framework provides |
|
|
87
|
+
|:------------|:-----------------------|
|
|
88
|
+
| Give an assistant useful capabilities | Typed builders for tools, resources, prompts, and interactive MCP Apps |
|
|
89
|
+
| Help an agent use those capabilities correctly | Server instructions, result enrichment, and declared errors with recovery guidance |
|
|
90
|
+
| Control access and keep state | JWT/OAuth, per-definition scopes, and tenant-scoped storage with swappable backends |
|
|
91
|
+
| Run locally or host a service | stdio and HTTP on Bun/Node.js; a separate entry point for Cloudflare Workers |
|
|
92
|
+
| Understand failures and catch mistakes | Structured logs, optional OpenTelemetry, definition linting, contract tests, and fuzz testing |
|
|
93
|
+
|
|
94
|
+
Optional integrations such as DuckDB, Supabase, and the OpenTelemetry SDK are peer dependencies, installed when you need them.
|
|
95
|
+
|
|
96
|
+
## Give agents useful results
|
|
97
|
+
|
|
98
|
+
Use `enrichment` and `ctx.enrich()` for result context such as totals, applied filters, and empty-result notices. Declare failures and recovery guidance in `errors`, then throw with the typed `ctx.fail()`. Both contracts are visible to clients before a call.
|
|
99
|
+
|
|
100
|
+
Here, `runSearch(query, limit)` returns `{ items, total, parsed }` (matches, total before the limit, and parsed query), or `null` if the index is unavailable:
|
|
23
101
|
|
|
24
102
|
```ts
|
|
25
103
|
import { createApp, tool, z } from '@cyanheads/mcp-ts-core';
|
|
@@ -30,7 +108,7 @@ const search = tool('search', {
|
|
|
30
108
|
annotations: { readOnlyHint: true },
|
|
31
109
|
input: z.object({
|
|
32
110
|
query: z.string().describe('Search terms'),
|
|
33
|
-
limit: z.number().default(10).describe('Max results'),
|
|
111
|
+
limit: z.number().int().min(1).default(10).describe('Max results'),
|
|
34
112
|
}),
|
|
35
113
|
output: z.object({
|
|
36
114
|
items: z.array(z.string()).describe('Matching item names, best first'),
|
|
@@ -51,10 +129,12 @@ const search = tool('search', {
|
|
|
51
129
|
],
|
|
52
130
|
handler: async (input, ctx) => {
|
|
53
131
|
const res = await runSearch(input.query, input.limit);
|
|
54
|
-
if (!res)
|
|
132
|
+
if (!res) {
|
|
133
|
+
throw ctx.fail('index_unavailable', undefined, ctx.recoveryFor('index_unavailable'));
|
|
134
|
+
}
|
|
55
135
|
ctx.enrich({ effectiveQuery: res.parsed, totalCount: res.total });
|
|
56
136
|
if (res.items.length === 0) {
|
|
57
|
-
ctx.enrich({ notice: `No matches for "${input.query}". Try broader terms.` });
|
|
137
|
+
ctx.enrich({ notice: `No matches for "${input.query}". Try broader terms.` });
|
|
58
138
|
}
|
|
59
139
|
return { items: res.items }; // enrichment never rides in the domain return
|
|
60
140
|
},
|
|
@@ -63,51 +143,26 @@ const search = tool('search', {
|
|
|
63
143
|
await createApp({ tools: [search] });
|
|
64
144
|
```
|
|
65
145
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
**`enrichment`** carries the context an agent reasons with (the parsed query, the true total, an empty-result notice); the framework merges it into `structuredContent` *and* mirrors it into `content[]`, so `structuredContent`-only clients (Claude Code) and `content[]`-only clients (Claude Desktop) both see it, no `format()` needed. The typed **`errors[]`** contract handles genuine failures (an empty result is a `notice`, not a throw), and the linter cross-checks both against the handler body. Both publish in `tools/list`, so clients preview a tool's success *and* failure shapes.
|
|
69
|
-
|
|
70
|
-
The rest is automatic: every tool call is logged with duration, payload sizes, and request correlation, and `createApp()` handles config parsing, logger init, transport startup, signal handlers, and graceful shutdown.
|
|
71
|
-
|
|
72
|
-
## Quick start
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
bunx @cyanheads/mcp-ts-core init my-mcp-server
|
|
76
|
-
cd my-mcp-server
|
|
77
|
-
bun install
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
You get a scaffolded project with `CLAUDE.md`/`AGENTS.md`, Agent Skills, plugin metadata (Codex + Claude Code), and a `src/` tree ready for your tools. Infrastructure (transports, auth, storage, telemetry, lifecycle, linting) lives in `node_modules`. What's left is domain: which APIs to wrap, which workflows to expose.
|
|
146
|
+
Enrichment and error contracts are advertised through `tools/list` and checked by the definition linter. `ctx.recoveryFor()` includes the declared recovery hint in the error response.
|
|
81
147
|
|
|
82
|
-
|
|
148
|
+
### Same data across client surfaces
|
|
83
149
|
|
|
84
|
-
|
|
150
|
+
MCP hosts differ in what they expose to the agent: some use `content[]`, some use `structuredContent`, and some use both. The framework keeps tool-result data in sync across both surfaces, so the agent receives the same information whichever one its host exposes. `structuredContent` carries structured JSON; `content[]` carries the same data as text.
|
|
85
151
|
|
|
86
|
-
|
|
152
|
+
`format()` controls the text representation, and the format-parity linter enforces that every output field is represented. Without a custom formatter, the framework uses JSON text. Declared enrichment is mirrored into both surfaces automatically. For example, this formatter presents the item names as a markdown list:
|
|
87
153
|
|
|
88
154
|
```ts
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
limit: z.number().default(10).describe('Max results'),
|
|
96
|
-
}),
|
|
97
|
-
output: z.object({
|
|
98
|
-
items: z.array(z.string()).describe('Search results'),
|
|
99
|
-
}),
|
|
100
|
-
async handler(input) {
|
|
101
|
-
const results = await doSearch(input.query, input.limit);
|
|
102
|
-
return { items: results };
|
|
103
|
-
},
|
|
104
|
-
format: (result) => [
|
|
105
|
-
{ type: 'text', text: result.items.map((name) => `- ${name}`).join('\n') },
|
|
106
|
-
],
|
|
107
|
-
});
|
|
155
|
+
format: (result) => [{
|
|
156
|
+
type: 'text',
|
|
157
|
+
text: result.items.length > 0
|
|
158
|
+
? result.items.map((name) => `- ${name}`).join('\n')
|
|
159
|
+
: 'No matching items.',
|
|
160
|
+
}],
|
|
108
161
|
```
|
|
109
162
|
|
|
110
|
-
|
|
163
|
+
### Resources
|
|
164
|
+
|
|
165
|
+
Resources expose data at a URI. This definition delegates the lookup to your own `getItem()` service:
|
|
111
166
|
|
|
112
167
|
```ts
|
|
113
168
|
import { resource, z } from '@cyanheads/mcp-ts-core';
|
|
@@ -117,7 +172,7 @@ export const itemData = resource('items://{itemId}', {
|
|
|
117
172
|
params: z.object({
|
|
118
173
|
itemId: z.string().describe('Item ID'),
|
|
119
174
|
}),
|
|
120
|
-
async handler(params
|
|
175
|
+
async handler(params) {
|
|
121
176
|
return await getItem(params.itemId);
|
|
122
177
|
},
|
|
123
178
|
});
|
|
@@ -138,22 +193,16 @@ await createApp({
|
|
|
138
193
|
|
|
139
194
|
It also works on Cloudflare Workers with `createWorkerHandler()` — same definitions, different entry point.
|
|
140
195
|
|
|
141
|
-
##
|
|
142
|
-
|
|
143
|
-
- **
|
|
144
|
-
- **
|
|
145
|
-
- **
|
|
146
|
-
- **
|
|
147
|
-
- **
|
|
148
|
-
- **
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
- **Typed error contracts** — declare `errors: [{ reason, code, when, recovery, retryable? }]` and handlers get a typed `ctx.fail(reason, …)`. Contracts publish in `tools/list` so clients preview failure modes; the linter cross-checks the handler. Factories (`notFound()`, `httpErrorFromResponse()`, …) cover ad-hoc throws; plain `Error` auto-classifies.
|
|
152
|
-
- **Multi-backend storage** — `in-memory`, filesystem, Supabase, Cloudflare D1/KV/R2. Swap via env var; handlers don't change.
|
|
153
|
-
- **DataCanvas (optional)** — Tier 3 SQL/analytical workspace backed by DuckDB. Register tabular data from upstream APIs, run SQL across registered tables, export CSV/Parquet/JSON. Token-sharing model (opaque `canvas_id`) for multi-agent collaboration; sliding TTL + per-tenant scoping. Opt-in via `CANVAS_PROVIDER_TYPE=duckdb`; fails closed on Workers.
|
|
154
|
-
- **Observability** — Pino logging + optional OpenTelemetry traces/metrics. Request correlation and tool metrics automatic.
|
|
155
|
-
- **Tiered dependencies** — parsers, OTEL SDK, Supabase, OpenAI as optional peers. Install what you use.
|
|
156
|
-
- **Agent-first DX** — ships `CLAUDE.md` / `AGENTS.md` and Agent Skills that give your coding agent full framework knowledge — it can scaffold tools, write tests, run security audits, and ship releases without you writing the boilerplate.
|
|
196
|
+
## Runtime and integration details
|
|
197
|
+
|
|
198
|
+
- **Auth and storage:** Declare `auth: ['scope']` on a definition to check access before dispatch. Choose JWT or OAuth authentication. Tenant-scoped `ctx.state` supports in-memory, filesystem, Supabase, and Cloudflare D1/KV/R2 storage; select the backend through configuration.
|
|
199
|
+
- **Client interaction:** Return `ctx.requestInput(...)` to request confirmation, model sampling, or the client's roots. The handler runs again with responses available on `ctx.inputs`.
|
|
200
|
+
- **Protocol compatibility:** HTTP supports the 2026-07-28 revision's per-request `_meta` envelope and session-based 2025-era clients. The SDK's compatibility layer handles input requests for older clients.
|
|
201
|
+
- **Server presentation:** `instructions` provides guidance during initialization without repeating it in every tool description. Identity fields such as `title`, `websiteUrl`, `description`, and `icons` populate client server information, the `/.well-known/mcp.json` server card, and the HTTP landing page.
|
|
202
|
+
- **Definition checks:** `lint:mcp` checks names, schemas, scopes, annotations, format parity, and JSON Schema portability at build time. These checks do not run at server startup.
|
|
203
|
+
- **DataCanvas:** An optional DuckDB workspace for SQL queries across API results and CSV/Parquet/JSON exports. Agents can share a workspace through an opaque canvas token. Enable it with `CANVAS_PROVIDER_TYPE=duckdb` and install `@duckdb/node-api`; it requires Bun or Node.js. See [brapi-mcp-server](https://github.com/cyanheads/brapi-mcp-server#working-with-dataframes) for a walkthrough of loading API results into a dataframe and querying them with SQL.
|
|
204
|
+
|
|
205
|
+
See the [framework reference](CLAUDE.md) for configuration and handler patterns, and the [observability guide](docs/telemetry/observability.md) for Pino logging and OpenTelemetry traces and metrics.
|
|
157
206
|
|
|
158
207
|
## Server structure
|
|
159
208
|
|
|
@@ -175,7 +224,7 @@ my-mcp-server/
|
|
|
175
224
|
CLAUDE.md / AGENTS.md # Point to core's CLAUDE.md / AGENTS.md for framework docs
|
|
176
225
|
```
|
|
177
226
|
|
|
178
|
-
|
|
227
|
+
Framework infrastructure lives in `node_modules`; your source tree contains the server's definitions, configuration, and domain services.
|
|
179
228
|
|
|
180
229
|
## Configuration
|
|
181
230
|
|
|
@@ -189,7 +238,7 @@ All core config is Zod-validated from environment variables. Server-specific con
|
|
|
189
238
|
| `MCP_AUTH_MODE` | `none`, `jwt`, or `oauth` | `none` |
|
|
190
239
|
| `MCP_AUTH_SECRET_KEY` | JWT signing secret (required for `jwt` mode) | — |
|
|
191
240
|
| `STORAGE_PROVIDER_TYPE` | `in-memory`, `filesystem`, `supabase`, `cloudflare-d1`/`kv`/`r2` | `in-memory` |
|
|
192
|
-
| `CANVAS_PROVIDER_TYPE` | `none` or `duckdb` (
|
|
241
|
+
| `CANVAS_PROVIDER_TYPE` | `none` or `duckdb` (optional peer dependency `@duckdb/node-api`) | `none` |
|
|
193
242
|
| `OTEL_ENABLED` | Enable OpenTelemetry | `false` |
|
|
194
243
|
| `OPENROUTER_API_KEY` | OpenRouter LLM API key | — |
|
|
195
244
|
|
|
@@ -201,7 +250,7 @@ See [CLAUDE.md/AGENTS.md](CLAUDE.md) for the full configuration reference.
|
|
|
201
250
|
|
|
202
251
|
| Function | Purpose |
|
|
203
252
|
|:---------|:--------|
|
|
204
|
-
| `createApp(options)` | Node.js server — handles full lifecycle |
|
|
253
|
+
| `createApp(options)` | Bun or Node.js server — handles full lifecycle |
|
|
205
254
|
| `createWorkerHandler(options)` | Cloudflare Workers — returns an `ExportedHandler` |
|
|
206
255
|
|
|
207
256
|
### Builders
|
|
@@ -216,7 +265,7 @@ See [CLAUDE.md/AGENTS.md](CLAUDE.md) for the full configuration reference.
|
|
|
216
265
|
|
|
217
266
|
### Context
|
|
218
267
|
|
|
219
|
-
Handlers receive a
|
|
268
|
+
Handlers receive a shared `Context`, with typed helpers for declared enrichment and error contracts:
|
|
220
269
|
|
|
221
270
|
| Property | Type | Description |
|
|
222
271
|
|:---------|:-----|:------------|
|
|
@@ -224,7 +273,10 @@ Handlers receive a unified `Context` object:
|
|
|
224
273
|
| `ctx.state` | `ContextState` | Tenant-scoped key-value storage |
|
|
225
274
|
| `ctx.requestInput` | `(spec) => never` | Suspend and ask the caller for more input; the handler is re-entered with the answers |
|
|
226
275
|
| `ctx.inputs` | `ContextInputs` | Reader over a retried request's responses — `.accepted()`, `.view()`, `.state()`, `.dropped` |
|
|
227
|
-
| `ctx.
|
|
276
|
+
| `ctx.enrich` | `Enrich` / `TypedEnrich<E>` | Add declared result context to structured output and text content |
|
|
277
|
+
| `ctx.content` | `ContentCollect` | Attach image/audio blocks to `content[]` — `content.image(data, mimeType)`, `content.audio(...)`, or a raw block |
|
|
278
|
+
| `ctx.fail` | `(reason, msg?, data?) => McpError` | Creates an error for `throw ctx.fail(...)`; available with a declared `errors` contract |
|
|
279
|
+
| `ctx.recoveryFor` | `(reason) => object` | Resolves a declared recovery hint to `{ recovery: { hint } }` — spread into `ctx.fail`'s data argument |
|
|
228
280
|
| `ctx.signal` | `AbortSignal` | Cancellation signal |
|
|
229
281
|
| `ctx.notifyResourceUpdated` | `Function?` | Notify subscribed clients a resource changed |
|
|
230
282
|
| `ctx.notifyResourceListChanged` | `Function?` | Notify clients the resource list changed |
|
|
@@ -232,6 +284,9 @@ Handlers receive a unified `Context` object:
|
|
|
232
284
|
| `ctx.notifyToolListChanged` | `Function?` | Notify clients the tool list changed |
|
|
233
285
|
| `ctx.requestId` | `string` | Unique request ID |
|
|
234
286
|
| `ctx.tenantId` | `string?` | Tenant ID (JWT `tid` claim, or `'default'` for stdio and HTTP+`MCP_AUTH_MODE=none`) |
|
|
287
|
+
| `ctx.auth` | `AuthContext?` | Token claims and scopes when the request is authenticated |
|
|
288
|
+
| `ctx.sessionId` | `string?` | HTTP session ID in stateful/`auto` session mode — a scoping key, not an authorization principal |
|
|
289
|
+
| `ctx.uri` | `URL?` | The parsed resource URI; set in resource handlers only |
|
|
235
290
|
|
|
236
291
|
### Subpath exports
|
|
237
292
|
|
|
@@ -245,6 +300,7 @@ import { OpenRouterProvider, GraphService } from '@cyanheads/mcp-ts-core/service
|
|
|
245
300
|
import type { DataCanvas, CanvasInstance } from '@cyanheads/mcp-ts-core/canvas';
|
|
246
301
|
import { validateDefinitions } from '@cyanheads/mcp-ts-core/linter';
|
|
247
302
|
import { createMockContext } from '@cyanheads/mcp-ts-core/testing';
|
|
303
|
+
import { mcpTest, toolContractSuite } from '@cyanheads/mcp-ts-core/testing/vitest';
|
|
248
304
|
import { fuzzTool, fuzzResource, fuzzPrompt } from '@cyanheads/mcp-ts-core/testing/fuzz';
|
|
249
305
|
```
|
|
250
306
|
|
|
@@ -252,7 +308,7 @@ See [CLAUDE.md/AGENTS.md](CLAUDE.md) for the complete exports reference.
|
|
|
252
308
|
|
|
253
309
|
## Examples
|
|
254
310
|
|
|
255
|
-
The `examples/` directory contains a reference server consuming core through public exports, demonstrating
|
|
311
|
+
The `examples/` directory contains a reference server consuming core through public exports, demonstrating core patterns:
|
|
256
312
|
|
|
257
313
|
| Tool | Pattern |
|
|
258
314
|
|:-----|:--------|
|
|
@@ -260,12 +316,13 @@ The `examples/` directory contains a reference server consuming core through pub
|
|
|
260
316
|
| `template_cat_fact` | External API call, error factories |
|
|
261
317
|
| `template_madlibs_elicitation` | `ctx.requestInput` / `ctx.inputs` for multi-round-trip input |
|
|
262
318
|
| `template_image_test` | Image content blocks |
|
|
263
|
-
| `template_data_explorer` | MCP Apps with linked UI resource
|
|
319
|
+
| `template_data_explorer` | MCP Apps with a linked HTML UI resource |
|
|
264
320
|
|
|
265
321
|
## Testing
|
|
266
322
|
|
|
267
323
|
```ts
|
|
268
324
|
import { createMockContext } from '@cyanheads/mcp-ts-core/testing';
|
|
325
|
+
import { mcpTest, toolContractSuite } from '@cyanheads/mcp-ts-core/testing/vitest';
|
|
269
326
|
import { myTool } from '@/mcp-server/tools/definitions/my-tool.tool.js';
|
|
270
327
|
|
|
271
328
|
const ctx = createMockContext();
|
|
@@ -277,9 +334,7 @@ const result = await myTool.handler(input, ctx);
|
|
|
277
334
|
|
|
278
335
|
`/testing` also exports `createMockSession()` for session-bound contexts, `createFetchMock()` for upstream HTTP boundaries, and `runToolContract()` to drive a definition through schema, handler, formatting, and error-envelope checks. `/testing/vitest` adds the `mcpTest` fixtures (`ctx`, `session`, `fetchMock`, `storage`) and `toolContractSuite()`.
|
|
279
336
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
Schema-aware fuzz testing via `fast-check`. Generates valid inputs from Zod schemas and adversarial payloads (prototype pollution, injection strings, type confusion) to verify handler invariants.
|
|
337
|
+
For fuzz testing, `/testing/fuzz` uses `fast-check` to generate valid inputs from Zod schemas and adversarial payloads that probe for crashes, data leaks, and prototype pollution:
|
|
283
338
|
|
|
284
339
|
```ts
|
|
285
340
|
import { fuzzTool } from '@cyanheads/mcp-ts-core/testing/fuzz';
|
|
@@ -296,15 +351,15 @@ Also exports `fuzzResource`, `fuzzPrompt`, `zodToArbitrary`, and `ADVERSARIAL_ST
|
|
|
296
351
|
|
|
297
352
|
- **[CLAUDE.md/AGENTS.md](CLAUDE.md)** — Framework reference: exports catalog, patterns, Context interface, error codes, auth, config, testing. Ships in the npm package and is auto-accessible in your project after `init`.
|
|
298
353
|
- **[docs/telemetry/](docs/telemetry/)** — OpenTelemetry: full catalog of spans, metrics, and attributes the framework emits ([observability.md](docs/telemetry/observability.md)), plus an example Grafana dashboard and vendor-agnostic query recipes for Datadog, New Relic, Honeycomb ([dashboards.md](docs/telemetry/dashboards.md)).
|
|
299
|
-
- **[CHANGELOG.md](CHANGELOG.md)** — Version history. Each entry includes a summary, migration notes, and links to commits/issues.
|
|
354
|
+
- **[CHANGELOG.md](CHANGELOG.md)** — Version history. Each entry includes a summary, migration notes, and links to commits/issues. Directory-based changelogs that work well for Agents. Entries include agent-specific notes per version as needed.
|
|
300
355
|
|
|
301
356
|
## Development
|
|
302
357
|
|
|
303
358
|
```bash
|
|
304
359
|
bun run rebuild # clean + build (scripts/clean.ts + scripts/build.ts)
|
|
305
|
-
bun run devcheck # full gate: lint/format, typecheck, MCP defs, framework antipatterns, docs/skills/changelog sync,
|
|
360
|
+
bun run devcheck # full gate: lint/format, typecheck, MCP defs, framework antipatterns, docs/skills/changelog sync, audit, outdated, secrets/TODO scan
|
|
306
361
|
bun run lint:mcp # validate MCP definitions against spec
|
|
307
|
-
bun run test:all #
|
|
362
|
+
bun run test:all # rebuild + coverage + Node.js + Workers + integration
|
|
308
363
|
```
|
|
309
364
|
|
|
310
365
|
## License
|
package/biome.json
CHANGED
|
@@ -11,8 +11,8 @@ agent-notes: |
|
|
|
11
11
|
|
|
12
12
|
## Added
|
|
13
13
|
|
|
14
|
-
- **`bun run test:leaks` is an async-resource retention gate** ([#404](https://github.com/cyanheads/mcp-ts-core/issues/404)) — it runs the root runtime projects on real Node, observes every allocation from file collection through worker teardown, and exits nonzero on retained operations, missing evidence, or forced termination. Its sentinel suite runs separately as `bun run test:leak-gate`. Scope and evidence: [tests/leaks/README.md](tests/leaks/README.md).
|
|
15
|
-
- **Benchmark lanes** — `bun run bench` / `bench:node` for micro workloads over the tool pipeline, storage, and rate limiter, and `bench:io` / `bench:io:bun` for opt-in HTTP, Workerd, and native I/O measurements. Reports carry runtime and machine provenance; no timing threshold enters `test:all`. See [tests/benchmarks/README.md](tests/benchmarks/README.md).
|
|
14
|
+
- **`bun run test:leaks` is an async-resource retention gate** ([#404](https://github.com/cyanheads/mcp-ts-core/issues/404)) — it runs the root runtime projects on real Node, observes every allocation from file collection through worker teardown, and exits nonzero on retained operations, missing evidence, or forced termination. Its sentinel suite runs separately as `bun run test:leak-gate`. Scope and evidence: [tests/leaks/README.md](https://github.com/cyanheads/mcp-ts-core/blob/v0.12.6/tests/leaks/README.md).
|
|
15
|
+
- **Benchmark lanes** — `bun run bench` / `bench:node` for micro workloads over the tool pipeline, storage, and rate limiter, and `bench:io` / `bench:io:bun` for opt-in HTTP, Workerd, and native I/O measurements. Reports carry runtime and machine provenance; no timing threshold enters `test:all`. See [tests/benchmarks/README.md](https://github.com/cyanheads/mcp-ts-core/blob/v0.12.6/tests/benchmarks/README.md).
|
|
16
16
|
- **`bun run test:worker` also boots the built bundle standalone** ([#406](https://github.com/cyanheads/mcp-ts-core/issues/406)) — a Wrangler-hosted lane outside the Workerd test pool's module loader, which is where a startup-time failure is observable at all.
|
|
17
17
|
|
|
18
18
|
## Changed
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Argument rejections now carry the structured error envelope, classified as InvalidParams, on every transport. The Node and workerd type environments are checked as separate TypeScript programs, which lifts the @cloudflare/workers-types hold."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
agent-notes: |
|
|
6
|
+
Two adoption points. A test asserting `JsonRpcErrorCode.ValidationError` for a
|
|
7
|
+
tool's argument rejection must now expect `InvalidParams`. A project whose
|
|
8
|
+
tsconfig loads `node` alongside `@cloudflare/workers-types` can split the two
|
|
9
|
+
programs the way `tsconfig.worker.json` does here, and drop any
|
|
10
|
+
`@cloudflare/workers-types` version hold it carried for the Buffer collision.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# 0.12.7 — 2026-09-08
|
|
14
|
+
|
|
15
|
+
## Added
|
|
16
|
+
|
|
17
|
+
- **`bun run typecheck:worker`** — the workerd lane checked in its own program (`tsconfig.worker.json`) against the built declarations, so run it after a build. `devcheck` runs it as the `TypeScript (Worker)` check when `dist/` exists.
|
|
18
|
+
|
|
19
|
+
## Changed
|
|
20
|
+
|
|
21
|
+
- **README rewritten** around a quick start, one worked tool, and a capability table. The package description shortens to "Agent-native TypeScript framework for MCP servers. Includes runtime infrastructure and agent skills for building, testing, and shipping servers." in `package.json`, `CITATION.cff`, and the Docker OCI label; `server.json` carries the first sentence.
|
|
22
|
+
- **Gating a tool now carries a reference audit** ([#402](https://github.com/cyanheads/mcp-ts-core/issues/402)) — `disabledTool()` removes a tool from `tools/list` but rewrites nothing else, so `add-tool` and `design-mcp-server` require that prose, `errors[].recovery` text, and `nextToolSuggestions` entries naming a gated tool be resolved from the same config that decides registration.
|
|
23
|
+
- **Scaffold devDependency pins** — `@biomejs/biome` 2.5.5 → 2.5.12, `@types/node` 26.1.1 → 26.4.0, `vitest` and `@vitest/coverage-istanbul` 4.1.10 → 4.1.11, `ignore` ^7.0.6 → ^7.0.7, `tsc-alias` ^1.9.1 → ^1.9.2.
|
|
24
|
+
- Shipped skills: `add-tool` 2.22, `api-errors` 1.9, `design-mcp-server` 2.24, `field-test` 2.10, `git-wrapup` 1.12.
|
|
25
|
+
|
|
26
|
+
## Fixed
|
|
27
|
+
|
|
28
|
+
- **An argument rejection carries `structuredContent.error`** ([#377](https://github.com/cyanheads/mcp-ts-core/issues/377)) — an unknown root key, wrong type, missing required field, or failed constraint returns `isError: true` with `error.code` `-32602` and the same readable `Invalid arguments for tool <name>: …` text as before, on the 2025-11-25 and 2026-07-28 revisions and under workerd. `registerTool` receives a schema that projects the source JSON Schema verbatim and defers validation to the handler, which never runs for a rejected call. Direct handler tests that expected `ValidationError` now see `InvalidParams`.
|
|
29
|
+
- **Node and workerd ambient types no longer share a program** ([#397](https://github.com/cyanheads/mcp-ts-core/issues/397)) — `@cloudflare/workers-types` declares `Buffer` as an ambient `const … : any`, which stripped the encoding-aware `toString()` overloads off every Node Buffer while `skipLibCheck` hid the redeclaration. The root and build configs load Node globals only; `tsconfig.worker.json` loads the workerd set for `tests/worker`. Worker types still reach `src/` through module imports.
|
|
30
|
+
- **`bun outdated` rows resolve to the dependency key that declared them** ([#398](https://github.com/cyanheads/mcp-ts-core/issues/398)) — an `npm:` alias is printed under its resolved target, so an allowlist entry keyed on the `package.json` key could never match. Rows are attributed through declared ranges and reprinted under the declared key; where a direct dependency and an alias share a target and attribution stays ambiguous, the row is called out rather than allowlisted.
|
|
31
|
+
- **Packaging validation compares each plugin manifest's `version` with `package.json`** ([#393](https://github.com/cyanheads/mcp-ts-core/issues/393)) — a missing or stale `version` in `.claude-plugin/plugin.json` or `.codex-plugin/plugin.json` fails with the path and expected version. `checkPluginManifests()` takes the package version as a fourth argument; absent manifests and the `packaging.pluginManifests` opt-out are unchanged. Both manifests are named in the `git-wrapup` version lists.
|
|
32
|
+
- **The field-test helper initializes against a stateless server** ([#391](https://github.com/cyanheads/mcp-ts-core/issues/391)) — `mcp_init` decides success on the initialize result rather than the `Mcp-Session-Id` header, and fails loudly on a transport error, a non-2xx status, a JSON-RPC error, or a result with no `protocolVersion`. `mcp_call` accepts an empty `sid`, omits the session header when there is none, and takes the negotiated protocol as a fifth argument.
|
|
33
|
+
|
|
34
|
+
## Dependencies
|
|
35
|
+
|
|
36
|
+
- `@cloudflare/workers-types` 5.20260730.1 → 5.20260905.1 (the exact pin held for #397 is lifted, and its `devcheck.config.json` allowlist entry removed)
|
|
37
|
+
- `@opentelemetry/exporter-metrics-otlp-http`, `@opentelemetry/exporter-trace-otlp-http`, `@opentelemetry/instrumentation-http`, `@opentelemetry/sdk-node` ^0.221.0 → ^0.222.0; `@opentelemetry/instrumentation-pino` ^0.67.0 → ^0.68.0 (peer floor ^0.66.0 → ^0.68.0)
|
|
38
|
+
- `hono` ^4.13.5 → ^4.13.7, `jose` ^6.2.10 → ^6.2.12
|
|
39
|
+
- `@supabase/supabase-js` ^2.112.4 → ^2.115.0, `openai` ^7.8.0 → ^7.10.0, `@biomejs/biome` 2.5.11 → 2.5.12, `@types/bun` ^1.4.0 → ^1.4.1, `bun-types` ^1.4.0 → ^1.4.2
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Releases run through a gated release PR, runInContext re-establishes the span it names, and the config, services, and utils surfaces drop fields that were parsed and never read — including MCP_RESPONSE_VERBOSITY and the OAUTH_PROXY_* block."
|
|
3
|
+
breaking: true
|
|
4
|
+
security: false
|
|
5
|
+
agent-notes: |
|
|
6
|
+
Removals to sweep for. Drop `MCP_RESPONSE_VERBOSITY` and every `OAUTH_PROXY_*`
|
|
7
|
+
entry from `.env`, `.env.example`, and deployment config — they are no longer
|
|
8
|
+
recognized. Replace `supportsTTS(provider)` / `supportsSTT(provider)` with the
|
|
9
|
+
`provider.supportsTTS` / `provider.supportsSTT` booleans they read. Drop
|
|
10
|
+
`format`, `language`, `pitch`, `volume`, and `options` from speech call sites;
|
|
11
|
+
none reached a provider, and `speed` now does.
|
|
12
|
+
|
|
13
|
+
A server using the `supabase` storage provider needs `SUPABASE_SERVICE_ROLE_KEY`.
|
|
14
|
+
`SUPABASE_ANON_KEY` is now optional and the framework never reads it — a
|
|
15
|
+
deployment carrying the URL and the service-role key but no anon key
|
|
16
|
+
previously got no `config.supabase` at all.
|
|
17
|
+
|
|
18
|
+
Release flow: a project adopting the gated release PR declares it in its
|
|
19
|
+
`CLAUDE.md`/`AGENTS.md`, and its repo needs squash- and rebase-merge disabled
|
|
20
|
+
(`gh repo edit --enable-squash-merge=false --enable-rebase-merge=false`).
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# 0.12.8 — 2026-09-09
|
|
24
|
+
|
|
25
|
+
## Added
|
|
26
|
+
|
|
27
|
+
- **`release-pr-review` skill** — the review pass between `git-wrapup` and `release-and-publish` in gated release PR mode: reads the PR's commit range through the `code-simplifier` lens, lands fixes as fixup commits autosquashed into the stack, and keeps the PR body in sync with what ships.
|
|
28
|
+
- **Field-test size and latency readings** — `mcp_call` prints reply bytes, the `content` / `structuredContent` split, a token estimate, and wall-clock per call; the new `mcp_catalog_size` weighs `tools/list`, the context every client loads per session.
|
|
29
|
+
|
|
30
|
+
## Changed
|
|
31
|
+
|
|
32
|
+
- **Releases run through a gated release PR** ([#413](https://github.com/cyanheads/mcp-ts-core/issues/413)) — `git-wrapup` now stops at a pushed `release/<version>` branch with an open PR and creates no tag; `release-and-publish` fast-forwards `main` locally, creates the tag on its tip, and publishes. A project opts in from its `CLAUDE.md`/`AGENTS.md`; without a declaration the stack still lands on `main`.
|
|
33
|
+
- **`supabase` config activates on `SUPABASE_URL` alone** — `supabase.anonKey` is optional and the framework never reads it. A deployment with the URL and `SUPABASE_SERVICE_ROLE_KEY` but no anon key previously got no `config.supabase` at all, failing the storage provider's configuration check.
|
|
34
|
+
- **`runToolContract` renders `content[]` through the production path** — `/testing` carried its own copy of format-plus-collected-blocks; it and the tool handler factory now share `renderToolContent`, so a `format()` throw classifies as `InternalError` carrying the formatter error as `cause`.
|
|
35
|
+
- **`SpeechService.healthCheck()` probes both providers in parallel** rather than one after the other.
|
|
36
|
+
- **Internal consolidation** — the storage providers share `providerHelpers.ts` (batch fan-out, TTL envelope, cursor paging, `LIKE` escaping), the tool and resource handler factories share `handlerContext.ts`, and the linter reads Zod internals through one `zodDef()` accessor, dropping its Zod 3 `_def` fallbacks.
|
|
37
|
+
- Shipped skills: `add-provider` 1.1, `api-config` 1.16, `api-services` 1.5, `api-telemetry` 1.8, `api-utils` 2.9, `code-simplifier` 1.4, `field-test` 2.12, `git-wrapup` 1.13, `orchestrations` 1.8, `release-and-publish` 2.14, `release-pr-review` 1.0.
|
|
38
|
+
|
|
39
|
+
## Removed
|
|
40
|
+
|
|
41
|
+
- **`./config`** — `AppConfig.mcpResponseVerbosity` (`MCP_RESPONSE_VERBOSITY`) and `AppConfig.oauthProxy` (`OAUTH_PROXY_AUTHORIZATION_URL`, `OAUTH_PROXY_TOKEN_URL`, `OAUTH_PROXY_REVOCATION_URL`, `OAUTH_PROXY_ISSUER_URL`, `OAUTH_PROXY_SERVICE_DOCUMENTATION_URL`, `OAUTH_PROXY_DEFAULT_CLIENT_REDIRECT_URIS`). Both were validated at startup and read by nothing.
|
|
42
|
+
- **`./services`** — `supportsSTT` and `supportsTTS`; the types `GraphPattern`, `GraphProviderType`, `GraphServiceConfig`, `PatternMatchResult`; `SpeechProviderConfig.options` and its `'mock'` provider; and the speech option fields no provider ever sent upstream — `VoiceSettings.pitch`, `VoiceSettings.volume`, `TextToSpeechOptions.format`, `TextToSpeechOptions.language`, `WordTimestamp.confidence`, `SpeechToTextResult.confidence`, `Voice.languages`.
|
|
43
|
+
- **`./utils`** — `BaseErrorMapping.messageTemplate`, which the error classifier never consulted.
|
|
44
|
+
|
|
45
|
+
## Fixed
|
|
46
|
+
|
|
47
|
+
- **`runInContext(ctx, fn)` re-establishes the span `ctx` names** — it re-entered whatever context was already active, so a span opened inside `fn` after an async hop (`setTimeout`, `queueMicrotask`) started a new trace instead of parenting to the request's span.
|
|
48
|
+
- **A trailing slash on `MCP_PUBLIC_URL` no longer doubles in emitted links** — the landing page and the `/.well-known/mcp.json` server card built URLs straight off the configured value; every advertised origin now goes through `resolvePublicOrigin`, which the OAuth metadata paths already had.
|
|
49
|
+
- **`VoiceSettings.speed` reaches ElevenLabs** — `textToSpeech` accepted the field and dropped it before the request body. ElevenLabs accepts 0.7 to 1.2.
|
|
50
|
+
- **The Worker consumer lane typechecks with `skipLibCheck: true`** ([#411](https://github.com/cyanheads/mcp-ts-core/issues/411)) — from `openai` 7.12.1 its `undici-types` probe resolves and pulls `@types/node` into a program carrying `@cloudflare/workers-types`, colliding on `Buffer`, `console`, and `Event`. The Node lane still checks the shipped declarations strictly.
|
|
51
|
+
|
|
52
|
+
## Dependencies
|
|
53
|
+
|
|
54
|
+
- `@opentelemetry/resources`, `@opentelemetry/sdk-metrics`, `@opentelemetry/sdk-trace-node` ^2.10.0 → ^2.11.0
|
|
55
|
+
- `ignore` ^7.0.7 → ^7.0.8, `tsc-alias` ^1.9.2 → ^1.9.4
|