@cyanheads/mcp-ts-core 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +1 -1
- package/README.md +1 -1
- package/changelog/0.8.x/0.8.0.md +17 -15
- package/changelog/0.8.x/0.8.1.md +17 -0
- package/changelog/template.md +13 -0
- package/dist/logs/combined.log +4 -4
- package/dist/logs/error.log +4 -4
- package/package.json +1 -1
- package/skills/add-service/SKILL.md +7 -1
- package/skills/add-tool/SKILL.md +3 -1
- package/skills/api-errors/SKILL.md +21 -1
- package/skills/field-test/SKILL.md +73 -13
- package/skills/maintenance/SKILL.md +4 -1
- package/templates/changelog/template.md +18 -5
package/CLAUDE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agent Protocol
|
|
2
2
|
|
|
3
|
-
**Package:** `@cyanheads/mcp-ts-core` · **Version:** 0.8.
|
|
3
|
+
**Package:** `@cyanheads/mcp-ts-core` · **Version:** 0.8.1
|
|
4
4
|
**npm:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) · **Docker:** [ghcr.io/cyanheads/mcp-ts-core](https://ghcr.io/cyanheads/mcp-ts-core)
|
|
5
5
|
|
|
6
6
|
> **Developer note:** Never assume. Read related files and docs before making changes. Read full file content for context. Never edit a file before reading it.
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
<div align="center">
|
|
7
7
|
|
|
8
|
-
[](./CHANGELOG.md) [](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-11-25/changelog.mdx) [](https://modelcontextprotocol.io/) [](./LICENSE)
|
|
9
9
|
|
|
10
10
|
[](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
11
11
|
|
package/changelog/0.8.x/0.8.0.md
CHANGED
|
@@ -5,27 +5,29 @@ breaking: false
|
|
|
5
5
|
|
|
6
6
|
# 0.8.0 — 2026-04-28
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Typed error contracts for tools and resources. Backwards compatible — definitions without `errors[]` keep the existing `Context` signature.
|
|
9
9
|
|
|
10
10
|
## Added
|
|
11
11
|
|
|
12
|
-
-
|
|
13
|
-
- **`
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
- **
|
|
18
|
-
-
|
|
19
|
-
- **
|
|
12
|
+
- **`errors: [{ reason, code, when, retryable? }]`** on `tool()` / `resource()` / `appTool()` / `appResource()`. Surfaces in `tools/list` and `resources/list` under `_meta['mcp-ts-core/errors']`. Const-tuple inference flows the reason union into the handler's `ctx.fail`.
|
|
13
|
+
- **`ctx.fail(reason, msg?, data?, opts?)`** — typed `TypedFail<R>` keyed against the contract's reasons. `ctx.fail('typo')` is a TS error. Constructs an `McpError` with the contract's code, message (defaults to `when`), and `data.reason` auto-populated from the contract — caller-supplied `data.reason` cannot override it.
|
|
14
|
+
- **New core exports:** `TypedFail`, `ReasonOf`, `HandlerContext`, `createFail`. **New `/errors` exports:** `ErrorContract`, `ERROR_CONTRACT_META_KEY`, `buildMetaWithErrorContract`.
|
|
15
|
+
- **`httpErrorFromResponse(response, opts?)` and `httpStatusToErrorCode(status)` in `/utils`** — full 4xx/5xx → `JsonRpcErrorCode` table (401/403/408/422/429/5xx). Captures truncated body and `Retry-After` into `error.data`. Supports `service`, `cause`, `codeOverride`. Exports `HttpErrorFromResponseOptions`.
|
|
16
|
+
- **`partialResult` / `partialResultSchema` / `failureEntrySchema` in `/utils`** — typed succeeded/failed pattern with stable reason enums. Generic over key names. `failed[]` is omitted when empty. Exports `PartialResultObject`.
|
|
17
|
+
- **Three more factories on `/errors`:** `internalError`, `serializationError`, `databaseError`. Same `(message, data?, options?)` signature as the rest.
|
|
18
|
+
- **Handler-body lints** (warnings, surfaced in devcheck): `prefer-mcp-error-in-handler`, `prefer-error-factory`, `preserve-cause-on-rethrow`, `no-stringify-upstream-error`.
|
|
19
|
+
- **Contract lints** (warnings): structural validation (`error-contract-{type,empty,entry-type,code-type,code-unknown,reason-required,reason-format,reason-unique,when-required,retryable-type}`); conformance — `error-contract-conformance` flags undeclared codes thrown from the handler, `error-contract-prefer-fail` flags declared codes thrown directly instead of via `ctx.fail`. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) auto-allowed.
|
|
20
|
+
- **`createMockContext({ errors })`** — attaches `ctx.fail` against a contract for tests.
|
|
21
|
+
- **Tests** for typed-fail propagation, the two new lint families, `httpErrorFromResponse`, `partialResult`, and the auto-task / app-tool integration paths.
|
|
20
22
|
|
|
21
23
|
## Changed
|
|
22
24
|
|
|
23
|
-
- **`
|
|
24
|
-
- **`
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
- **`docs/tree.md`** regenerated
|
|
25
|
+
- **`ToolDefinition` / `ResourceDefinition`** gain a third type parameter `TErrors extends readonly ErrorContract[] | undefined = undefined`. Existing `ToolDefinition<X, Y>` annotations keep working; handler signature collapses to plain `Context` when no contract is declared. `AnyToolDefinition` / `AnyResourceDefinition` widened accordingly.
|
|
26
|
+
- **`AGENTS.md` / `CLAUDE.md` / `templates/AGENTS.md` / `templates/CLAUDE.md`** — Error Handling sections rewritten to lead with the contract path, factories demoted to fallback. Exports table and factory list updated.
|
|
27
|
+
- **`README.md`** — "Structured errors" feature bullet rewritten as "Typed error contracts".
|
|
28
|
+
- **Skills sync** — `add-tool`, `add-resource`, `add-service`, `add-test`, `add-app-tool`, `api-context`, `api-errors`, `api-linter`, `api-testing`, `api-utils`, `design-mcp-server`, `field-test` updated for the new surface. `maintenance` v1.7 → v1.8, `security-pass` v1.1 → v1.2, `report-issue-framework` v1.3 → v1.4.
|
|
29
|
+
- **`docs/tree.md`** regenerated.
|
|
28
30
|
|
|
29
31
|
## Fixed
|
|
30
32
|
|
|
31
|
-
-
|
|
33
|
+
- Stale `@see docs/service-resilience.md` reference removed from `src/utils/network/retry.ts`.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Skills sync — service-thrown contract reasons (pass `data: { reason }` from factories), field-test helper hardening, maintenance skill-version paradox check, factory-choice semantic audit"
|
|
3
|
+
breaking: false
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 0.8.1 — 2026-04-28
|
|
7
|
+
|
|
8
|
+
Skill-only follow-up to 0.8.0. Documents the missing pattern for carrying contract `reason` from service-layer throws (services don't have `ctx.fail`), hardens the field-test helper, and teaches `maintenance` to self-detect a skill-version paradox.
|
|
9
|
+
|
|
10
|
+
## Changed
|
|
11
|
+
|
|
12
|
+
- **`api-errors` v1.0 → v1.1** — adds "Carrying contract `reason` from services" section. Services pass `data: { reason: 'X' }` to error factories; the auto-classifier preserves `data` on the wire so clients see the same `error.data.reason` they'd see from `ctx.fail`. Trade-off noted: lint-time conformance enforcement is lost; compensate with one wire-shape test per reason.
|
|
13
|
+
- **`add-service` v1.3 → v1.4** — same pattern surfaced as a "Carry contract `reason` via `data: { reason }`" bullet under the "Services don't declare contracts" rule.
|
|
14
|
+
- **`add-tool` v1.8 → v1.9** — cross-references the service-thrown reason path in the `ctx.fail` section, so handler authors see it where they're already reading.
|
|
15
|
+
- **`field-test` v2.0 → v2.1** — `/tmp/mcp-field-test.sh` helper hardened: build/start failures auto-tail their logs; `mcp_init` captures HTTP status + body on failure; `mcp_call` surfaces 4xx/5xx with body + falls through to raw body when SSE framing is absent (so plain-JSON errors aren't swallowed); new `mcp_log [N]` tails server log for surprising responses; `mcp_stop` waits for SIGTERM, escalates to SIGKILL, reports residual processes.
|
|
16
|
+
- **`maintenance` v1.8 → v1.9** — adds skill-version paradox check: if `node_modules/@cyanheads/mcp-ts-core/skills/maintenance/SKILL.md` version exceeds the running one, run Phase A first and re-invoke. Also adds factory-choice semantic audit row: `invalidParams` is for malformed JSON-RPC params (rare post-Zod); semantic post-shape validation should use `validationError`. Wrong codes degrade `mcp_error_classified_code` observability and break client retry logic.
|
|
17
|
+
- **`templates/changelog/template.md`** — example issue/PR numbers updated from placeholder `#12`/`#17` to concrete `#38`/`#42` against `cyanheads/mcp-ts-core` so the linking guidance reads correctly when copied.
|
package/changelog/template.md
CHANGED
|
@@ -27,6 +27,19 @@ breaking: false
|
|
|
27
27
|
|
|
28
28
|
Optional narrative intro — 1-3 sentences framing the release theme. Delete if not needed.
|
|
29
29
|
|
|
30
|
+
TONE: terse and fact-dense. 1-2 sentence(s) per bullet where possible —
|
|
31
|
+
name the symbol, state what changed, stop. Drop "explains how it works" prose;
|
|
32
|
+
that belongs in JSDoc, AGENTS.md, or the relevant skill. Drop ceremonial
|
|
33
|
+
framings ("This release introduces…", "fully backwards compatible:" with a
|
|
34
|
+
paragraph of justification). Prefer code/symbol names over English
|
|
35
|
+
re-explanations. If a bullet runs more than ~2 lines, split it or cut it.
|
|
36
|
+
|
|
37
|
+
WHAT TO INCLUDE: every distinct fact a reader needs to adopt or audit the
|
|
38
|
+
release — new exports, signatures, lint rule IDs, env vars, breaking
|
|
39
|
+
changes, version bumps on shipped skills. WHAT TO CUT: mechanism walkthroughs,
|
|
40
|
+
duplicate prose between Added and Changed, file-by-file test enumerations,
|
|
41
|
+
internal implementation notes. Trust the reader to read the code or the docs.
|
|
42
|
+
|
|
30
43
|
Linking issues/PRs: use full URLs so the link works everywhere (GitHub web UI,
|
|
31
44
|
npm/node_modules reads, local editors). GitHub's bare `#NN` auto-link only
|
|
32
45
|
resolves inside its own UI.
|
package/dist/logs/combined.log
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{"level":50,"time":
|
|
2
|
-
{"level":50,"time":
|
|
3
|
-
{"level":50,"time":
|
|
4
|
-
{"level":50,"time":
|
|
1
|
+
{"level":50,"time":1777415595855,"env":"testing","version":"0.0.0-test","pid":99019,"requestId":"63RGD-YP65A","timestamp":"2026-04-28T22:33:15.853Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"5a13b0091a5f90f81e77c9eb13f93c113b034065cf9fae82c794997d56c9cf18","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"errorData":{"sessionId":"5a13b0091a5f90f81e77c9eb13f93c113b034065cf9fae82c794997d56c9cf18","toolName":"scoped_echo","requestId":"63RGD-YP65A","timestamp":"2026-04-28T22:33:15.853Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:61:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:72:17)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Insufficient permissions.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:107:42)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
2
|
+
{"level":50,"time":1777415596525,"env":"testing","version":"0.8.1","pid":99023,"requestId":"SJ7A2-YIFMY","timestamp":"2026-04-28T22:33:16.524Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"SJ7A2-YIFMY","timestamp":"2026-04-28T22:33:16.524Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Missing or invalid Authorization header. Bearer scheme required.","originalStack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at authMiddleware (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/authMiddleware.js:64:19)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpTransport.js:232:22)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at cors2 (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/middleware/cors/index.js:82:11)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Missing or invalid Authorization header. Bearer scheme required."}
|
|
3
|
+
{"level":50,"time":1777415596539,"env":"testing","version":"0.8.1","pid":99023,"requestId":"OO6W9-ZL8EV","timestamp":"2026-04-28T22:33:16.539Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"OO6W9-ZL8EV","timestamp":"2026-04-28T22:33:16.539Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Token has expired.","originalStack":"McpError: Token has expired.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at handleJoseVerifyError (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/claimParser.js:56:11)\n at verify (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/strategies/jwtStrategy.js:91:13)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Token has expired.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Token has expired."}
|
|
4
|
+
{"level":50,"time":1777415596544,"env":"testing","version":"0.8.1","pid":99023,"requestId":"3NQXB-GTIJC","timestamp":"2026-04-28T22:33:16.544Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"GET","errorData":{"path":"/mcp","method":"GET","requestId":"3NQXB-GTIJC","timestamp":"2026-04-28T22:33:16.544Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Missing or invalid Authorization header. Bearer scheme required.","originalStack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at authMiddleware (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/authMiddleware.js:64:19)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpTransport.js:232:22)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at cors2 (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/middleware/cors/index.js:82:11)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Missing or invalid Authorization header. Bearer scheme required."}
|
package/dist/logs/error.log
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{"level":50,"time":
|
|
2
|
-
{"level":50,"time":
|
|
3
|
-
{"level":50,"time":
|
|
4
|
-
{"level":50,"time":
|
|
1
|
+
{"level":50,"time":1777415595855,"env":"testing","version":"0.0.0-test","pid":99019,"requestId":"63RGD-YP65A","timestamp":"2026-04-28T22:33:15.853Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"5a13b0091a5f90f81e77c9eb13f93c113b034065cf9fae82c794997d56c9cf18","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"errorData":{"sessionId":"5a13b0091a5f90f81e77c9eb13f93c113b034065cf9fae82c794997d56c9cf18","toolName":"scoped_echo","requestId":"63RGD-YP65A","timestamp":"2026-04-28T22:33:15.853Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:61:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:72:17)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Insufficient permissions.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:107:42)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
|
|
2
|
+
{"level":50,"time":1777415596525,"env":"testing","version":"0.8.1","pid":99023,"requestId":"SJ7A2-YIFMY","timestamp":"2026-04-28T22:33:16.524Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"SJ7A2-YIFMY","timestamp":"2026-04-28T22:33:16.524Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Missing or invalid Authorization header. Bearer scheme required.","originalStack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at authMiddleware (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/authMiddleware.js:64:19)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpTransport.js:232:22)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at cors2 (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/middleware/cors/index.js:82:11)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Missing or invalid Authorization header. Bearer scheme required."}
|
|
3
|
+
{"level":50,"time":1777415596539,"env":"testing","version":"0.8.1","pid":99023,"requestId":"OO6W9-ZL8EV","timestamp":"2026-04-28T22:33:16.539Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"OO6W9-ZL8EV","timestamp":"2026-04-28T22:33:16.539Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Token has expired.","originalStack":"McpError: Token has expired.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at handleJoseVerifyError (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/claimParser.js:56:11)\n at verify (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/strategies/jwtStrategy.js:91:13)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Token has expired.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Token has expired."}
|
|
4
|
+
{"level":50,"time":1777415596544,"env":"testing","version":"0.8.1","pid":99023,"requestId":"3NQXB-GTIJC","timestamp":"2026-04-28T22:33:16.544Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"GET","errorData":{"path":"/mcp","method":"GET","requestId":"3NQXB-GTIJC","timestamp":"2026-04-28T22:33:16.544Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Missing or invalid Authorization header. Bearer scheme required.","originalStack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at authMiddleware (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/authMiddleware.js:64:19)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpTransport.js:232:22)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at cors2 (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/middleware/cors/index.js:82:11)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:169:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Missing or invalid Authorization header. Bearer scheme required."}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyanheads/mcp-ts-core",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"mcpName": "io.github.cyanheads/mcp-ts-core",
|
|
5
5
|
"description": "Agent-native TypeScript framework for building MCP servers. Declarative definitions with auth, multi-backend storage, OpenTelemetry, and first-class support for Bun/Node/Cloudflare Workers.",
|
|
6
6
|
"main": "dist/core/index.js",
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Scaffold a new service integration. Use when the user asks to add a service, integrate an external API, or create a reusable domain module with its own initialization and state.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.4"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -234,6 +234,12 @@ Services don't declare `errors: [...]` contracts and don't have `ctx.fail` — t
|
|
|
234
234
|
```
|
|
235
235
|
|
|
236
236
|
- **Tool/resource handlers bubble service errors unchanged** — the contract advertises the *advertised* failure surface, and any code thrown from a service still reaches the client correctly via the auto-classifier. The conformance lint scans handler source text only, so service-thrown codes aren't flagged.
|
|
237
|
+
- **Carry contract `reason` via `data: { reason }`** when the calling tool declares an `errors[]` contract entry for this failure mode. Services can't call `ctx.fail`, but passing the reason in `data` flows through the auto-classifier untouched, so clients see the same `error.data.reason` they'd see from `ctx.fail` — no handler-side catch-and-rethrow needed:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
// tool declares: errors: [{ reason: 'empty_expression', code: JsonRpcErrorCode.ValidationError, when: '…' }]
|
|
241
|
+
throw validationError('Expression cannot be empty.', { reason: 'empty_expression' });
|
|
242
|
+
```
|
|
237
243
|
|
|
238
244
|
## API Efficiency
|
|
239
245
|
|
package/skills/add-tool/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Scaffold a new MCP tool definition. Use when the user asks to add a tool, create a new tool, or implement a new capability for the server.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.9"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -269,6 +269,8 @@ export const fetchArticles = tool('fetch_articles', {
|
|
|
269
269
|
|
|
270
270
|
`ctx.fail` accepts an optional 4th `options` argument for ES2022 cause chaining: `throw ctx.fail('upstream_error', 'Upstream returned 500', { url }, { cause: e })`.
|
|
271
271
|
|
|
272
|
+
**Service-thrown contract reasons.** When the throw happens in a called service rather than the handler itself, `ctx.fail` isn't reachable — services don't have `ctx`. Pass `data: { reason: 'X' }` to the factory in the service; the framework's auto-classifier preserves `data` on the wire, so the contract reason rides through unchanged. The handler bubbles the error without catching. See `add-service` for the pattern.
|
|
273
|
+
|
|
272
274
|
**Fallback: error factories.** Use when no contract entry fits — ad-hoc throws, prototype tools, or service-layer code. The framework also auto-classifies plain `throw new Error()` from message patterns as a last resort.
|
|
273
275
|
|
|
274
276
|
```typescript
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
McpError constructor, JsonRpcErrorCode reference, and error handling patterns for `@cyanheads/mcp-ts-core`. Use when looking up error codes, understanding where errors should be thrown vs. caught, or using ErrorHandler.tryCatch in services.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.1"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -69,6 +69,26 @@ export const fetchTool = tool('fetch_articles', {
|
|
|
69
69
|
|
|
70
70
|
> **Limits of the conformance lint.** The conformance and prefer-fail rules scan the handler's source text for `throw` statements. Errors thrown from called services (e.g. `await myService.fetch()` raising `RateLimited` internally) are invisible — the lint only sees what's lexically in the handler. Treat the contract as the *advertised* failure surface; bubbled-up codes still reach the client correctly via the auto-classifier, just without lint enforcement.
|
|
71
71
|
|
|
72
|
+
### Carrying contract `reason` from services
|
|
73
|
+
|
|
74
|
+
Services don't have `ctx`, so they can't call `ctx.fail`. To make a service-thrown failure carry the contract's `reason` on the wire, **pass `data: { reason: 'X' }` to the factory**. The framework's auto-classifier preserves `data` unchanged, so clients see the same `error.data.reason` they'd see from `ctx.fail`:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
// my-service.ts
|
|
78
|
+
throw validationError('Expression cannot be empty.', { reason: 'empty_expression' });
|
|
79
|
+
throw serviceUnavailable('Upstream timeout', { reason: 'evaluation_timeout' });
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
// my-tool.tool.ts
|
|
84
|
+
errors: [
|
|
85
|
+
{ reason: 'empty_expression', code: JsonRpcErrorCode.ValidationError, when: 'Input is empty.' },
|
|
86
|
+
{ reason: 'evaluation_timeout', code: JsonRpcErrorCode.ServiceUnavailable, when: 'Upstream exceeded the configured timeout.' },
|
|
87
|
+
]
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The handler doesn't catch and re-throw — letting service errors bubble unchanged keeps "logic throws, framework catches" intact. The contract still publishes in `tools/list`, the wire payload still carries `code` + `data.reason`, and clients can switch on reason without parsing message text. What's lost is lint-time enforcement that every reason is reachable; compensate with one wire-shape test per reason.
|
|
91
|
+
|
|
72
92
|
---
|
|
73
93
|
|
|
74
94
|
## Error Factories (fallback)
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Exercise tools, resources, and prompts against a live HTTP server via MCP JSON-RPC over curl. Starts the server, surfaces the catalog, runs real and adversarial inputs, and produces a tight report with concrete findings and numbered follow-up options. Use after adding or modifying definitions, or when the user asks to test, try out, or verify their MCP surface.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "2.
|
|
7
|
+
version: "2.1"
|
|
8
8
|
audience: external
|
|
9
9
|
type: debug
|
|
10
10
|
---
|
|
@@ -27,14 +27,20 @@ Write the helper to `/tmp/mcp-field-test.sh` once, then source it in every subse
|
|
|
27
27
|
cat > /tmp/mcp-field-test.sh <<'HELPER_EOF'
|
|
28
28
|
#!/bin/bash
|
|
29
29
|
# Field-test helper: manage an MCP HTTP server + JSON-RPC session across shell calls.
|
|
30
|
+
# Surfaces failures aggressively — field test is for finding things that fail,
|
|
31
|
+
# so the helper auto-tails logs and prints HTTP status/body on errors instead
|
|
32
|
+
# of swallowing them.
|
|
30
33
|
STATE_FILE="/tmp/mcp-field-test.env"
|
|
31
34
|
[ -f "$STATE_FILE" ] && . "$STATE_FILE"
|
|
32
35
|
|
|
33
36
|
mcp_start() {
|
|
34
37
|
local dir="${1:-$PWD}"
|
|
35
38
|
echo "building $dir ..."
|
|
36
|
-
(cd "$dir" && bun run rebuild) >/tmp/mcp-build.log 2>&1
|
|
37
|
-
|
|
39
|
+
if ! (cd "$dir" && bun run rebuild) >/tmp/mcp-build.log 2>&1; then
|
|
40
|
+
echo "BUILD FAILED — last 30 lines of /tmp/mcp-build.log:"
|
|
41
|
+
tail -30 /tmp/mcp-build.log
|
|
42
|
+
return 1
|
|
43
|
+
fi
|
|
38
44
|
echo "starting server ..."
|
|
39
45
|
(cd "$dir" && bun run start:http) >/tmp/mcp-server.log 2>&1 &
|
|
40
46
|
local pid=$!
|
|
@@ -45,7 +51,8 @@ mcp_start() {
|
|
|
45
51
|
sleep 0.25
|
|
46
52
|
done
|
|
47
53
|
if [ -z "$line" ]; then
|
|
48
|
-
echo "server failed to start —
|
|
54
|
+
echo "server failed to start within 10s — last 30 lines of /tmp/mcp-server.log:"
|
|
55
|
+
tail -30 /tmp/mcp-server.log
|
|
49
56
|
kill "$pid" 2>/dev/null
|
|
50
57
|
return 1
|
|
51
58
|
fi
|
|
@@ -63,12 +70,21 @@ EOF
|
|
|
63
70
|
mcp_init() {
|
|
64
71
|
[ -z "$MCP_URL" ] && { echo "run mcp_start first"; return 1; }
|
|
65
72
|
local hdr="/tmp/mcp-init-headers.txt"
|
|
66
|
-
|
|
73
|
+
local body_file="/tmp/mcp-init-body.txt"
|
|
74
|
+
local status
|
|
75
|
+
status=$(curl -sS -D "$hdr" -o "$body_file" -w '%{http_code}' -X POST "$MCP_URL" \
|
|
67
76
|
-H "Content-Type: application/json" \
|
|
68
77
|
-H "Accept: application/json, text/event-stream" \
|
|
69
|
-
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"field-test","version":"2.
|
|
78
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"field-test","version":"2.1"}}}')
|
|
70
79
|
local sid; sid=$(grep -i '^mcp-session-id:' "$hdr" | awk '{print $2}' | tr -d '\r\n')
|
|
71
|
-
[ -z "$sid" ]
|
|
80
|
+
if [ -z "$sid" ]; then
|
|
81
|
+
echo "init failed — HTTP $status, no Mcp-Session-Id header returned"
|
|
82
|
+
echo "--- response body ---"
|
|
83
|
+
cat "$body_file"
|
|
84
|
+
echo "--- response headers ---"
|
|
85
|
+
cat "$hdr"
|
|
86
|
+
return 1
|
|
87
|
+
fi
|
|
72
88
|
cat > "$STATE_FILE" <<EOF
|
|
73
89
|
export MCP_PID=$MCP_PID
|
|
74
90
|
export MCP_URL=$MCP_URL
|
|
@@ -81,11 +97,13 @@ EOF
|
|
|
81
97
|
-H "Accept: application/json, text/event-stream" \
|
|
82
98
|
-H "Mcp-Session-Id: $sid" \
|
|
83
99
|
-d '{"jsonrpc":"2.0","method":"notifications/initialized"}' >/dev/null
|
|
84
|
-
echo "session=$sid"
|
|
100
|
+
echo "session=$sid (HTTP $status)"
|
|
85
101
|
}
|
|
86
102
|
|
|
87
103
|
# Usage: mcp_call METHOD [JSON_PARAMS]
|
|
88
|
-
# Prints the JSON-RPC response
|
|
104
|
+
# Prints the JSON-RPC response. SSE framing is stripped when present; on
|
|
105
|
+
# non-SSE responses the raw body is printed instead so plain-JSON error
|
|
106
|
+
# replies (HTTP 4xx/5xx) still surface. Pipe to `jq`.
|
|
89
107
|
mcp_call() {
|
|
90
108
|
[ -z "$MCP_SID" ] && { echo "run mcp_init first"; return 1; }
|
|
91
109
|
local method="$1"; local params="${2:-}"
|
|
@@ -95,17 +113,57 @@ mcp_call() {
|
|
|
95
113
|
else
|
|
96
114
|
body=$(printf '{"jsonrpc":"2.0","id":%d,"method":"%s","params":%s}' "$RANDOM" "$method" "$params")
|
|
97
115
|
fi
|
|
98
|
-
|
|
116
|
+
local resp_file="/tmp/mcp-call-body.txt"
|
|
117
|
+
local status
|
|
118
|
+
status=$(curl -sS -o "$resp_file" -w '%{http_code}' -X POST "$MCP_URL" \
|
|
99
119
|
-H "Content-Type: application/json" \
|
|
100
120
|
-H "Accept: application/json, text/event-stream" \
|
|
101
121
|
-H "Mcp-Session-Id: $MCP_SID" \
|
|
102
|
-
-d "$body"
|
|
122
|
+
-d "$body")
|
|
123
|
+
if [ "$status" -ge 400 ]; then
|
|
124
|
+
echo "HTTP $status from $method — response:" >&2
|
|
125
|
+
cat "$resp_file" >&2
|
|
126
|
+
return 1
|
|
127
|
+
fi
|
|
128
|
+
local sse; sse=$(sed -n 's/^data: //p' "$resp_file")
|
|
129
|
+
if [ -n "$sse" ]; then
|
|
130
|
+
printf '%s\n' "$sse"
|
|
131
|
+
else
|
|
132
|
+
cat "$resp_file"
|
|
133
|
+
fi
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# Tail the server log. Useful when a call surprises you — pino startup banner,
|
|
137
|
+
# definition lint diagnostics, request handler errors, upstream calls, and
|
|
138
|
+
# rate-limit warnings live in /tmp/mcp-server.log.
|
|
139
|
+
# Usage: mcp_log [N] (default: 50 lines)
|
|
140
|
+
mcp_log() {
|
|
141
|
+
local n="${1:-50}"
|
|
142
|
+
tail -n "$n" /tmp/mcp-server.log
|
|
103
143
|
}
|
|
104
144
|
|
|
105
145
|
mcp_stop() {
|
|
106
|
-
[ -
|
|
146
|
+
if [ -z "$MCP_PID" ]; then
|
|
147
|
+
rm -f "$STATE_FILE"
|
|
148
|
+
echo "no PID to stop"
|
|
149
|
+
return 0
|
|
150
|
+
fi
|
|
151
|
+
kill "$MCP_PID" 2>/dev/null
|
|
152
|
+
for _ in $(seq 1 12); do
|
|
153
|
+
kill -0 "$MCP_PID" 2>/dev/null || break
|
|
154
|
+
sleep 0.25
|
|
155
|
+
done
|
|
156
|
+
if kill -0 "$MCP_PID" 2>/dev/null; then
|
|
157
|
+
echo "PID $MCP_PID didn't exit on SIGTERM — sending SIGKILL"
|
|
158
|
+
kill -9 "$MCP_PID" 2>/dev/null
|
|
159
|
+
sleep 0.5
|
|
160
|
+
fi
|
|
161
|
+
if kill -0 "$MCP_PID" 2>/dev/null; then
|
|
162
|
+
echo "WARNING: PID $MCP_PID still alive after SIGKILL"
|
|
163
|
+
else
|
|
164
|
+
echo "stopped pid=$MCP_PID"
|
|
165
|
+
fi
|
|
107
166
|
rm -f "$STATE_FILE"
|
|
108
|
-
echo "stopped"
|
|
109
167
|
}
|
|
110
168
|
HELPER_EOF
|
|
111
169
|
|
|
@@ -190,6 +248,8 @@ Use `TaskCreate` — one task per definition. Mark complete as you go. Don't bat
|
|
|
190
248
|
|
|
191
249
|
For each call, capture: input sent, response (trim huge payloads to files), whether `isError: true` appeared, anything surprising (slow response, parity drift, unhelpful text, crash).
|
|
192
250
|
|
|
251
|
+
When a call surprises you — slow, hangs, returns terse output, surfaces an unhelpful error — run `. /tmp/mcp-field-test.sh && mcp_log` to tail the server log. The pino startup banner, request handler errors, upstream API call traces, and rate-limit warnings all land in `/tmp/mcp-server.log` rather than coming back through `mcp_call`. Don't guess at runtime behavior from response text alone.
|
|
252
|
+
|
|
193
253
|
**Interpreting responses**
|
|
194
254
|
|
|
195
255
|
- Tool domain errors return `{result: {content: [...], isError: true}}` — they live in `result`, not `error`. Check `isError`, not the JSON-RPC error field.
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Investigate, adopt, and verify dependency updates — with special handling for `@cyanheads/mcp-ts-core`. Captures what changed, understands why, cross-references against the codebase, adopts framework improvements, syncs project skills, and runs final checks. Supports two entry modes: run the full flow end-to-end, or review updates you already applied.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.9"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -50,6 +50,8 @@ Do not redo this investigation inline — the `changelog` skill handles tag-form
|
|
|
50
50
|
|
|
51
51
|
### 4. Framework review (`@cyanheads/mcp-ts-core`)
|
|
52
52
|
|
|
53
|
+
**Skill-version paradox.** If `node_modules/@cyanheads/mcp-ts-core/skills/maintenance/SKILL.md`'s `version` exceeds the one running, run Step 5 Phase A first and re-invoke `maintenance` — otherwise feature-adoption rows added in the new version silently don't surface.
|
|
54
|
+
|
|
53
55
|
If `@cyanheads/mcp-ts-core` was updated, do a deeper pass beyond what the `changelog` skill covers. The framework ships a **directory-based changelog** grouped by minor series (`.x` semver-wildcard convention) — one file per released version at `node_modules/@cyanheads/mcp-ts-core/changelog/<major.minor>.x/<version>.md`. Read only the files between old and new rather than scanning a monolithic file.
|
|
54
56
|
|
|
55
57
|
Example — `0.5.2 → 0.5.4` means reading two new version files:
|
|
@@ -66,6 +68,7 @@ Scan specifically for:
|
|
|
66
68
|
| Area | Adoption Check |
|
|
67
69
|
|:-----|:---------------|
|
|
68
70
|
| New `/errors` surface — factories, typed contracts (`errors[]` + `ctx.fail`), `httpErrorFromResponse` | Replace ad-hoc `new McpError(...)` with factories; declare `errors: [...]` on tools that surface domain-specific failure modes; route declared throws through `ctx.fail(reason, …)` so the conformance lint is happy |
|
|
71
|
+
| Existing factory choice — semantic audit | Beyond factory-vs-`new McpError`: audit each `throw factory(...)` against intent. `invalidParams` (-32602) is for malformed JSON-RPC params (wrong-shape post-Zod is rare); semantic post-shape validation should use `validationError` (-32007). `notFound` for missing entities, `conflict` for state collisions, `unauthorized` vs `forbidden` for unauth vs scope-denied. Wrong codes degrade `mcp_error_classified_code` observability and break client retry logic — fix during this pass even if not adopting contracts yet. |
|
|
69
72
|
| New utilities in `/utils` | Identify any that supersede local helper code |
|
|
70
73
|
| New context capabilities | Added `ctx.*` methods worth adopting |
|
|
71
74
|
| Provider/service APIs | Updates to `OpenRouterProvider`, `SpeechService`, `GraphService`, etc. |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# FORMAT REFERENCE — this file is never edited, never moved, never renamed.
|
|
3
3
|
#
|
|
4
4
|
# At release time, author a new per-version file at:
|
|
5
|
-
# changelog/<major.minor>.x/<version>.md (e.g. changelog/0.
|
|
5
|
+
# changelog/<major.minor>.x/<version>.md (e.g. changelog/0.6.x/0.6.6.md)
|
|
6
6
|
# using this file's frontmatter and section layout as the starting point.
|
|
7
7
|
# Set that new file's H1 to `# <version> — YYYY-MM-DD` with a concrete date.
|
|
8
8
|
|
|
@@ -27,16 +27,29 @@ breaking: false
|
|
|
27
27
|
|
|
28
28
|
Optional narrative intro — 1-3 sentences framing the release theme. Delete if not needed.
|
|
29
29
|
|
|
30
|
+
TONE: terse and fact-dense. 1-2 sentence(s) per bullet where possible —
|
|
31
|
+
name the symbol, state what changed, stop. Drop "explains how it works" prose;
|
|
32
|
+
that belongs in JSDoc, AGENTS.md, or the relevant skill. Drop ceremonial
|
|
33
|
+
framings ("This release introduces…", "fully backwards compatible:" with a
|
|
34
|
+
paragraph of justification). Prefer code/symbol names over English
|
|
35
|
+
re-explanations. If a bullet runs more than ~2 lines, split it or cut it.
|
|
36
|
+
|
|
37
|
+
WHAT TO INCLUDE: every distinct fact a reader needs to adopt or audit the
|
|
38
|
+
release — new exports, signatures, lint rule IDs, env vars, breaking
|
|
39
|
+
changes, version bumps on shipped skills. WHAT TO CUT: mechanism walkthroughs,
|
|
40
|
+
duplicate prose between Added and Changed, file-by-file test enumerations,
|
|
41
|
+
internal implementation notes. Trust the reader to read the code or the docs.
|
|
42
|
+
|
|
30
43
|
Linking issues/PRs: use full URLs so the link works everywhere (GitHub web UI,
|
|
31
44
|
npm/node_modules reads, local editors). GitHub's bare `#NN` auto-link only
|
|
32
45
|
resolves inside its own UI.
|
|
33
46
|
|
|
34
|
-
[#
|
|
35
|
-
[#
|
|
47
|
+
[#38](https://github.com/cyanheads/mcp-ts-core/issues/38) ← issue
|
|
48
|
+
[#42](https://github.com/cyanheads/mcp-ts-core/pull/42) ← PR
|
|
36
49
|
|
|
37
50
|
Only link numbers you've verified exist (via `gh issue view NN` or
|
|
38
|
-
`gh pr view NN`). Never speculate on a future number — `#
|
|
39
|
-
upcoming PR" will quietly resolve to whatever real item already owns
|
|
51
|
+
`gh pr view NN`). Never speculate on a future number — `#42` for "my
|
|
52
|
+
upcoming PR" will quietly resolve to whatever real item already owns 42,
|
|
40
53
|
and GitHub timeline previews will pull in that unrelated item's title.
|
|
41
54
|
-->
|
|
42
55
|
|