@cyanheads/mcp-ts-core 0.13.7 → 0.13.9
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 +19 -15
- package/CLAUDE.md +19 -15
- package/README.md +4 -2
- package/changelog/0.13.x/0.13.8.md +101 -0
- package/changelog/0.13.x/0.13.9.md +113 -0
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +39 -9
- package/dist/config/index.js.map +1 -1
- package/dist/core/app.d.ts +6 -3
- package/dist/core/app.d.ts.map +1 -1
- package/dist/core/app.js +20 -6
- package/dist/core/app.js.map +1 -1
- package/dist/core/context.d.ts +25 -1
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +13 -3
- package/dist/core/context.js.map +1 -1
- package/dist/core/serverManifest.d.ts +6 -0
- package/dist/core/serverManifest.d.ts.map +1 -1
- package/dist/core/serverManifest.js +6 -0
- package/dist/core/serverManifest.js.map +1 -1
- package/dist/core/worker.d.ts +2 -0
- package/dist/core/worker.d.ts.map +1 -1
- package/dist/core/worker.js +2 -0
- package/dist/core/worker.js.map +1 -1
- package/dist/linter/rules/enrichment-rules.d.ts +3 -2
- package/dist/linter/rules/enrichment-rules.d.ts.map +1 -1
- package/dist/linter/rules/enrichment-rules.js +9 -2
- package/dist/linter/rules/enrichment-rules.js.map +1 -1
- package/dist/linter/rules/handler-body-rules.d.ts.map +1 -1
- package/dist/linter/rules/handler-body-rules.js +10 -4
- package/dist/linter/rules/handler-body-rules.js.map +1 -1
- package/dist/linter/rules/schema-rules.d.ts +5 -0
- package/dist/linter/rules/schema-rules.d.ts.map +1 -1
- package/dist/linter/rules/schema-rules.js +44 -17
- package/dist/linter/rules/schema-rules.js.map +1 -1
- package/dist/linter/rules/tool-rules.d.ts +2 -1
- package/dist/linter/rules/tool-rules.d.ts.map +1 -1
- package/dist/linter/rules/tool-rules.js +36 -1
- package/dist/linter/rules/tool-rules.js.map +1 -1
- package/dist/mcp-server/inputRequired.d.ts +14 -5
- package/dist/mcp-server/inputRequired.d.ts.map +1 -1
- package/dist/mcp-server/inputRequired.js +15 -8
- package/dist/mcp-server/inputRequired.js.map +1 -1
- package/dist/mcp-server/outputContract.d.ts +33 -0
- package/dist/mcp-server/outputContract.d.ts.map +1 -0
- package/dist/mcp-server/outputContract.js +43 -0
- package/dist/mcp-server/outputContract.js.map +1 -0
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts.map +1 -1
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.js +10 -2
- package/dist/mcp-server/resources/utils/resourceHandlerFactory.js.map +1 -1
- package/dist/mcp-server/tools/utils/inputPrevalidation.d.ts +163 -40
- package/dist/mcp-server/tools/utils/inputPrevalidation.d.ts.map +1 -1
- package/dist/mcp-server/tools/utils/inputPrevalidation.js +330 -114
- package/dist/mcp-server/tools/utils/inputPrevalidation.js.map +1 -1
- package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts +39 -15
- package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -1
- package/dist/mcp-server/tools/utils/toolHandlerFactory.js +361 -93
- package/dist/mcp-server/tools/utils/toolHandlerFactory.js.map +1 -1
- package/dist/mcp-server/transports/auth/lib/authUtils.js +4 -1
- package/dist/mcp-server/transports/auth/lib/authUtils.js.map +1 -1
- package/dist/mcp-server/transports/auth/strategies/jwtStrategy.d.ts.map +1 -1
- package/dist/mcp-server/transports/auth/strategies/jwtStrategy.js +1 -1
- package/dist/mcp-server/transports/auth/strategies/jwtStrategy.js.map +1 -1
- package/dist/mcp-server/transports/auth/strategies/oauthStrategy.d.ts.map +1 -1
- package/dist/mcp-server/transports/auth/strategies/oauthStrategy.js +2 -5
- package/dist/mcp-server/transports/auth/strategies/oauthStrategy.js.map +1 -1
- package/dist/mcp-server/transports/http/httpTransport.d.ts.map +1 -1
- package/dist/mcp-server/transports/http/httpTransport.js +65 -9
- package/dist/mcp-server/transports/http/httpTransport.js.map +1 -1
- package/dist/mcp-server/transports/http/sessionStore.d.ts.map +1 -1
- package/dist/mcp-server/transports/http/sessionStore.js +2 -2
- package/dist/mcp-server/transports/http/sessionStore.js.map +1 -1
- package/dist/mcp-server/transports/stdio/stdioTransport.d.ts +9 -5
- package/dist/mcp-server/transports/stdio/stdioTransport.d.ts.map +1 -1
- package/dist/mcp-server/transports/stdio/stdioTransport.js +9 -5
- package/dist/mcp-server/transports/stdio/stdioTransport.js.map +1 -1
- package/dist/services/canvas/core/CanvasRegistry.d.ts +6 -2
- package/dist/services/canvas/core/CanvasRegistry.d.ts.map +1 -1
- package/dist/services/canvas/core/CanvasRegistry.js +8 -4
- package/dist/services/canvas/core/CanvasRegistry.js.map +1 -1
- package/dist/services/canvas/core/DataCanvas.d.ts.map +1 -1
- package/dist/services/canvas/core/DataCanvas.js +7 -5
- package/dist/services/canvas/core/DataCanvas.js.map +1 -1
- package/dist/services/canvas/core/canvasFactory.d.ts.map +1 -1
- package/dist/services/canvas/core/canvasFactory.js +2 -2
- package/dist/services/canvas/core/canvasFactory.js.map +1 -1
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts +82 -18
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts.map +1 -1
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.js +645 -344
- package/dist/services/canvas/providers/duckdb/DuckdbProvider.js.map +1 -1
- package/dist/services/canvas/providers/duckdb/exportWriter.d.ts +11 -7
- package/dist/services/canvas/providers/duckdb/exportWriter.d.ts.map +1 -1
- package/dist/services/canvas/providers/duckdb/exportWriter.js +19 -16
- package/dist/services/canvas/providers/duckdb/exportWriter.js.map +1 -1
- package/dist/services/llm/providers/openrouter.provider.js +1 -1
- package/dist/services/llm/providers/openrouter.provider.js.map +1 -1
- package/dist/services/mirror/core/defineMirror.d.ts +1 -0
- package/dist/services/mirror/core/defineMirror.d.ts.map +1 -1
- package/dist/services/mirror/core/defineMirror.js +1 -0
- package/dist/services/mirror/core/defineMirror.js.map +1 -1
- package/dist/services/speech/providers/elevenlabs.provider.js +3 -3
- 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 +5 -5
- package/dist/services/speech/providers/whisper.provider.js.map +1 -1
- package/dist/storage/core/StorageService.d.ts.map +1 -1
- package/dist/storage/core/StorageService.js +3 -6
- package/dist/storage/core/StorageService.js.map +1 -1
- package/dist/storage/core/storageFactory.d.ts.map +1 -1
- package/dist/storage/core/storageFactory.js +12 -15
- package/dist/storage/core/storageFactory.js.map +1 -1
- package/dist/storage/core/storageValidation.d.ts +13 -13
- package/dist/storage/core/storageValidation.d.ts.map +1 -1
- package/dist/storage/core/storageValidation.js +49 -125
- package/dist/storage/core/storageValidation.js.map +1 -1
- package/dist/storage/providers/cloudflare/d1Provider.d.ts.map +1 -1
- package/dist/storage/providers/cloudflare/d1Provider.js +5 -3
- 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 +1 -1
- package/dist/storage/providers/cloudflare/kvProvider.js.map +1 -1
- package/dist/storage/providers/cloudflare/r2Provider.d.ts.map +1 -1
- package/dist/storage/providers/cloudflare/r2Provider.js +3 -3
- package/dist/storage/providers/cloudflare/r2Provider.js.map +1 -1
- package/dist/storage/providers/fileSystem/fileSystemProvider.js +4 -4
- package/dist/storage/providers/fileSystem/fileSystemProvider.js.map +1 -1
- package/dist/storage/providers/inMemory/inMemoryProvider.d.ts +1 -1
- package/dist/storage/providers/inMemory/inMemoryProvider.d.ts.map +1 -1
- package/dist/storage/providers/inMemory/inMemoryProvider.js +6 -5
- package/dist/storage/providers/inMemory/inMemoryProvider.js.map +1 -1
- package/dist/testing/fuzz.d.ts.map +1 -1
- package/dist/testing/fuzz.js +7 -1
- package/dist/testing/fuzz.js.map +1 -1
- package/dist/testing/index.d.ts +15 -2
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +51 -6
- package/dist/testing/index.js.map +1 -1
- package/dist/types-global/errors.d.ts +7 -4
- package/dist/types-global/errors.d.ts.map +1 -1
- package/dist/types-global/errors.js.map +1 -1
- package/dist/utils/formatting/codeSpan.d.ts +27 -0
- package/dist/utils/formatting/codeSpan.d.ts.map +1 -0
- package/dist/utils/formatting/codeSpan.js +42 -0
- package/dist/utils/formatting/codeSpan.js.map +1 -0
- package/dist/utils/formatting/diffFormatter.d.ts.map +1 -1
- package/dist/utils/formatting/diffFormatter.js +7 -15
- package/dist/utils/formatting/diffFormatter.js.map +1 -1
- package/dist/utils/formatting/markdownBuilder.d.ts +12 -5
- package/dist/utils/formatting/markdownBuilder.d.ts.map +1 -1
- package/dist/utils/formatting/markdownBuilder.js +14 -2
- package/dist/utils/formatting/markdownBuilder.js.map +1 -1
- package/dist/utils/formatting/tableFormatter.d.ts.map +1 -1
- package/dist/utils/formatting/tableFormatter.js +5 -9
- package/dist/utils/formatting/tableFormatter.js.map +1 -1
- package/dist/utils/formatting/treeFormatter.d.ts.map +1 -1
- package/dist/utils/formatting/treeFormatter.js +5 -9
- package/dist/utils/formatting/treeFormatter.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/internal/error-handler/errorHandler.d.ts +17 -10
- package/dist/utils/internal/error-handler/errorHandler.d.ts.map +1 -1
- package/dist/utils/internal/error-handler/errorHandler.js +47 -26
- package/dist/utils/internal/error-handler/errorHandler.js.map +1 -1
- package/dist/utils/internal/error-handler/mappings.d.ts +17 -1
- package/dist/utils/internal/error-handler/mappings.d.ts.map +1 -1
- package/dist/utils/internal/error-handler/mappings.js +22 -1
- package/dist/utils/internal/error-handler/mappings.js.map +1 -1
- package/dist/utils/internal/error-handler/types.d.ts +2 -0
- package/dist/utils/internal/error-handler/types.d.ts.map +1 -1
- package/dist/utils/internal/logger.d.ts +75 -3
- package/dist/utils/internal/logger.d.ts.map +1 -1
- package/dist/utils/internal/logger.js +181 -52
- package/dist/utils/internal/logger.js.map +1 -1
- package/dist/utils/internal/performance.d.ts +11 -0
- package/dist/utils/internal/performance.d.ts.map +1 -1
- package/dist/utils/internal/performance.js +46 -12
- package/dist/utils/internal/performance.js.map +1 -1
- package/dist/utils/network/fetchWithTimeout.d.ts +11 -5
- package/dist/utils/network/fetchWithTimeout.d.ts.map +1 -1
- package/dist/utils/network/fetchWithTimeout.js +50 -23
- package/dist/utils/network/fetchWithTimeout.js.map +1 -1
- package/dist/utils/network/pacer.d.ts +38 -5
- package/dist/utils/network/pacer.d.ts.map +1 -1
- package/dist/utils/network/pacer.js +87 -25
- package/dist/utils/network/pacer.js.map +1 -1
- package/dist/utils/network/retry.d.ts +16 -8
- package/dist/utils/network/retry.d.ts.map +1 -1
- package/dist/utils/network/retry.js +19 -8
- package/dist/utils/network/retry.js.map +1 -1
- package/dist/utils/overflow/outlineOnOverflow.d.ts +18 -2
- package/dist/utils/overflow/outlineOnOverflow.d.ts.map +1 -1
- package/dist/utils/overflow/outlineOnOverflow.js +28 -3
- package/dist/utils/overflow/outlineOnOverflow.js.map +1 -1
- package/dist/utils/pagination/pagination.d.ts +3 -1
- package/dist/utils/pagination/pagination.d.ts.map +1 -1
- package/dist/utils/pagination/pagination.js +10 -2
- package/dist/utils/pagination/pagination.js.map +1 -1
- package/dist/utils/parsing/csvParser.d.ts.map +1 -1
- package/dist/utils/parsing/csvParser.js +4 -2
- package/dist/utils/parsing/csvParser.js.map +1 -1
- package/dist/utils/parsing/htmlExtractor.js +1 -1
- package/dist/utils/parsing/htmlExtractor.js.map +1 -1
- package/dist/utils/parsing/jsonParser.d.ts.map +1 -1
- package/dist/utils/parsing/jsonParser.js +3 -1
- package/dist/utils/parsing/jsonParser.js.map +1 -1
- package/dist/utils/parsing/xmlParser.d.ts.map +1 -1
- package/dist/utils/parsing/xmlParser.js +3 -1
- package/dist/utils/parsing/xmlParser.js.map +1 -1
- package/dist/utils/parsing/yamlParser.d.ts.map +1 -1
- package/dist/utils/parsing/yamlParser.js +3 -1
- package/dist/utils/parsing/yamlParser.js.map +1 -1
- package/dist/utils/security/idGenerator.d.ts.map +1 -1
- package/dist/utils/security/idGenerator.js +20 -4
- package/dist/utils/security/idGenerator.js.map +1 -1
- package/dist/utils/security/sanitization.d.ts +31 -0
- package/dist/utils/security/sanitization.d.ts.map +1 -1
- package/dist/utils/security/sanitization.js +98 -11
- package/dist/utils/security/sanitization.js.map +1 -1
- package/dist/utils/telemetry/attributes.d.ts +21 -2
- package/dist/utils/telemetry/attributes.d.ts.map +1 -1
- package/dist/utils/telemetry/attributes.js +21 -2
- package/dist/utils/telemetry/attributes.js.map +1 -1
- package/dist/utils/telemetry/instrumentation.d.ts +9 -3
- package/dist/utils/telemetry/instrumentation.d.ts.map +1 -1
- package/dist/utils/telemetry/instrumentation.js +85 -13
- package/dist/utils/telemetry/instrumentation.js.map +1 -1
- package/framework-skills/add-app-tool/SKILL.md +3 -3
- package/framework-skills/add-export/SKILL.md +5 -16
- package/framework-skills/add-prompt/SKILL.md +7 -3
- package/framework-skills/add-resource/SKILL.md +7 -5
- package/framework-skills/add-tool/SKILL.md +12 -10
- package/framework-skills/api-auth/SKILL.md +4 -2
- package/framework-skills/api-canvas/SKILL.md +19 -10
- package/framework-skills/api-config/SKILL.md +9 -6
- package/framework-skills/api-context/SKILL.md +16 -5
- package/framework-skills/api-errors/SKILL.md +23 -17
- package/framework-skills/api-linter/SKILL.md +32 -9
- package/framework-skills/api-mirror/SKILL.md +2 -1
- package/framework-skills/api-telemetry/SKILL.md +34 -14
- package/framework-skills/api-testing/SKILL.md +5 -3
- package/framework-skills/api-utils/SKILL.md +10 -10
- package/framework-skills/api-utils/references/formatting.md +1 -1
- package/framework-skills/api-utils/references/parsing.md +2 -2
- package/framework-skills/api-utils/references/security.md +6 -4
- package/framework-skills/design-mcp-server/SKILL.md +2 -2
- package/framework-skills/field-test/SKILL.md +4 -4
- package/framework-skills/git-wrapup/SKILL.md +12 -7
- package/framework-skills/maintenance/SKILL.md +2 -2
- package/framework-skills/orchestrations/SKILL.md +7 -6
- package/framework-skills/orchestrations/workflows/field-test-fix.md +9 -19
- package/framework-skills/orchestrations/workflows/fix-wrapup-release.md +7 -7
- package/framework-skills/orchestrations/workflows/greenfield-build.md +8 -5
- package/framework-skills/orchestrations/workflows/maintenance-release.md +8 -8
- package/framework-skills/polish-docs-meta/SKILL.md +4 -4
- package/framework-skills/polish-docs-meta/references/readme.md +1 -0
- package/framework-skills/release-and-publish/SKILL.md +7 -5
- package/framework-skills/release-pr-review/SKILL.md +37 -23
- package/framework-skills/report-issue-framework/SKILL.md +7 -5
- package/framework-skills/report-issue-local/SKILL.md +8 -6
- package/framework-skills/security-pass/SKILL.md +8 -8
- package/framework-skills/techniques/SKILL.md +1 -1
- package/framework-skills/techniques/references/outline-on-overflow.md +12 -7
- package/package.json +20 -5
- package/scripts/check-skill-versions.ts +103 -22
- package/scripts/devcheck.ts +11 -9
- package/scripts/lint-mcp.ts +87 -27
- package/scripts/lint-packaging.ts +99 -1
- package/scripts/release-github.ts +117 -5
- package/templates/.env.example +4 -0
- package/templates/Dockerfile +26 -6
- package/templates/_.mcpbignore +2 -0
- package/templates/package.json +1 -0
package/AGENTS.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Package:** `@cyanheads/mcp-ts-core`
|
|
4
|
-
**Version:** 0.13.
|
|
4
|
+
**Version:** 0.13.9
|
|
5
5
|
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
6
|
-
**MCP SDK:** `@modelcontextprotocol/server` ^2.
|
|
6
|
+
**MCP SDK:** `@modelcontextprotocol/server` ^2.1.0 (protocol revisions 2026-07-28 and 2025-*)
|
|
7
7
|
**Zod:** ^4.6.5
|
|
8
8
|
**GitHub:** [cyanheads/mcp-ts-core](https://github.com/cyanheads/mcp-ts-core)
|
|
9
9
|
**npm:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
|
|
@@ -100,9 +100,8 @@ await createApp({
|
|
|
100
100
|
tools: allToolDefinitions,
|
|
101
101
|
resources: allResourceDefinitions,
|
|
102
102
|
prompts: allPromptDefinitions,
|
|
103
|
-
instructions: // server-level orientation, sent on every initialize
|
|
104
|
-
'
|
|
105
|
-
'Other endpoints reachable via `connect({ baseUrl })`.',
|
|
103
|
+
instructions: // server-level orientation, sent on every initialize — one literal, a few sentences
|
|
104
|
+
'Calls reach the production API by default. Pass `baseUrl` to `connect` to reach another endpoint.',
|
|
106
105
|
extensions: { // SEP-2133 extensions advertised in capabilities
|
|
107
106
|
'vendor/my-extension': { /* extension config */ },
|
|
108
107
|
},
|
|
@@ -168,7 +167,7 @@ interface ServerHandle {
|
|
|
168
167
|
}
|
|
169
168
|
```
|
|
170
169
|
|
|
171
|
-
**Exit contract.** `shutdown()` is exit-free and unbounded: it also serves the startup-failure rollback and direct calls from embedders and tests, so ending the process belongs to the handlers. `SIGTERM`, `SIGINT`, and stdin EOF each run that same shutdown and then exit explicitly. A signal exits 0 once the shutdown settles and 1 when the 10 s ceiling fires, after a warning naming the step that never settled; stdin EOF exits 0 either way, unchanged. The ceiling bounds the shutdown as a whole rather than any single await, so a step that settles inside it is never truncated. A second signal mid-shutdown reaches no handler (shutdown detaches them as it starts) and terminates on the OS default, 143 / 130 — the operator's force-kill escape hatch. `uncaughtException` / `unhandledRejection` exit 1.
|
|
170
|
+
**Exit contract.** `shutdown()` is exit-free and unbounded: it also serves the startup-failure rollback and direct calls from embedders and tests, so ending the process belongs to the handlers. `SIGTERM`, `SIGINT`, and stdin EOF each run that same shutdown and then exit explicitly. On stdin EOF the SDK transport has already closed itself, so a request still in flight is aborted (its `ctx.signal` fires) and never answered — the client has hung up. A signal exits 0 once the shutdown settles and 1 when the 10 s ceiling fires, after a warning naming the step that never settled; stdin EOF exits 0 either way, unchanged. The ceiling bounds the shutdown as a whole rather than any single await, so a step that settles inside it is never truncated. A second signal mid-shutdown reaches no handler (shutdown detaches them as it starts) and terminates on the OS default, 143 / 130 — the operator's force-kill escape hatch. `uncaughtException` / `unhandledRejection` exit 1.
|
|
172
171
|
|
|
173
172
|
---
|
|
174
173
|
|
|
@@ -238,7 +237,7 @@ export const myTool = tool('my_tool', {
|
|
|
238
237
|
});
|
|
239
238
|
```
|
|
240
239
|
|
|
241
|
-
**Steps:** Create `src/mcp-server/tools/definitions/[name].tool.ts` (kebab-case) → use `tool('snake_case', {...})` with Zod `.describe()` on all fields → implement `handler(input, ctx)` (pure, throws on failure) → add `auth`/`format` if needed → register in `definitions/index.ts` → `bun run devcheck` → smoke-test with `bun run rebuild && bun run start:stdio` (or `start:http`).
|
|
240
|
+
**Steps:** Create `src/mcp-server/tools/definitions/[name].tool.ts` (kebab-case) → use `tool('snake_case', {...})` with Zod `.describe()` on all fields → implement `handler(input, ctx)` (pure, throws on failure) → add `auth`/`format` if needed → register in `definitions/index.ts` → `bun run devcheck` → smoke-test with `bun run rebuild && bun run start:stdio < /dev/null` (or `start:http`) and confirm the `Core services constructed` log record lists the tool in its `tools` field (the message text shows only counts).
|
|
242
241
|
|
|
243
242
|
**Schema constraint:** Input/output schemas must use JSON-Schema-serializable Zod types only. The MCP SDK converts schemas to JSON Schema for `tools/list` — non-serializable types (`z.custom()`, `z.date()`, `z.transform()`, `z.bigint()`, `z.symbol()`, `z.void()`, `z.map()`, `z.set()`, `z.function()`, `z.nan()`) cause a hard runtime failure. Use structural equivalents instead (e.g., `z.string()` with `.describe('ISO 8601 date')` instead of `z.date()`). The `schema-serializable` lint rule catches this at build time (`bun run lint:mcp` / `devcheck`).
|
|
244
243
|
|
|
@@ -255,7 +254,7 @@ export const myTool = tool('my_tool', {
|
|
|
255
254
|
|
|
256
255
|
**Strict input:** `tool()` stores `input.strict()`, so an unrecognized argument key is rejected by name before the handler runs and `inputSchema` advertises `additionalProperties: false`. Root-level only — a nested `z.object()` still strips unless it is strict itself. An explicit `.passthrough()` / `.catchall()` is honored. Declare `.strict()` **before** `.describe()` / `.meta()` on the root: Zod keys both to the schema instance and `.strict()` clones without it, so a root describe declared after is discarded and never advertised — `lint:mcp` reports that as `schema-root-meta-discarded`.
|
|
257
256
|
|
|
258
|
-
**Pre-validation:** an ordered step inside `parseToolArguments` rescues calls strict input would otherwise reject — drop client-added root keys (`_meta`, `tool_call_description`, `toolCallId`, any undeclared `_`-prefixed key) → rewrite key aliases (declared `inputAliases`, plus any undeclared key whose case-folded form names exactly one declared key) → parse → on failure, repair a JSON-stringified array and re-parse once, keeping
|
|
257
|
+
**Pre-validation:** an ordered step inside `parseToolArguments` rescues calls strict input would otherwise reject — drop client-added root keys (`_meta`, `tool_call_description`, `toolCallId`, any undeclared `_`-prefixed key) → rewrite key aliases (declared `inputAliases`, plus any undeclared key whose case-folded form names exactly one declared key) → parse → on failure, repair a JSON-stringified array or object, or a safe integer where a string is expected (`8654467` → `"8654467"`), and re-parse once, keeping the repair only if the schema then accepts it. If that still fails and the drop discarded a key, the stages rerun alias-first so `_query` or a declared `_q` alias reaches its target, and the retry (repair included) is kept only if it validates — a call the first order validates resolves exactly as it would without the retry. All on by default, and nothing changes the advertised `inputSchema`. A call that still fails throws the rejection of the last order tried — the retry's when it ran, so a declared `_q` alias with a bad value reports that value's failure and `Validated _q as query.` — the same one it gets under `coerce: false`, reporting that order's rewrites and underscore-rule drops as `data.input` and in the hint. A declared key, an author-opened root, and a `headerParam` target are never touched. Server-level switches: `createApp({ input: { ignoreKeys, caseStyleAliases, coerce } })`. Counters, for the attempt the handler receives: `mcp.input.ignored_key`, `mcp.input.aliased`, `mcp.input.coerced` (once per repair kind). Lint: `input-alias-conflict`. See `add-tool` skill.
|
|
259
258
|
|
|
260
259
|
**Header-mirrored input (2026-07-28):** `headerParam(z.string(), 'Region')` designates an input property with `x-mcp-header`, so its value also rides an `Mcp-Param-Region` request header and an intermediary can read it without parsing the body. Mirroring, not relocation — the handler still reads the argument from the body, and nothing else about the field changes. Only a primitive-typed (`string`/`integer`/`number`/`boolean`) property statically reachable through a chain of `properties` keys qualifies: an array element, a `z.record()` value, and every field of a discriminated-union input root are unreachable, and header names must be RFC 9110 tokens, case-insensitively unique per schema. `tool()` rejects a violation at definition time naming the field path — the SDK only warns, then conforming Streamable HTTP clients drop the tool. Lint rule: `header-param-designation`.
|
|
261
260
|
|
|
@@ -303,7 +302,7 @@ interface Context {
|
|
|
303
302
|
readonly auth?: AuthContext;
|
|
304
303
|
readonly log: ContextLogger; // auto-correlated: requestId, traceId, tenantId
|
|
305
304
|
readonly state: ContextState; // tenant-scoped KV storage
|
|
306
|
-
readonly requestInput: RequestInputFn; // (spec) => never — suspends and asks the caller for input
|
|
305
|
+
readonly requestInput: RequestInputFn; // (spec, options?) => never — suspends and asks the caller for input
|
|
307
306
|
readonly inputs: ContextInputs; // reader over a retried request's responses
|
|
308
307
|
readonly notifyPromptListChanged?: (() => void) | undefined; // prompt list changed
|
|
309
308
|
readonly notifyResourceListChanged?: (() => void) | undefined; // resource list changed
|
|
@@ -368,6 +367,11 @@ useFormat(answer.format);
|
|
|
368
367
|
cancelled prompt is terminal, not a round to retry. `inputRequired.elicitUrl({ message, url })`
|
|
369
368
|
hands the user an external link instead of a form.
|
|
370
369
|
|
|
370
|
+
A 2025-era client that declared no matching capability is refused as `client_capability_missing`,
|
|
371
|
+
with a hint that ends at reconnecting. When the tool's own arguments can stand in for the answer,
|
|
372
|
+
say so per call — `ctx.requestInput(spec, { fallbackHint: 'Or call again with noun supplied.' })` —
|
|
373
|
+
and the sentence is appended to that hint. A consent gate passes none: it has no such field.
|
|
374
|
+
|
|
371
375
|
### `ctx.content`
|
|
372
376
|
|
|
373
377
|
Accumulates non-text content blocks — image/audio bytes, embedded resources, resource links — onto the response: `ctx.content.image(data, mimeType)`, `ctx.content.audio(data, mimeType)`, or `ctx.content(block)` for a raw `ContentBlock`. Blocks are prepended to `content[]` after `format()` runs and never enter `structuredContent`, so a handler can emit media for the calling model without the base64 duplicating into typed output. Always present (no-op when unused); callable from handler and service layer.
|
|
@@ -416,9 +420,9 @@ Available factories: `invalidParams`, `invalidRequest`, `notFound`, `forbidden`,
|
|
|
416
420
|
|
|
417
421
|
For HTTP responses from upstream APIs, use `httpErrorFromResponse(response, { service, data })` from `/utils` — maps the full status table (401/403/408/422/429/5xx) and captures body + `Retry-After`.
|
|
418
422
|
|
|
419
|
-
**Auto-classification.** Plain `Error`, `ZodError`, and any other thrown value are caught and classified automatically. Resolution order: request signal already aborted (→ `RequestCancelled`, outranking the thrown value's own code, `McpError` included) → `McpError` code (preserved as-is) → SDK `ConnectionClosed` (→ `RequestCancelled`) → JS constructor name (`
|
|
423
|
+
**Auto-classification.** Plain `Error`, `ZodError`, and any other thrown value are caught and classified automatically. Resolution order: request signal already aborted (→ `RequestCancelled`, outranking the thrown value's own code, `McpError` included) → `McpError` code (preserved as-is) → SDK `ConnectionClosed` (→ `RequestCancelled`) → engine resource-limit `RangeError` by whole message — stack overflow, maximum string size (→ `InternalError`) → JS constructor name (`SyntaxError` → `ValidationError`; `TypeError` is excluded) → provider patterns (HTTP status codes, AWS errors, DB errors) → common message patterns → `AbortError` name (→ `Timeout`) → `InternalError` fallback. A result that breaks the definition's own `output` or `enrichment` schema fails as `InternalError` naming that contract, not `ValidationError`.
|
|
420
424
|
|
|
421
|
-
**Error-path parity.** Tool errors: `content[]` carries `Error: <message>`, then `Recovery: <hint>` when the hint says something the message does not already contain, then a closing `(reason … · not retryable)` for whichever of `data.reason` / `data.retryable` is present; the numeric code and `data.issues` stay JSON-only. `structuredContent.error` carries `{ code, message, data? }`. No `_meta.error`. Resources re-throw via JSON-RPC error envelope. An argument rejection is one of them: `-32602` with `data.issues`, plus `data.reason: 'invalid_arguments'` and a hint synthesized from the issues and the root schema — never a tool-declared `reason`, since the handler never ran. `client_capability_missing` is the second framework-owned reason: a `ctx.requestInput` return a 2025-era connection cannot serve is refused before any wire traffic as `-32600` carrying that reason and a hint naming the capability.
|
|
425
|
+
**Error-path parity.** Tool errors: `content[]` carries `Error: <message>`, then `Recovery: <hint>` when the hint says something the message does not already contain, then a closing `(reason … · not retryable)` for whichever of `data.reason` / `data.retryable` is present; the numeric code and `data.issues` stay JSON-only. `structuredContent.error` carries `{ code, message, data? }`. No `_meta.error`. Resources re-throw via JSON-RPC error envelope. An argument rejection is one of them: `-32602` with `data.issues`, plus `data.reason: 'invalid_arguments'` and a hint synthesized from the issues and the root schema — never a tool-declared `reason`, since the handler never ran. When pre-validation rewrote or dropped a key the caller wrote, `data.input` (`{ aliased: [{ alias, target }], ignored }`) names it and the hint closes with `Validated … as ….` / `Dropped undeclared key ….`; an ignore-list drop is never reported. `client_capability_missing` is the second framework-owned reason: a `ctx.requestInput` return a 2025-era connection cannot serve is refused before any wire traffic as `-32600` carrying that reason and a hint naming the capability.
|
|
422
426
|
|
|
423
427
|
**Lint rules** (all warnings, surfaced in `devcheck`): `prefer-mcp-error-in-handler`, `prefer-error-factory`, `preserve-cause-on-rethrow`, `no-stringify-upstream-error`, `error-contract-conformance`, `error-contract-prefer-fail`, `error-contract-unthrown` (a declared reason no literal `ctx.fail`/`ctx.recoveryFor` in the handler names, unless marked `thrownBy: 'service'`), `error-contract-recovery-unforwarded` (a `ctx.fail` site carrying neither `ctx.recoveryFor('<reason>')` nor its own `recovery` key, so the declared hint reaches neither client surface). See `api-linter` skill.
|
|
424
428
|
|
|
@@ -520,7 +524,7 @@ Detailed method signatures, options, and examples live in skill files. Read the
|
|
|
520
524
|
|
|
521
525
|
Each `framework-skills/<name>/SKILL.md` carries `metadata.version` in frontmatter. The `maintenance` skill's Phase A uses this to sync consumer copies — replaces the **entire skill directory** as one unit. Without a version bump, Phase A skips the skill (content-hash backstop catches drift, but noisier).
|
|
522
526
|
|
|
523
|
-
**Policy:** Bump `metadata.version` when changing any file under `framework-skills/<name>/` — SKILL.md is the single version knob for the directory. Typo/whitespace fixes exempt.
|
|
527
|
+
**Policy:** Bump `metadata.version` when changing any file under `framework-skills/<name>/` — SKILL.md is the single version knob for the directory. Typo/whitespace fixes exempt. **Exactly one step per release, however many edits land:** before bumping, compare the version against the last release tag (`git show $(git describe --tags --abbrev=0):framework-skills/<name>/SKILL.md`) and skip the bump when it has already moved. Enforced by `bun run devcheck` (`scripts/check-skill-versions.ts`), both directions as warnings: a SKILL.md body change vs `HEAD` without a `metadata.version` bump while the version still matches the last release tag, and a version more than one step (next minor, or next major at `.0`) past the last release tag. Whitespace-only edits never trigger the first, and a genuine typo fix opts out via `devcheck.config.json` `skillVersions.ignore`.
|
|
524
528
|
|
|
525
529
|
Skills live in `framework-skills/<name>/SKILL.md`; the full list is discoverable via the agent's skill registry at session start. The directory is deliberately not `skills/`: Claude Code and Codex auto-load a plugin's root `skills/`, and these are development-time skills, not skills for the agents that use a server. `skills/` stays free for that second kind.
|
|
526
530
|
|
|
@@ -540,10 +544,10 @@ Skills live in `framework-skills/<name>/SKILL.md`; the full list is discoverable
|
|
|
540
544
|
- **Auth:** via `auth: ['scope']` on definitions (not HOF wrapper)
|
|
541
545
|
- **Missing input:** read `ctx.inputs` first, then `return ctx.requestInput(...)`
|
|
542
546
|
- **Pagination:** large resource lists use `extractCursor`/`paginateArray`
|
|
543
|
-
- **Registration:** definitions
|
|
547
|
+
- **Registration:** definitions collected in the `definitions/index.ts` barrel's array passed to `createApp()` — an `export` line alone registers nothing
|
|
544
548
|
- **Tests:** `createMockContext()`, `.handler()` tested directly
|
|
545
549
|
- **Gate:** `bun run devcheck` passes (includes MCP definition linting)
|
|
546
|
-
- **Smoke-test:** `bun run rebuild && bun run start:stdio` (or `start:http`)
|
|
550
|
+
- **Smoke-test:** `bun run rebuild && bun run start:stdio < /dev/null` (or `start:http`); the `Core services constructed` log record lists every definition in its `tools` / `resources` / `prompts` fields
|
|
547
551
|
|
|
548
552
|
---
|
|
549
553
|
|
|
@@ -566,7 +570,7 @@ Skills live in `framework-skills/<name>/SKILL.md`; the full list is discoverable
|
|
|
566
570
|
| `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 |
|
|
567
571
|
| `bun run test:coverage` | Root projects with coverage thresholds enforced |
|
|
568
572
|
| `bun run test:integration` | Real server subprocesses over stdio and HTTP |
|
|
569
|
-
| `bun run test:worker` | The framework under real `workerd`, then a standalone Worker bundle through the Wrangler toolchain — real Node on both legs |
|
|
573
|
+
| `bun run test:worker` | The framework under real `workerd`, then a standalone Worker bundle through the Wrangler toolchain — real Node on both legs. The `workerd` leg enforces its own coverage thresholds over the Worker entry and Cloudflare storage providers, reported to `reports/coverage-worker/` |
|
|
570
574
|
| `bun run test:package` | Rebuilds, packs the tarball, and consumes it as an external project would (exports, declarations, both runtimes) |
|
|
571
575
|
| `bun run test:node` | Root projects + integration under real Node via `scripts/with-node.ts`, which bypasses Bun's `node` PATH shim |
|
|
572
576
|
| `bun run test:order` | Root projects on real Node in shuffled file order under a pinned seed — catches inter-file state leakage |
|
package/CLAUDE.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Package:** `@cyanheads/mcp-ts-core`
|
|
4
|
-
**Version:** 0.13.
|
|
4
|
+
**Version:** 0.13.9
|
|
5
5
|
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
6
|
-
**MCP SDK:** `@modelcontextprotocol/server` ^2.
|
|
6
|
+
**MCP SDK:** `@modelcontextprotocol/server` ^2.1.0 (protocol revisions 2026-07-28 and 2025-*)
|
|
7
7
|
**Zod:** ^4.6.5
|
|
8
8
|
**GitHub:** [cyanheads/mcp-ts-core](https://github.com/cyanheads/mcp-ts-core)
|
|
9
9
|
**npm:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
|
|
@@ -100,9 +100,8 @@ await createApp({
|
|
|
100
100
|
tools: allToolDefinitions,
|
|
101
101
|
resources: allResourceDefinitions,
|
|
102
102
|
prompts: allPromptDefinitions,
|
|
103
|
-
instructions: // server-level orientation, sent on every initialize
|
|
104
|
-
'
|
|
105
|
-
'Other endpoints reachable via `connect({ baseUrl })`.',
|
|
103
|
+
instructions: // server-level orientation, sent on every initialize — one literal, a few sentences
|
|
104
|
+
'Calls reach the production API by default. Pass `baseUrl` to `connect` to reach another endpoint.',
|
|
106
105
|
extensions: { // SEP-2133 extensions advertised in capabilities
|
|
107
106
|
'vendor/my-extension': { /* extension config */ },
|
|
108
107
|
},
|
|
@@ -168,7 +167,7 @@ interface ServerHandle {
|
|
|
168
167
|
}
|
|
169
168
|
```
|
|
170
169
|
|
|
171
|
-
**Exit contract.** `shutdown()` is exit-free and unbounded: it also serves the startup-failure rollback and direct calls from embedders and tests, so ending the process belongs to the handlers. `SIGTERM`, `SIGINT`, and stdin EOF each run that same shutdown and then exit explicitly. A signal exits 0 once the shutdown settles and 1 when the 10 s ceiling fires, after a warning naming the step that never settled; stdin EOF exits 0 either way, unchanged. The ceiling bounds the shutdown as a whole rather than any single await, so a step that settles inside it is never truncated. A second signal mid-shutdown reaches no handler (shutdown detaches them as it starts) and terminates on the OS default, 143 / 130 — the operator's force-kill escape hatch. `uncaughtException` / `unhandledRejection` exit 1.
|
|
170
|
+
**Exit contract.** `shutdown()` is exit-free and unbounded: it also serves the startup-failure rollback and direct calls from embedders and tests, so ending the process belongs to the handlers. `SIGTERM`, `SIGINT`, and stdin EOF each run that same shutdown and then exit explicitly. On stdin EOF the SDK transport has already closed itself, so a request still in flight is aborted (its `ctx.signal` fires) and never answered — the client has hung up. A signal exits 0 once the shutdown settles and 1 when the 10 s ceiling fires, after a warning naming the step that never settled; stdin EOF exits 0 either way, unchanged. The ceiling bounds the shutdown as a whole rather than any single await, so a step that settles inside it is never truncated. A second signal mid-shutdown reaches no handler (shutdown detaches them as it starts) and terminates on the OS default, 143 / 130 — the operator's force-kill escape hatch. `uncaughtException` / `unhandledRejection` exit 1.
|
|
172
171
|
|
|
173
172
|
---
|
|
174
173
|
|
|
@@ -238,7 +237,7 @@ export const myTool = tool('my_tool', {
|
|
|
238
237
|
});
|
|
239
238
|
```
|
|
240
239
|
|
|
241
|
-
**Steps:** Create `src/mcp-server/tools/definitions/[name].tool.ts` (kebab-case) → use `tool('snake_case', {...})` with Zod `.describe()` on all fields → implement `handler(input, ctx)` (pure, throws on failure) → add `auth`/`format` if needed → register in `definitions/index.ts` → `bun run devcheck` → smoke-test with `bun run rebuild && bun run start:stdio` (or `start:http`).
|
|
240
|
+
**Steps:** Create `src/mcp-server/tools/definitions/[name].tool.ts` (kebab-case) → use `tool('snake_case', {...})` with Zod `.describe()` on all fields → implement `handler(input, ctx)` (pure, throws on failure) → add `auth`/`format` if needed → register in `definitions/index.ts` → `bun run devcheck` → smoke-test with `bun run rebuild && bun run start:stdio < /dev/null` (or `start:http`) and confirm the `Core services constructed` log record lists the tool in its `tools` field (the message text shows only counts).
|
|
242
241
|
|
|
243
242
|
**Schema constraint:** Input/output schemas must use JSON-Schema-serializable Zod types only. The MCP SDK converts schemas to JSON Schema for `tools/list` — non-serializable types (`z.custom()`, `z.date()`, `z.transform()`, `z.bigint()`, `z.symbol()`, `z.void()`, `z.map()`, `z.set()`, `z.function()`, `z.nan()`) cause a hard runtime failure. Use structural equivalents instead (e.g., `z.string()` with `.describe('ISO 8601 date')` instead of `z.date()`). The `schema-serializable` lint rule catches this at build time (`bun run lint:mcp` / `devcheck`).
|
|
244
243
|
|
|
@@ -255,7 +254,7 @@ export const myTool = tool('my_tool', {
|
|
|
255
254
|
|
|
256
255
|
**Strict input:** `tool()` stores `input.strict()`, so an unrecognized argument key is rejected by name before the handler runs and `inputSchema` advertises `additionalProperties: false`. Root-level only — a nested `z.object()` still strips unless it is strict itself. An explicit `.passthrough()` / `.catchall()` is honored. Declare `.strict()` **before** `.describe()` / `.meta()` on the root: Zod keys both to the schema instance and `.strict()` clones without it, so a root describe declared after is discarded and never advertised — `lint:mcp` reports that as `schema-root-meta-discarded`.
|
|
257
256
|
|
|
258
|
-
**Pre-validation:** an ordered step inside `parseToolArguments` rescues calls strict input would otherwise reject — drop client-added root keys (`_meta`, `tool_call_description`, `toolCallId`, any undeclared `_`-prefixed key) → rewrite key aliases (declared `inputAliases`, plus any undeclared key whose case-folded form names exactly one declared key) → parse → on failure, repair a JSON-stringified array and re-parse once, keeping
|
|
257
|
+
**Pre-validation:** an ordered step inside `parseToolArguments` rescues calls strict input would otherwise reject — drop client-added root keys (`_meta`, `tool_call_description`, `toolCallId`, any undeclared `_`-prefixed key) → rewrite key aliases (declared `inputAliases`, plus any undeclared key whose case-folded form names exactly one declared key) → parse → on failure, repair a JSON-stringified array or object, or a safe integer where a string is expected (`8654467` → `"8654467"`), and re-parse once, keeping the repair only if the schema then accepts it. If that still fails and the drop discarded a key, the stages rerun alias-first so `_query` or a declared `_q` alias reaches its target, and the retry (repair included) is kept only if it validates — a call the first order validates resolves exactly as it would without the retry. All on by default, and nothing changes the advertised `inputSchema`. A call that still fails throws the rejection of the last order tried — the retry's when it ran, so a declared `_q` alias with a bad value reports that value's failure and `Validated _q as query.` — the same one it gets under `coerce: false`, reporting that order's rewrites and underscore-rule drops as `data.input` and in the hint. A declared key, an author-opened root, and a `headerParam` target are never touched. Server-level switches: `createApp({ input: { ignoreKeys, caseStyleAliases, coerce } })`. Counters, for the attempt the handler receives: `mcp.input.ignored_key`, `mcp.input.aliased`, `mcp.input.coerced` (once per repair kind). Lint: `input-alias-conflict`. See `add-tool` skill.
|
|
259
258
|
|
|
260
259
|
**Header-mirrored input (2026-07-28):** `headerParam(z.string(), 'Region')` designates an input property with `x-mcp-header`, so its value also rides an `Mcp-Param-Region` request header and an intermediary can read it without parsing the body. Mirroring, not relocation — the handler still reads the argument from the body, and nothing else about the field changes. Only a primitive-typed (`string`/`integer`/`number`/`boolean`) property statically reachable through a chain of `properties` keys qualifies: an array element, a `z.record()` value, and every field of a discriminated-union input root are unreachable, and header names must be RFC 9110 tokens, case-insensitively unique per schema. `tool()` rejects a violation at definition time naming the field path — the SDK only warns, then conforming Streamable HTTP clients drop the tool. Lint rule: `header-param-designation`.
|
|
261
260
|
|
|
@@ -303,7 +302,7 @@ interface Context {
|
|
|
303
302
|
readonly auth?: AuthContext;
|
|
304
303
|
readonly log: ContextLogger; // auto-correlated: requestId, traceId, tenantId
|
|
305
304
|
readonly state: ContextState; // tenant-scoped KV storage
|
|
306
|
-
readonly requestInput: RequestInputFn; // (spec) => never — suspends and asks the caller for input
|
|
305
|
+
readonly requestInput: RequestInputFn; // (spec, options?) => never — suspends and asks the caller for input
|
|
307
306
|
readonly inputs: ContextInputs; // reader over a retried request's responses
|
|
308
307
|
readonly notifyPromptListChanged?: (() => void) | undefined; // prompt list changed
|
|
309
308
|
readonly notifyResourceListChanged?: (() => void) | undefined; // resource list changed
|
|
@@ -368,6 +367,11 @@ useFormat(answer.format);
|
|
|
368
367
|
cancelled prompt is terminal, not a round to retry. `inputRequired.elicitUrl({ message, url })`
|
|
369
368
|
hands the user an external link instead of a form.
|
|
370
369
|
|
|
370
|
+
A 2025-era client that declared no matching capability is refused as `client_capability_missing`,
|
|
371
|
+
with a hint that ends at reconnecting. When the tool's own arguments can stand in for the answer,
|
|
372
|
+
say so per call — `ctx.requestInput(spec, { fallbackHint: 'Or call again with noun supplied.' })` —
|
|
373
|
+
and the sentence is appended to that hint. A consent gate passes none: it has no such field.
|
|
374
|
+
|
|
371
375
|
### `ctx.content`
|
|
372
376
|
|
|
373
377
|
Accumulates non-text content blocks — image/audio bytes, embedded resources, resource links — onto the response: `ctx.content.image(data, mimeType)`, `ctx.content.audio(data, mimeType)`, or `ctx.content(block)` for a raw `ContentBlock`. Blocks are prepended to `content[]` after `format()` runs and never enter `structuredContent`, so a handler can emit media for the calling model without the base64 duplicating into typed output. Always present (no-op when unused); callable from handler and service layer.
|
|
@@ -416,9 +420,9 @@ Available factories: `invalidParams`, `invalidRequest`, `notFound`, `forbidden`,
|
|
|
416
420
|
|
|
417
421
|
For HTTP responses from upstream APIs, use `httpErrorFromResponse(response, { service, data })` from `/utils` — maps the full status table (401/403/408/422/429/5xx) and captures body + `Retry-After`.
|
|
418
422
|
|
|
419
|
-
**Auto-classification.** Plain `Error`, `ZodError`, and any other thrown value are caught and classified automatically. Resolution order: request signal already aborted (→ `RequestCancelled`, outranking the thrown value's own code, `McpError` included) → `McpError` code (preserved as-is) → SDK `ConnectionClosed` (→ `RequestCancelled`) → JS constructor name (`
|
|
423
|
+
**Auto-classification.** Plain `Error`, `ZodError`, and any other thrown value are caught and classified automatically. Resolution order: request signal already aborted (→ `RequestCancelled`, outranking the thrown value's own code, `McpError` included) → `McpError` code (preserved as-is) → SDK `ConnectionClosed` (→ `RequestCancelled`) → engine resource-limit `RangeError` by whole message — stack overflow, maximum string size (→ `InternalError`) → JS constructor name (`SyntaxError` → `ValidationError`; `TypeError` is excluded) → provider patterns (HTTP status codes, AWS errors, DB errors) → common message patterns → `AbortError` name (→ `Timeout`) → `InternalError` fallback. A result that breaks the definition's own `output` or `enrichment` schema fails as `InternalError` naming that contract, not `ValidationError`.
|
|
420
424
|
|
|
421
|
-
**Error-path parity.** Tool errors: `content[]` carries `Error: <message>`, then `Recovery: <hint>` when the hint says something the message does not already contain, then a closing `(reason … · not retryable)` for whichever of `data.reason` / `data.retryable` is present; the numeric code and `data.issues` stay JSON-only. `structuredContent.error` carries `{ code, message, data? }`. No `_meta.error`. Resources re-throw via JSON-RPC error envelope. An argument rejection is one of them: `-32602` with `data.issues`, plus `data.reason: 'invalid_arguments'` and a hint synthesized from the issues and the root schema — never a tool-declared `reason`, since the handler never ran. `client_capability_missing` is the second framework-owned reason: a `ctx.requestInput` return a 2025-era connection cannot serve is refused before any wire traffic as `-32600` carrying that reason and a hint naming the capability.
|
|
425
|
+
**Error-path parity.** Tool errors: `content[]` carries `Error: <message>`, then `Recovery: <hint>` when the hint says something the message does not already contain, then a closing `(reason … · not retryable)` for whichever of `data.reason` / `data.retryable` is present; the numeric code and `data.issues` stay JSON-only. `structuredContent.error` carries `{ code, message, data? }`. No `_meta.error`. Resources re-throw via JSON-RPC error envelope. An argument rejection is one of them: `-32602` with `data.issues`, plus `data.reason: 'invalid_arguments'` and a hint synthesized from the issues and the root schema — never a tool-declared `reason`, since the handler never ran. When pre-validation rewrote or dropped a key the caller wrote, `data.input` (`{ aliased: [{ alias, target }], ignored }`) names it and the hint closes with `Validated … as ….` / `Dropped undeclared key ….`; an ignore-list drop is never reported. `client_capability_missing` is the second framework-owned reason: a `ctx.requestInput` return a 2025-era connection cannot serve is refused before any wire traffic as `-32600` carrying that reason and a hint naming the capability.
|
|
422
426
|
|
|
423
427
|
**Lint rules** (all warnings, surfaced in `devcheck`): `prefer-mcp-error-in-handler`, `prefer-error-factory`, `preserve-cause-on-rethrow`, `no-stringify-upstream-error`, `error-contract-conformance`, `error-contract-prefer-fail`, `error-contract-unthrown` (a declared reason no literal `ctx.fail`/`ctx.recoveryFor` in the handler names, unless marked `thrownBy: 'service'`), `error-contract-recovery-unforwarded` (a `ctx.fail` site carrying neither `ctx.recoveryFor('<reason>')` nor its own `recovery` key, so the declared hint reaches neither client surface). See `api-linter` skill.
|
|
424
428
|
|
|
@@ -520,7 +524,7 @@ Detailed method signatures, options, and examples live in skill files. Read the
|
|
|
520
524
|
|
|
521
525
|
Each `framework-skills/<name>/SKILL.md` carries `metadata.version` in frontmatter. The `maintenance` skill's Phase A uses this to sync consumer copies — replaces the **entire skill directory** as one unit. Without a version bump, Phase A skips the skill (content-hash backstop catches drift, but noisier).
|
|
522
526
|
|
|
523
|
-
**Policy:** Bump `metadata.version` when changing any file under `framework-skills/<name>/` — SKILL.md is the single version knob for the directory. Typo/whitespace fixes exempt.
|
|
527
|
+
**Policy:** Bump `metadata.version` when changing any file under `framework-skills/<name>/` — SKILL.md is the single version knob for the directory. Typo/whitespace fixes exempt. **Exactly one step per release, however many edits land:** before bumping, compare the version against the last release tag (`git show $(git describe --tags --abbrev=0):framework-skills/<name>/SKILL.md`) and skip the bump when it has already moved. Enforced by `bun run devcheck` (`scripts/check-skill-versions.ts`), both directions as warnings: a SKILL.md body change vs `HEAD` without a `metadata.version` bump while the version still matches the last release tag, and a version more than one step (next minor, or next major at `.0`) past the last release tag. Whitespace-only edits never trigger the first, and a genuine typo fix opts out via `devcheck.config.json` `skillVersions.ignore`.
|
|
524
528
|
|
|
525
529
|
Skills live in `framework-skills/<name>/SKILL.md`; the full list is discoverable via the agent's skill registry at session start. The directory is deliberately not `skills/`: Claude Code and Codex auto-load a plugin's root `skills/`, and these are development-time skills, not skills for the agents that use a server. `skills/` stays free for that second kind.
|
|
526
530
|
|
|
@@ -540,10 +544,10 @@ Skills live in `framework-skills/<name>/SKILL.md`; the full list is discoverable
|
|
|
540
544
|
- **Auth:** via `auth: ['scope']` on definitions (not HOF wrapper)
|
|
541
545
|
- **Missing input:** read `ctx.inputs` first, then `return ctx.requestInput(...)`
|
|
542
546
|
- **Pagination:** large resource lists use `extractCursor`/`paginateArray`
|
|
543
|
-
- **Registration:** definitions
|
|
547
|
+
- **Registration:** definitions collected in the `definitions/index.ts` barrel's array passed to `createApp()` — an `export` line alone registers nothing
|
|
544
548
|
- **Tests:** `createMockContext()`, `.handler()` tested directly
|
|
545
549
|
- **Gate:** `bun run devcheck` passes (includes MCP definition linting)
|
|
546
|
-
- **Smoke-test:** `bun run rebuild && bun run start:stdio` (or `start:http`)
|
|
550
|
+
- **Smoke-test:** `bun run rebuild && bun run start:stdio < /dev/null` (or `start:http`); the `Core services constructed` log record lists every definition in its `tools` / `resources` / `prompts` fields
|
|
547
551
|
|
|
548
552
|
---
|
|
549
553
|
|
|
@@ -566,7 +570,7 @@ Skills live in `framework-skills/<name>/SKILL.md`; the full list is discoverable
|
|
|
566
570
|
| `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 |
|
|
567
571
|
| `bun run test:coverage` | Root projects with coverage thresholds enforced |
|
|
568
572
|
| `bun run test:integration` | Real server subprocesses over stdio and HTTP |
|
|
569
|
-
| `bun run test:worker` | The framework under real `workerd`, then a standalone Worker bundle through the Wrangler toolchain — real Node on both legs |
|
|
573
|
+
| `bun run test:worker` | The framework under real `workerd`, then a standalone Worker bundle through the Wrangler toolchain — real Node on both legs. The `workerd` leg enforces its own coverage thresholds over the Worker entry and Cloudflare storage providers, reported to `reports/coverage-worker/` |
|
|
570
574
|
| `bun run test:package` | Rebuilds, packs the tarball, and consumes it as an external project would (exports, declarations, both runtimes) |
|
|
571
575
|
| `bun run test:node` | Root projects + integration under real Node via `scripts/with-node.ts`, which bypasses Bun's `node` PATH shim |
|
|
572
576
|
| `bun run test:order` | Root projects on real Node in shuffled file order under a pinned seed — catches inter-file state leakage |
|
package/README.md
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
<div align="center">
|
|
8
8
|
|
|
9
|
-
[](./CHANGELOG.md) [](./LICENSE) [](https://modelcontextprotocol.io/specification/2026-07-28)
|
|
10
10
|
|
|
11
|
-
[](https://modelcontextprotocol.io/) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
12
12
|
|
|
13
13
|
[Quick start](#quick-start) · [Capabilities](#what-comes-with-it) · [API reference](#api-overview) · [Examples](#examples)
|
|
14
14
|
|
|
@@ -239,6 +239,8 @@ Core config comes from environment variables, validated with Zod. Server-specifi
|
|
|
239
239
|
| `STORAGE_PROVIDER_TYPE` | `in-memory`, `filesystem`, `supabase`, `cloudflare-d1`/`kv`/`r2` | `in-memory` |
|
|
240
240
|
| `CANVAS_PROVIDER_TYPE` | `none` or `duckdb` (optional peer dependency `@duckdb/node-api`) | `none` |
|
|
241
241
|
| `OTEL_ENABLED` | Enable OpenTelemetry | `false` |
|
|
242
|
+
| `LOG_TOOL_FAILURE_PAYLOADS` | Log each failed tool call's arguments and result, redacted by key name (a secret inside a free-form value is not caught) | `false` |
|
|
243
|
+
| `LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES` | Cap per logged payload, in UTF-8 bytes | `16384` |
|
|
242
244
|
| `OPENROUTER_API_KEY` | API key for the optional OpenRouter LLM provider (`/services`) | — |
|
|
243
245
|
|
|
244
246
|
See [CLAUDE.md/AGENTS.md](CLAUDE.md) for the full configuration reference.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Request context and elicited answers no longer reach client error data, error counters and wire codes agree per failure, and OTLP log export and failed-call payload logging arrive as opt-ins."
|
|
3
|
+
breaking: true
|
|
4
|
+
security: true
|
|
5
|
+
agent-notes: |
|
|
6
|
+
Adoption steps for a consumer upgrading from 0.13.7.
|
|
7
|
+
|
|
8
|
+
1. Error codes callers can see change in three places; update tests and
|
|
9
|
+
any client logic keyed on them. A result that breaks a tool's `output`,
|
|
10
|
+
its `enrichment` block, or a resource's `output` now fails as `-32603`
|
|
11
|
+
(was `-32007`) with no `data.issues`. An engine resource-limit
|
|
12
|
+
`RangeError` (stack overflow, maximum string size) is `-32603` (was
|
|
13
|
+
`-32007`). A `fetchWithTimeout` abort whose signal reason is a
|
|
14
|
+
`TimeoutError` is `-32004` `Timeout` (was `-32011`).
|
|
15
|
+
2. `McpError.data` from `ErrorHandler.handleError` / `tryCatch` no longer
|
|
16
|
+
carries the `context` fields (`requestId`, `tenantId`, `operation`,
|
|
17
|
+
`extra`, …), and framework storage, parser, formatter, fetch, and
|
|
18
|
+
provider errors no longer carry a context either. Put anything the
|
|
19
|
+
caller should act on in the thrown `McpError`'s own `data`; code or
|
|
20
|
+
tests that read `requestId` or an `extra` field off error data read the
|
|
21
|
+
log record instead.
|
|
22
|
+
3. `selectSections` now throws `InvalidParams` on a requested name that is
|
|
23
|
+
not a key of the document. Catch it only if the tool must degrade; the
|
|
24
|
+
default path returns the valid names to the caller.
|
|
25
|
+
4. `InMemoryProvider.set` / `list` reject instead of throwing
|
|
26
|
+
synchronously. Tests written as `expect(() => provider.set(…)).toThrow()`
|
|
27
|
+
become `await expect(provider.set(…)).rejects…`.
|
|
28
|
+
5. `prefer-mcp-error-in-handler` now fires under Bun and on a bare
|
|
29
|
+
`throw Error(…)`. Expect new warnings on the next `devcheck`; replace
|
|
30
|
+
each with an error factory or `ctx.fail`.
|
|
31
|
+
6. A server with `manifest.json` adds `"!dist/*.mcpb"` to `package.json`
|
|
32
|
+
`files`. `lint:packaging` fails without it once the framework scripts
|
|
33
|
+
are resynced.
|
|
34
|
+
7. Port the scaffold `Dockerfile` production-stage changes: copy
|
|
35
|
+
`bunfig.toml` with `package.json` and `bun.lock`, seed
|
|
36
|
+
`@socketsecurity/bun-security-scanner` from the build stage, replace the
|
|
37
|
+
OTEL `bun add` with the step that reads each package's range from the
|
|
38
|
+
installed framework's `peerDependencies` (the list now includes the
|
|
39
|
+
three log packages), and drop `ENV MCP_FORCE_CONSOLE_LOGGING`.
|
|
40
|
+
8. Optional: `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` turns on OTLP log export
|
|
41
|
+
and needs `@opentelemetry/sdk-logs`, `@opentelemetry/exporter-logs-otlp-http`,
|
|
42
|
+
and `@opentelemetry/api-logs`. `LOG_TOOL_FAILURE_PAYLOADS` /
|
|
43
|
+
`LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES` add the failed-call payload record.
|
|
44
|
+
Document whichever the server exposes in `.env.example` and the README.
|
|
45
|
+
9. Dashboards: `mcp.tool.calls` / `mcp.tool.errors` gain
|
|
46
|
+
`mcp.tool.outcome`, `mcp.errors.classified` gains `mcp.error.category`,
|
|
47
|
+
and `mcp.tool.rejections` is new. Existing `sum()` queries are
|
|
48
|
+
unchanged; group `mcp.errors.classified` by `mcp.error.category` rather
|
|
49
|
+
than decoding the code. New `data.reason` values are listed under
|
|
50
|
+
Changed.
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
# 0.13.8 — 2026-09-26
|
|
54
|
+
|
|
55
|
+
## Added
|
|
56
|
+
|
|
57
|
+
- **OTLP log export** ([#547](https://github.com/cyanheads/mcp-ts-core/issues/547)) — `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` attaches a `BatchLogRecordProcessor`, and the framework logger forwards each record that passes the level filter and rate limit, redacted, with its MCP level as the severity. The base `OTEL_EXPORTER_OTLP_ENDPOINT` never enables it; `interactions.log` is never exported.
|
|
58
|
+
- **Failed-call payload logging** ([#291](https://github.com/cyanheads/mcp-ts-core/issues/291)) — `LOG_TOOL_FAILURE_PAYLOADS=true` writes one `Tool failure payload: <tool>` record per failed call with `toolInput` and `toolResult` as redacted JSON strings, capped by `LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES` (default `16384`). Cancellations and `input_required` write nothing; redaction is by key name only. Backed by the new `sanitization.serializeForLogging(value, maxBytes)`.
|
|
59
|
+
- **`mcp.tool.rejections` counter and `mcp.tool.outcome` attribute** ([#546](https://github.com/cyanheads/mcp-ts-core/issues/546)) — a call refused by argument validation or the inline `auth` check counts once on `mcp.tool.rejections` (`mcp.tool.error_code`, `mcp.tool.error_category`); `mcp.tool.calls` and `mcp.tool.errors` carry `ok` / `error` / `cancelled`.
|
|
60
|
+
- **`mcp.error.category` on `mcp.errors.classified`** ([#481](https://github.com/cyanheads/mcp-ts-core/issues/481)) — the same `upstream` / `server` / `client` bucket as the per-surface counters, so the canvas tenant-cap refusal reads `-32003` + `server`.
|
|
61
|
+
- **`lint:packaging` check 13** ([#469](https://github.com/cyanheads/mcp-ts-core/issues/469)) — with `manifest.json` present, a `files` allowlist covering `dist/` must carry `"!dist/*.mcpb"`. The scaffold's `package.json` now does.
|
|
62
|
+
- **Skill-version overshoot check** — in the framework repo, `devcheck` warns when a skill's `metadata.version` is more than one step past its version at the last release tag, and a skill already bumped since that tag takes further body edits without another bump.
|
|
63
|
+
|
|
64
|
+
## Changed
|
|
65
|
+
|
|
66
|
+
- **Output-contract failures are `InternalError`, and error counters follow the wire code** ([#480](https://github.com/cyanheads/mcp-ts-core/issues/480)) — a tool `output`, `enrichment`, or resource `output` violation returns `-32603` naming the definition and contract, with no `data`, in production and `runToolContract` alike. A non-`McpError` from a tool or prompt handler is bucketed by the code it classifies to, so `Error('Request timed out')` is `upstream` on every counter.
|
|
67
|
+
- **Engine resource-limit `RangeError`s are `InternalError`** ([#482](https://github.com/cyanheads/mcp-ts-core/issues/482)) — a whole message of `Maximum call stack size exceeded` (with or without the period), `Invalid string length`, or `Out of memory` maps to `-32603`; other `RangeError`s stay `ValidationError`.
|
|
68
|
+
- **`selectSections` rejects unknown names** ([#477](https://github.com/cyanheads/mcp-ts-core/issues/477)) — throws `InvalidParams` naming the unmatched and available keys, with both in `data` (`unmatched`, `available`). An absent `alwaysKeep` key is still ignored.
|
|
69
|
+
- **`InMemoryProvider.set` and `list` reject** ([#544](https://github.com/cyanheads/mcp-ts-core/issues/544)) — the encode, capacity, and cursor failures return a rejected promise instead of throwing synchronously.
|
|
70
|
+
- **`prefer-mcp-error-in-handler` reads Bun's handler source** ([#465](https://github.com/cyanheads/mcp-ts-core/issues/465)) — `throw Error(…)` counts as a plain-`Error` throw, so the rule now fires under Bun and on a bare `throw Error(…)`. `preserve-cause-on-rethrow` accepts `{ cause }` when the catch binding is `cause`.
|
|
71
|
+
- **Stable `data.reason` on framework validation errors** ([#552](https://github.com/cyanheads/mcp-ts-core/issues/552), [#504](https://github.com/cyanheads/mcp-ts-core/issues/504)) — with a `recovery.hint` where the caller can change the input: `sanitization` (`invalid_url`, `invalid_path`, `path_traversal`, `absolute_path_disallowed`, `invalid_json`, `json_too_large`, `invalid_number`, `unsupported_sanitize_context`), `fetchWithTimeout` (`invalid_url`, `private_address_blocked`, `too_many_redirects`), `IdGenerator` (`invalid_charset`, `unknown_entity_type`, `invalid_id_format`), `decodeCursor` / `paginateArray` (`invalid_cursor`), and the parsers (`parser_input_empty`, `csv_parse_failed`).
|
|
72
|
+
- **The startup line separates disabled tools** ([#514](https://github.com/cyanheads/mcp-ts-core/issues/514)) — `N tool(s) (+M disabled: …)`, with a `disabledTools` array beside `tools`; `manifest.definitionCounts` still counts every definition.
|
|
73
|
+
- **Docker production stage** ([#549](https://github.com/cyanheads/mcp-ts-core/issues/549)) — the OTEL step installs each package at its `peerDependencies` range (see Security), and the framework image installs them instead of skipping its own peers. `MCP_FORCE_CONSOLE_LOGGING`, which nothing read, is gone from both Dockerfiles.
|
|
74
|
+
- **`git-wrapup` orders `chore(deps)` first** ([#485](https://github.com/cyanheads/mcp-ts-core/issues/485)) — a dependency bump lands before the commits that use it and builds on its own, and every work commit must pass its tests at its own SHA, checked from a `git archive` snapshot. `maintenance` notes that a Bun pin moves `packageManager`, the Docker base tags, and the README badge together.
|
|
75
|
+
- Skill versions: `api-auth` 1.3 → 1.4, `api-canvas` 2.4 → 2.5, `api-config` 1.20 → 1.21, `api-context` 2.6 → 2.7, `api-errors` 1.16 → 1.17, `api-linter` 1.18 → 1.19, `api-telemetry` 1.13 → 1.14, `api-testing` 1.11 → 1.12, `api-utils` 2.12 → 2.13, `git-wrapup` 1.25 → 1.26, `maintenance` 2.9 → 2.10, `polish-docs-meta` 2.18 → 2.19, `release-and-publish` 2.20 → 2.21, `techniques` 0.3 → 0.4.
|
|
76
|
+
|
|
77
|
+
## Fixed
|
|
78
|
+
|
|
79
|
+
- **A caller-side deadline is `Timeout`** ([#551](https://github.com/cyanheads/mcp-ts-core/issues/551)) — `fetchWithTimeout` reports an external-signal abort whose reason is a `TimeoutError` as `-32004` with `data.errorSource: 'FetchSignalTimeout'`; any other abort stays `RequestCancelled`. `defaultIsTransient` keeps the deadline out of `withRetry`, as it kept the `RequestCancelled` out: every retry would reuse the fired signal.
|
|
80
|
+
- **Canvas `missing_table` means a missing table** ([#484](https://github.com/cyanheads/mcp-ts-core/issues/484)) — an unknown function, table function, type, collation, or schema is `invalid_sql` with `data.binderMessage`; `tableName` carries the full name, spaces included, and a `FROM`-first query counts as a `SELECT`. A `DROP`, `DELETE`, `INSERT`, `UPDATE`, or `ALTER` naming a missing table is `non_select_statement` (`-32007`), not `missing_table`, in `query()` and `registerView()` alike.
|
|
81
|
+
- **`describe-on-fields` survives a self-referential schema** ([#491](https://github.com/cyanheads/mcp-ts-core/issues/491)) — the walk stops when a schema re-enters its own path, so the rest of the lint report survives.
|
|
82
|
+
- **`OUTLINE_VARIANT`'s documented shape lints clean** ([#271](https://github.com/cyanheads/mcp-ts-core/issues/271)) — `enrichment-prefer-block` skips a `notice` beside a `sections` array, and the JSDoc and `techniques` snippet describe `kind` and the section item.
|
|
83
|
+
- **`MarkdownBuilder.inlineCode` sizes its code span** ([#505](https://github.com/cyanheads/mcp-ts-core/issues/505)) — the delimiter outgrows any backtick run in the value, so it reads back as one span; `formatOutline` renders section names the same way.
|
|
84
|
+
- **`ctx.log` keeps its line on the wire** ([#502](https://github.com/cyanheads/mcp-ts-core/issues/502)) — `message` and `error` are written after the call's data, so a `message` key no longer replaces the log line in `notifications/message`.
|
|
85
|
+
- **Canonical context fields win in log records** ([#550](https://github.com/cyanheads/mcp-ts-core/issues/550)) — an `extra` key named `requestId`, `traceId`, `spanId`, `sessionId`, `tenantId`, `operation`, or `timestamp` no longer replaces the context's own value.
|
|
86
|
+
- **An unwritable logs directory no longer kills the process** ([#497](https://github.com/cyanheads/mcp-ts-core/issues/497)) — each file sink is probed at startup; one that cannot be opened is dropped with a single `warning` naming it, and stderr keeps logging.
|
|
87
|
+
- **`MCP_LOG_LEVEL=debug` reaches stderr** ([#511](https://github.com/cyanheads/mcp-ts-core/issues/511)) — the stderr, stdout, pretty, and `combined.log` targets take `level: 'debug'`, so the logger's level is the only gate, after `setLevel()` too; `error.log` keeps `error`.
|
|
88
|
+
- **One OTel diag logger, on stderr** ([#545](https://github.com/cyanheads/mcp-ts-core/issues/545)) — `NodeSDK` no longer registers a second one from `OTEL_LOG_LEVEL`, so the overwrite notices and the `Unknown log level` warning on aliases are gone and stdio's stdout carries only JSON-RPC. The metrics reader's export timeout matches its 15 s interval, ending the clamp notice.
|
|
89
|
+
- **`createFetchMock` static routes settle on Node** ([#503](https://github.com/cyanheads/mcp-ts-core/issues/503)) — the body is read once and served as a fresh `Response` per call instead of a `clone()` tee, so a cancelled body resolves.
|
|
90
|
+
- **`runToolContract` settles cancellation like production** ([#513](https://github.com/cyanheads/mcp-ts-core/issues/513)) — once `context.signal` has fired, anything the handler or success pipeline throws becomes `RequestCancelled`; argument parsing stays outside the settle.
|
|
91
|
+
- **The pre-commit hook no longer rewrites files** ([#543](https://github.com/cyanheads/mcp-ts-core/issues/543)) — `.husky/pre-commit` passes `--no-fix` instead of the removed `--no-format`, so Biome checks without writing.
|
|
92
|
+
|
|
93
|
+
## Security
|
|
94
|
+
|
|
95
|
+
- **Request context no longer reaches client error data** ([#548](https://github.com/cyanheads/mcp-ts-core/issues/548)) — `ErrorHandler.handleError` and every framework `McpError` site (storage validation and providers, parsers, formatters, `fetchWithTimeout`, speech and LLM providers, auth, sessions, canvas) pass explicit fields only, so `ctx.inputs.responses` and request metadata stay in the log. Formatter failures no longer put a stack in `data`, and the no-auth-context `-32006` carries no scope names.
|
|
96
|
+
- **The Docker OTEL install passes the supply-chain guards** ([#475](https://github.com/cyanheads/mcp-ts-core/issues/475)) — both Dockerfiles copy `bunfig.toml` into the production stage and seed the Socket scanner from the build stage, so the OTEL packages resolve through the release-age gate and the scanner, inside the framework's tested peer ranges.
|
|
97
|
+
|
|
98
|
+
## Dependencies
|
|
99
|
+
|
|
100
|
+
- New optional peers and devDependencies for OTLP log export: `@opentelemetry/sdk-logs` ^0.222.0, `@opentelemetry/exporter-logs-otlp-http` ^0.222.0, `@opentelemetry/api-logs` ^0.222.0.
|
|
101
|
+
- Dev: `openai` ^7.21.0 → ^7.23.0, `@supabase/supabase-js` ^2.117.0 → ^2.117.1.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Canvas scratch files move into a private directory, a JSON-stringified object or an integer sent for a string is repaired before the schema rejects it, and argument rejections name the field paths and key rewrites behind them."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: true
|
|
5
|
+
agent-notes: |
|
|
6
|
+
Adoption steps for a consumer upgrading from 0.13.8.
|
|
7
|
+
|
|
8
|
+
1. `lint:mcp` (and so `devcheck`) now fails on three conditions it used to
|
|
9
|
+
pass: a definition file that fails to import (`definition-import-failed`),
|
|
10
|
+
a `server.json` that exists but does not parse (`server-json-parse`), and
|
|
11
|
+
an `inputAliases` entry whose target is a `headerParam` field
|
|
12
|
+
(`input-alias-conflict`). The first two arrive with the maintenance
|
|
13
|
+
resync of `scripts/lint-mcp.ts`, the third with the package itself. Run
|
|
14
|
+
`bun run lint:mcp` once both are in: a server that passes needs no
|
|
15
|
+
change. Otherwise, for an import failure install the missing optional
|
|
16
|
+
peer where lint runs (a devDependency is enough) or make the import lazy
|
|
17
|
+
(`await import()` inside the handler or service, with module-load work
|
|
18
|
+
moved into `setup()` or a service's init); fix the `server.json` syntax;
|
|
19
|
+
and remove the dead alias, or drop the header designation.
|
|
20
|
+
2. A `runToolContract` / `toolContractSuite` case that expects
|
|
21
|
+
`InvalidParams` for an integer sent to a string field, or for a
|
|
22
|
+
JSON-stringified object sent to an object field, now succeeds: the
|
|
23
|
+
framework repairs both before the handler runs. Change such a case to
|
|
24
|
+
send a boolean so it keeps its wrong-type intent.
|
|
25
|
+
3. `CANVAS_TEMP_PATH` now names a parent directory. The DuckDB provider
|
|
26
|
+
creates a private `mcp-canvas-XXXXXX` directory inside it (or inside the
|
|
27
|
+
OS temp directory when unset), so a value pointing at a dedicated mount
|
|
28
|
+
keeps working. A leftover `<tmpdir>/mcp-canvas` from earlier versions is
|
|
29
|
+
never used again and can be deleted once no older server shares the
|
|
30
|
+
host. A process that exits abnormally leaves its `mcp-canvas-XXXXXX`
|
|
31
|
+
behind, still private; nothing sweeps it on the next start.
|
|
32
|
+
4. A tool whose handler has a non-elicitation path, where its own
|
|
33
|
+
arguments can stand in for what `ctx.requestInput` asks, passes one:
|
|
34
|
+
`ctx.requestInput(spec, { fallbackHint: 'Or call again with <field> supplied.' })`.
|
|
35
|
+
Without it, a 2025-era client lacking the capability is told only to
|
|
36
|
+
reconnect. A consent gate passes none.
|
|
37
|
+
5. MCP SDK 2.1.0 changes what callers see on the transports. A 2026-07-28
|
|
38
|
+
POST without `MCP-Protocol-Version` is refused `400` / `-32020`, as is a
|
|
39
|
+
2026-07-28 `tasks/get`, `tasks/update`, or `tasks/cancel` without an
|
|
40
|
+
`Mcp-Name` matching `params.taskId`. A JSON-RPC batch of more than 100
|
|
41
|
+
messages is refused whole, `400` / `-32600`. `notifications/cancelled`
|
|
42
|
+
with `requestId: 0` now aborts the handler. On stdio, stdin EOF aborts
|
|
43
|
+
requests still in flight, and they are never answered.
|
|
44
|
+
`MCP_HTTP_MAX_BODY_BYTES` remains the only request-body limit. Update
|
|
45
|
+
any client code or test that relied on the old answers.
|
|
46
|
+
6. The pre-validation repair's debug log field `coercion` is now
|
|
47
|
+
`coercions`, an array of repair kinds (`stringified_array`,
|
|
48
|
+
`stringified_object`, `integer_as_string`). Update any log query that
|
|
49
|
+
reads it.
|
|
50
|
+
7. A server with a `.mcpbignore` adds `/docs/idea.md` and `/logs/` to it,
|
|
51
|
+
as the scaffold now does.
|
|
52
|
+
8. `lint:packaging` (and so `devcheck`) now fails two conditions it used to
|
|
53
|
+
pass: a `manifest.json` whose `version` differs from `package.json`'s —
|
|
54
|
+
set it to the package version — and a Dockerfile stage that runs
|
|
55
|
+
`bun run build` without `FROM --platform=$BUILDPLATFORM` — start that
|
|
56
|
+
stage with `FROM --platform=$BUILDPLATFORM` and copy `dist/` into a
|
|
57
|
+
separate runtime stage, as the scaffold's `Dockerfile` does. Both arrive
|
|
58
|
+
with the maintenance resync of `scripts/lint-packaging.ts`. Run
|
|
59
|
+
`bun run lint:packaging` once it is in: a server that passes needs no
|
|
60
|
+
change.
|
|
61
|
+
9. `bun run release:github`, once `scripts/release-github.ts` is resynced,
|
|
62
|
+
refuses a tag that breaks the `release-and-publish` step 4 format before
|
|
63
|
+
any `gh` call. Run `bun run release:github -- --check` after
|
|
64
|
+
`git tag -a` and before pushing the tag, and recreate a tag it rejects.
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
# 0.13.9 — 2026-09-26
|
|
68
|
+
|
|
69
|
+
## Added
|
|
70
|
+
|
|
71
|
+
- **Stringified objects and integers sent for strings are repaired** ([#479](https://github.com/cyanheads/mcp-ts-core/issues/479), [#487](https://github.com/cyanheads/mcp-ts-core/issues/487)) — after a failed parse, a JSON-stringified object is decoded and a safe integer sent for a string becomes its digits (`8654467` → `"8654467"`), each kept only if the re-parse passes. `mcp.input.coercion` gains `stringified_object` and `integer_as_string`.
|
|
72
|
+
- **Argument rejections name pre-validation's rewrites** ([#468](https://github.com/cyanheads/mcp-ts-core/issues/468)) — when an alias rewrite or an underscore-rule drop touched a rejected call's keys, `data.input` names them (`{ aliased: [{ alias, target }], ignored }`) and the hint closes with `Validated q as query.` or `Dropped undeclared key _search.` (ignore-list drops never appear).
|
|
73
|
+
- **`pacer.cooldown`** ([#467](https://github.com/cyanheads/mcp-ts-core/issues/467)) — samples the shared gate as `PacerCooldownState` `{ remainingMs, consecutive }`, exported from `/utils`, so a task's rejection handler can report the wait on its own error. Both stay 0 without a `cooldown` option.
|
|
74
|
+
- **`lint:packaging` checks the manifest version and the Dockerfile build platform** — a `manifest.json` `version` other than `package.json`'s fails, as does a Dockerfile stage that runs `bun run build` without `FROM --platform=$BUILDPLATFORM`. `devcheck` runs the step when a `Dockerfile` is its only input.
|
|
75
|
+
- **`release:github` validates the tag before publishing** — a lightweight tag, a subject over 72 characters or carrying the version or a `;`, a section header, a leaked signature block, or a final line other than the changelog link is refused before any `gh` call; `-- --check` runs only that validation.
|
|
76
|
+
|
|
77
|
+
## Changed
|
|
78
|
+
|
|
79
|
+
- **Capability refusals end at reconnecting** ([#495](https://github.com/cyanheads/mcp-ts-core/issues/495)) — the `client_capability_missing` hint no longer offers to take the answer as an argument, which a consent gate cannot. A handler whose arguments can stand in passes `ctx.requestInput(spec, { fallbackHint })`, a sentence appended to the hint.
|
|
80
|
+
- **`CanvasIdSchema` says what a canvas ID is** ([#483](https://github.com/cyanheads/mcp-ts-core/issues/483)) — a rejected `canvas_id` reads "Expected a canvas ID exactly as an earlier response on this server returned it…" in the message and hint instead of the bare regex. The advertised JSON Schema is unchanged.
|
|
81
|
+
- **MCP SDK 2.1.0 transport behavior** — a 2026-07-28 POST without `MCP-Protocol-Version` is refused `400` / `-32020`, and a JSON-RPC batch over 100 messages `400` / `-32600`. `notifications/cancelled` with `requestId: 0` now cancels, and stdin EOF on stdio aborts in-flight requests unanswered.
|
|
82
|
+
- **Release workflow skills** — `git-wrapup`'s gate adds `rebuild`; `release-pr-review` waits on code scanning in bounded calls, reads the PR's merge-ref alerts, and reports `finished` or `halted`; the greenfield workflow disables CodeQL while the repo is private, then reopens the release PR once it is public so the first scan runs; `security-pass` flags catching `client_capability_missing` to run a side effect. `release-and-publish` runs `release:github -- --check` before the push, and the orchestration workflows defer the tag format to it and drop the tag-moving protocol.
|
|
83
|
+
- **Skill corrections** — issue reads fetch the body and the thread separately, since `gh issue view --comments` prints no body without a TTY; smoke tests run `start:stdio < /dev/null` and check the `Core services constructed` record's `tools` / `resources` / `prompts` fields; the `add-resource` and `add-prompt` barrel snippets add to the `createApp()` array; `security-pass` greps use `grep -E`.
|
|
84
|
+
- **Scaffold `.mcpbignore`** — excludes `docs/idea.md` and `logs/` from the bundle.
|
|
85
|
+
- Skill versions: `add-app-tool` 1.6 → 1.7, `add-export` 1.1 → 1.2, `add-prompt` 1.4 → 1.5, `add-resource` 1.7 → 1.8, `add-tool` 2.30 → 2.31, `api-auth` 1.4 → 1.5, `api-canvas` 2.5 → 2.6, `api-config` 1.21 → 1.22, `api-context` 2.7 → 2.8, `api-errors` 1.17 → 1.18, `api-linter` 1.19 → 1.20, `api-mirror` 1.2 → 1.3, `api-telemetry` 1.14 → 1.15, `api-utils` 2.13 → 2.14, `design-mcp-server` 2.29 → 2.30, `field-test` 2.16 → 2.17, `git-wrapup` 1.26 → 1.27, `orchestrations` 1.11 → 1.12 (workflows `field-test-fix` 1.1 → 1.2, `fix-wrapup-release` 1.1 → 1.2, `greenfield-build` 1.2 → 1.3, `maintenance-release` 1.2 → 1.3), `polish-docs-meta` 2.19 → 2.20, `release-and-publish` 2.21 → 2.22, `release-pr-review` 1.5 → 1.6, `report-issue-framework` 1.13 → 1.14, `report-issue-local` 1.11 → 1.12, `security-pass` 1.10 → 1.11.
|
|
86
|
+
|
|
87
|
+
## Fixed
|
|
88
|
+
|
|
89
|
+
- **Underscore-prefixed keys reach their alias** ([#563](https://github.com/cyanheads/mcp-ts-core/issues/563)) — when a call fails after the drop stage discarded a key, pre-validation retries alias-first and keeps the retry only if it validates, so a declared `_q` alias or an undeclared `_query` reaches `query`. A call that fails both ways is rejected as the retry saw it: the value's own failure, closed by `Validated _q as query.`
|
|
90
|
+
- **One-or-many unions render like the field they wrap** ([#492](https://github.com/cyanheads/mcp-ts-core/issues/492)) — a bad element in `z.union([z.array(Item), Item])` reports `items.1.name: …` with the typed hint `Send items.1.name as a string, not a boolean.`, as a plain array field does.
|
|
91
|
+
- **Argument hints keep field paths** ([#493](https://github.com/cyanheads/mcp-ts-core/issues/493)) — a constraint or refinement restated in the hint carries its path (`start: Must be …, end: Must be …`) and repeats collapse, so a consumer test pinning a path-less hint moves.
|
|
92
|
+
- **A fractional number on an integer field names the fix** ([#499](https://github.com/cyanheads/mcp-ts-core/issues/499)) — `Send rows as an integer, not a fractional number.` instead of `as an int, not a number`.
|
|
93
|
+
- **Nested unknown keys are hinted from their own object** ([#566](https://github.com/cyanheads/mcp-ts-core/issues/566)) — `Unknown key opts.b. opts accepts: a.` instead of the root's key list.
|
|
94
|
+
- **The pacer's cooldown streak decays** ([#471](https://github.com/cyanheads/mcp-ts-core/issues/471)) — a rate limit arriving once the gate has stood open for `cooldown.maxMs` restarts the doubling at `baseMs`.
|
|
95
|
+
- **Pacer sheds report their kind and a usable `retryAfter`** ([#472](https://github.com/cyanheads/mcp-ts-core/issues/472)) — sheds carry `data.shedKind` (`queue_full` / `wait_projected` / `wait_elapsed`, type `PacerShedKind`), and `retryAfter` counts every remaining waiter and, under a saturated `maxConcurrent`, is at least the longest queued wait and 1. A due waiter starts before a new arrival meets `maxQueueDepth`.
|
|
96
|
+
- **A nested pacer's shed leaves the outer gate open** ([#559](https://github.com/cyanheads/mcp-ts-core/issues/559)) — a task error with `data.reason: 'pacer_shed'` neither closes the cooldown gate nor touches the streak.
|
|
97
|
+
- **`maxQueueDepth` bounds waiters, not arrivals** ([#560](https://github.com/cyanheads/mcp-ts-core/issues/560)) — an arrival whose slot is open starts at once, so `maxQueueDepth: 0` means "run when a slot is free, never wait".
|
|
98
|
+
- **A `baseMs: 0` cooldown keeps pacing** ([#564](https://github.com/cyanheads/mcp-ts-core/issues/564)) — the doubling exponent is capped, so a streak past 1,024 rate limits no longer computes `NaN` and switches off every window, gap, and gate.
|
|
99
|
+
- **Concurrent canvas spills no longer corrupt each other** ([#561](https://github.com/cyanheads/mcp-ts-core/issues/561)) — each canvas spills into its own `temp_directory`, removed when the canvas is dropped or evicted, once the calls still running on it settle.
|
|
100
|
+
- **DuckDB errors classify by DuckDB's error class and redact host paths** ([#565](https://github.com/cyanheads/mcp-ts-core/issues/565)) — `sql_read_only` needs a `Permission Error` or a read-only-mode message and `sql_parse_error` a `Parser Error`, so an `IO Error` such as an unwritable export directory is `DatabaseError` (`-32010`) without a reason. The export root and scratch paths read `[path]` in engine error messages.
|
|
101
|
+
- **`lint:mcp` fails on definitions it cannot load** ([#516](https://github.com/cyanheads/mcp-ts-core/issues/516)) — a definition file whose `import()` rejects is a `definition-import-failed` error and an unparseable `server.json` a `server-json-parse` error, both exiting 1, while the remaining files are still linted.
|
|
102
|
+
- **`input-alias-conflict` covers `headerParam` targets** ([#569](https://github.com/cyanheads/mcp-ts-core/issues/569)) — an alias onto a header-mirrored field, which the rewrite never targets, now fails lint.
|
|
103
|
+
- **The CORS preflight allows the 2026-07-28 request headers** ([#571](https://github.com/cyanheads/mcp-ts-core/issues/571)) — an origin the Origin guard admits may also send `Mcp-Method`, `Mcp-Name`, `Last-Event-ID`, and each registered tool's `Mcp-Param-<Name>` (the manifest's new `headerParams`); other origins keep the 2025-era set. Every CORS response carries `Vary: Origin`.
|
|
104
|
+
- **Mirror examples pass `table`** — the `api-mirror` skill and the `defineMirror` JSDoc give `sqliteMirrorStore` its required `table` option.
|
|
105
|
+
|
|
106
|
+
## Security
|
|
107
|
+
|
|
108
|
+
- **Canvas scratch files live in a private directory** ([#554](https://github.com/cyanheads/mcp-ts-core/issues/554)) — the DuckDB provider no longer uses the fixed `<tmpdir>/mcp-canvas`, which other local users could read or pre-create. It creates a private `mkdtemp` directory (`0700` on POSIX) inside `CANVAS_TEMP_PATH`, now a parent directory, and removes it at shutdown; one it cannot create fails canvas creation as `ConfigurationError`.
|
|
109
|
+
|
|
110
|
+
## Dependencies
|
|
111
|
+
|
|
112
|
+
- `@modelcontextprotocol/server` ^2.0.0 → ^2.1.0 (transport behavior under Changed).
|
|
113
|
+
- Dev: `@modelcontextprotocol/client` ^2.0.0 → ^2.1.0.
|
package/dist/config/index.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
26
26
|
}>, unknown>>;
|
|
27
27
|
logsPath: z.ZodOptional<z.ZodString>;
|
|
28
28
|
logLlmInteractions: z.ZodPreprocess<z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCodec<z.ZodString, z.ZodBoolean>]>>, unknown>;
|
|
29
|
+
logToolFailurePayloads: z.ZodPreprocess<z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCodec<z.ZodString, z.ZodBoolean>]>>, unknown>;
|
|
30
|
+
logToolFailurePayloadMaxBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
29
31
|
logRateLimitThreshold: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
30
32
|
logRateLimitWindowMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
31
33
|
environment: z.ZodDefault<z.ZodPreprocess<z.ZodEnum<{
|
|
@@ -121,6 +123,7 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
121
123
|
serviceVersion: z.ZodString;
|
|
122
124
|
tracesEndpoint: z.ZodOptional<z.ZodURL>;
|
|
123
125
|
metricsEndpoint: z.ZodOptional<z.ZodURL>;
|
|
126
|
+
logsEndpoint: z.ZodOptional<z.ZodURL>;
|
|
124
127
|
samplingRatio: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
125
128
|
logLevel: z.ZodDefault<z.ZodPreprocess<z.ZodEnum<{
|
|
126
129
|
ALL: "ALL";
|
|
@@ -171,6 +174,8 @@ declare const parseConfig: (envOverrides?: Record<string, string | undefined>) =
|
|
|
171
174
|
logLevel: "alert" | "crit" | "debug" | "emerg" | "error" | "info" | "notice" | "warning";
|
|
172
175
|
logsPath?: string | undefined;
|
|
173
176
|
logLlmInteractions: boolean;
|
|
177
|
+
logToolFailurePayloads: boolean;
|
|
178
|
+
logToolFailurePayloadMaxBytes: number;
|
|
174
179
|
logRateLimitThreshold: number;
|
|
175
180
|
logRateLimitWindowMs: number;
|
|
176
181
|
environment: "development" | "production" | "testing";
|
|
@@ -241,6 +246,7 @@ declare const parseConfig: (envOverrides?: Record<string, string | undefined>) =
|
|
|
241
246
|
serviceVersion: string;
|
|
242
247
|
tracesEndpoint?: string | undefined;
|
|
243
248
|
metricsEndpoint?: string | undefined;
|
|
249
|
+
logsEndpoint?: string | undefined;
|
|
244
250
|
samplingRatio: number;
|
|
245
251
|
logLevel: "ALL" | "DEBUG" | "ERROR" | "INFO" | "NONE" | "VERBOSE" | "WARN";
|
|
246
252
|
};
|
|
@@ -296,6 +302,8 @@ declare const config: {
|
|
|
296
302
|
logLevel: "alert" | "crit" | "debug" | "emerg" | "error" | "info" | "notice" | "warning";
|
|
297
303
|
logsPath?: string | undefined;
|
|
298
304
|
logLlmInteractions: boolean;
|
|
305
|
+
logToolFailurePayloads: boolean;
|
|
306
|
+
logToolFailurePayloadMaxBytes: number;
|
|
299
307
|
logRateLimitThreshold: number;
|
|
300
308
|
logRateLimitWindowMs: number;
|
|
301
309
|
environment: "development" | "production" | "testing";
|
|
@@ -366,6 +374,7 @@ declare const config: {
|
|
|
366
374
|
serviceVersion: string;
|
|
367
375
|
tracesEndpoint?: string | undefined;
|
|
368
376
|
metricsEndpoint?: string | undefined;
|
|
377
|
+
logsEndpoint?: string | undefined;
|
|
369
378
|
samplingRatio: number;
|
|
370
379
|
logLevel: "ALL" | "DEBUG" | "ERROR" | "INFO" | "NONE" | "VERBOSE" | "WARN";
|
|
371
380
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB,wEAAwE;AACxE,eAAO,MAAM,cAAc,2BAA2B,CAAC;AACvD,eAAO,MAAM,iBAAiB,QAAkC,CAAC;AA6DjE,QAAA,MAAM,YAAY
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB,wEAAwE;AACxE,eAAO,MAAM,cAAc,2BAA2B,CAAC;AACvD,eAAO,MAAM,iBAAiB,QAAkC,CAAC;AA6DjE,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiYd,CAAC;AAGL,QAAA,MAAM,WAAW,kBAAmB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8MrE,CAAC;AAIF;;;;;;;;GAQG;AACH,QAAA,MAAM,WAAW,kBAAmB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,KAAG,IAExE,CAAC;AAEF;;;;;;GAMG;AACH,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuBV,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAErD;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC"}
|