Skip to content

mcp ts core changelog

io.github.cyanheads/mcp-ts-core

15 changes recorded. MCP Harbor re-reads this listing from the official registry every six hours and asks the live server what it exposes, and keeps what changed between one look and the next.

  • AGENTS.md +20 −18

    https://raw.githubusercontent.com/cyanheads/mcp-ts-core/HEAD/AGENTS.md

    − **Version:** 0.13.18− **Engines:** Bun ≥1.4.0, Node ≥24.0.0− | `/mirror` | `defineMirror`, `sqliteMirrorStore`, `buildSchemaSql`, `openSqliteHandle`, `Mirror`, `MirrorStore`, `MirrorDefinition`, `SyncContext`, `SyncPage`, ... | MirrorService primitive (Tier 3, optional peer dep `better-sqlite3` on Node; `bun:sqlite` built-in on Bun); persistent self-refreshing local mirror of a bulk upstream dataset (embedded SQLite + FTS5). Node/Bun only |− **`skills`** — a folder of skill folders to serve through the SEP-2640 Skills extension, conventionally `'skills'`, the root tree a server publishes to the agents that use it (`add-skill`). A relative path resolves against the application root (`resolveAppRoot()`), the working directory only when none is found. Read once at startup with the SDK's non-strict loader: a missing or unreadable folder fails startup with a `ConfigurationError` naming the resolved path; a skill the SDK rejects is skippe− **Also available** — `landing` (`LandingConfig`, HTTP transport only: landing-page config, all fields optional), `context: { exposeStatelessSessionId }` (populate `ctx.sessionId` from the SDK's per-request token in stateless HTTP mode; default `false`), `input` (`InputHandlingOptions` — the tool-argument pre-validation switches `ignoreKeys` / `caseStyleAliases` / `coerce`; every stage on when unset, see Adding a Tool), `eventBus` (the `ServerEventBus` backing `subscriptions/listen`; defaults to − Per-request `McpServer` factory (security: SDK GHSA-345p-7cg4-v4c7). Requires `compatibility_flags = ["nodejs_compat"]` and `compatibility_date >= "2025-09-01"` in `wrangler.toml`. Only `in-memory`, `cloudflare-r2`, `cloudflare-kv`, `cloudflare-d1` storage in Workers. See `api-workers` skill for full details.− **`enrichment`** (optional): The success-path counterpart to `errors[]` — a `ZodRawShape` of agent-facing context (empty-result notices, query/filter echo, pagination totals, truncation disclosure) that must reach both client surfaces. Populate via `ctx.enrich(...)` (or `ctx.enrich.notice()` / `.total()` / `.echo()` / `.truncated()`) in the handler or service layer. The framework merges it into `structuredContent`, advertises `output.extend(enrichment)` as `outputSchema`, and mirrors it into a `− **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, a safe integer where a string is expected (`8654467` → `"8654467"`), a string spel− **Advertised vs. parsed output:** `tools/list` advertises a widened schema (success fields optional, `error` declared) so an error envelope validates in strict clients; the framework still parses success results against the strict `output` (+ `enrichment`).− Handler receives `(params, ctx)` — URI on `ctx.uri` if needed. Optional `size` (bytes) for content size metadata. Large lists must use `extractCursor`/`paginateArray` from `/utils`.− **Keys match `^[a-zA-Z0-9_.\-/]+$` and may not contain `..`** — slashes are the namespace separator. A colon (`item:123`) throws `McpError(ValidationError)` on every call, in tests and in production alike.− **Recommended path: declare a typed error contract.** Add `errors: [{ reason, code, when, recovery, retryable?, severity?, thrownBy? }]` to `tool()` / `resource()`. Handler gets `ctx.fail(reason, msg?, data?)` typed against the reason union — typos fail at compile time. Runtime auto-populates `data.reason` for observability; linter enforces conformance against the handler body. `recovery` is required (≥5 words, lint-validated) — the wire hint for its reason: when a failure whose `data.reason` na− Available factories: `invalidParams`, `invalidRequest`, `notFound`, `forbidden`, `unauthorized`, `validationError`, `conflict`, `rateLimited`, `timeout`, `serviceUnavailable`, `configurationError`, `internalError`, `serializationError`, `databaseError`, `requestCancelled`. All accept `(message, data?, options?)` where `options` is `{ cause?: unknown }`.− 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`.− **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; a handler that returns after the abort, partial result included, settles the same way) → `McpError` code (preserved as-is) → SDK `ConnectionClosed` (→ `RequestCancelled`) → engine resource-limit `RangeError` by whole message — stack overflow, maxi− **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 · request <id>)` for whichever of `data.reason` / `data.retryable` / `data.requestId` is present; the numeric code and `data.issues` stay JSON-only. The message, hint, and reason render through `escapeMarkdownInline`, so a CommonMark parse of `content[]` reads each back exactly as `structur− Managed by `@cyanheads/mcp-ts-core`. Validated via Zod. Precedence: `createApp()` overrides > env vars > `package.json` (reads `name` → `MCP_SERVER_NAME`, `version` → `MCP_SERVER_VERSION`). That manifest is the one at the application root — the nearest `package.json` above the entry module — never the launching client's working directory, and relative `logsPath`/`LOGS_DIR` values resolve against the same root.− **Tool contracts:** `runToolContract(definition, input)` from `/testing` validates input/output, invokes the handler, formats content, and returns production-shaped success/error surfaces — the declared `recovery` fill included — without transport auth, telemetry, or `data.requestId`. `toolContractSuite(definition, { success, errors })` from `/testing/vitest` registers reusable schema/handler/error-envelope conformance cases for a server's own tool definitions.+ **Version:** 0.13.22+ **Engines:** Bun ≥1.4.4, Node ≥24.0.0+ | `/mirror` | `defineMirror`, `sqliteMirrorStore`, `buildSchemaSql`, `openSqliteHandle`, `Mirror`, `MirrorStore`, `MirrorDefinition`, `SyncContext`, `SyncPage`, ... | MirrorService primitive (Tier 3, optional peer dep `better-sqlite3` on Node; `bun:sqlite` built-in on Bun ≥1.4.4); persistent self-refreshing local mirror of a bulk upstream dataset (embedded SQLite + FTS5). Node/Bun only |+ **`skills`** — a folder of skill folders to serve through the SEP-2640 Skills extension, conventionally `'skills'`, the root tree a server publishes to the agents that use it (`add-skill`). A relative path resolves against the application root (`resolveAppRoot()`) — the server's own package from any working directory, the installing project's root included — the working directory only when none is found. Read once at startup with the SDK's non-strict loader: a missing or unreadable folder fails + **Also available** — `landing` (`LandingConfig`, HTTP transport only: landing-page config, all fields optional), `context: { exposeStatelessSessionId }` (populate `ctx.sessionId` from the SDK's per-request token in stateless HTTP mode; default `false`), `input` (`InputHandlingOptions` — the tool-argument pre-validation switches `ignoreKeys` / `caseStyleAliases` / `coerce`; every stage on when unset, see Adding a Tool), `eventBus` (the `ServerEventBus` backing `subscriptions/listen`; defaults to + **Node.js floor** — on real Node.js, `createApp()` first compares `process.versions.node` with the framework's own `engines.node` (`package.json`) and, below it, fails through the startup `ConfigurationError` banner (stderr, exit 1) naming the running version and `process.execPath`, before any service, `setup()`, or transport. A prerelease is judged by its `X.Y.Z`, and a version string with no leading `X.Y.Z` is not checked. Bun, Deno, and Workers are never checked (each reports a `process.versi+ Per-request `McpServer` factory (security: SDK GHSA-345p-7cg4-v4c7). Requires `compatibility_flags = ["nodejs_compat"]` and `compatibility_date >= "2025-09-01"` in `wrangler.toml`. Only `in-memory`, `cloudflare-r2`, `cloudflare-kv`, `cloudflare-d1` storage in Workers. `DEV_MCP_AUTH_BYPASS` is honored on a Worker only when its environment is explicitly `development` or `testing`, declared by the `ENVIRONMENT` binding or by a `NODE_ENV` that reaches `process.env`; with the flag on and neither set,+ **`enrichment`** (optional): The success-path counterpart to `errors[]` — a `ZodRawShape` of agent-facing context (empty-result notices, query/filter echo, pagination totals, truncation disclosure) that must reach both client surfaces. Populate via `ctx.enrich(...)` (or `ctx.enrich.notice()` / `.total()` / `.echo()` / `.truncated()`) in the handler or service layer. The framework merges it into `structuredContent`, advertises `output.extend(enrichment)` as `outputSchema`, and mirrors it into a `+ **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, a safe integer where a string is expected (`8654467` → `"8654467"`), a string spel+ **Advertised vs. parsed output:** `tools/list` advertises a widened schema (success fields optional, `error` declared) so an error envelope validates in strict clients. The framework's own parse is the only output check, and it runs once: the handler's return against the strict `output`, then the accumulated enrichment against the `enrichment` block, merged onto the parsed value. The SDK's post-handler re-validation of `structuredContent` is passed through, so a field whose parsed value is not v+ Handler receives `(params, ctx)` — URI on `ctx.uri` if needed. Optional `size` (bytes) for content size metadata. Large lists must use `extractCursor`/`paginateArray` from `/utils`. A template's `title` names the template in `resources/templates/list`; the entries its `list()` returns never inherit it (they keep their own `title`, or clients show their `name`), while every other template field still reaches them unless an entry sets its own.+ **Template variables arrive percent-encoded** — `§ 141.61` reaches `params` as `%C2%A7%20141.61` whether the client sent it encoded or raw. `createApp({ decodeResourceParams: true })` (also on `createWorkerHandler`) decodes each matched variable once before `params` validation, with or without a `params` schema: `decodeURIComponent` for `{v}`, `{.v}`, `{/v}`, `{?v}`, `{&v}`; `decodeURI` for `{+v}`, `{#v}`, so `%2F` stays encoded there; each element of an exploded variable; `+` stays `+`. A malfo+ **Keys match `^[a-zA-Z0-9_.\-/]+$` and may not contain `..`** — slashes are the namespace separator. A colon (`item:123`) throws `McpError(ValidationError)` on every call, in tests and in production alike. On `cloudflare-kv` and `cloudflare-r2` each value is stored under `<tenantId>:<key>`, capped at 512 and 1,024 bytes: a longer key throws `McpError(ValidationError)` naming the longest key the tenant can use, before any backend call (a batch, before its first). On `filesystem` a key's last `/`-+ **Recommended path: declare a typed error contract.** Add `errors: [{ reason, code, when, recovery, retryable?, severity?, thrownBy? }]` to `tool()` / `resource()`. Handler gets `ctx.fail(reason, msg?, data?)` typed against the reason union — typos fail at compile time. Runtime auto-populates `data.reason` for observability; linter enforces conformance against the handler body. `recovery` is required (≥5 words, lint-validated) — the wire hint for its reason: when a failure whose `data.reason` na+ Available factories: `invalidParams`, `invalidRequest`, `notFound`, `forbidden`, `unauthorized`, `validationError`, `conflict`, `rateLimited`, `timeout`, `serviceUnavailable`, `configurationError`, `internalError`, `serializationError`, `databaseError`, `requestCancelled`. All accept `(message, data?, options?)` where `options` is `{ cause?: unknown; literals?: readonly string[] }` (`literals`: see Error-path parity).+ 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 `Retry-After` and the body. The captured body (`data.body` / `data.responseBody`, from it or from `fetchWithTimeout`) stays server-side: server code reads it and the failure log records it, and every client envelope leaves it out unless the call passes `exposeBody: true` (`exposeErrorBody: true` on `fetchWithTimeout`).+ **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; a handler that returns after the abort, partial result included, settles the same way) → `McpError` code (preserved as-is; a tool, prompt, or resource handler's SDK `ProtocolError` keeps its code and object `data` the same way, so a resource's `Re+ **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 · retry after … · request <id>)` for whichever of `data.reason` / `data.retryable` / `data.retryAfter` / `data.requestId` is present — `retryAfter` as given, a finite non-negative number as itself and a one-line string as a code span, any other value left out; the numeric code and `data.iss+ Managed by `@cyanheads/mcp-ts-core`. Validated via Zod. Precedence: `createApp()` overrides > env vars > `package.json` (reads `name` → `MCP_SERVER_NAME`, `version` → `MCP_SERVER_VERSION`). That manifest is the one at the application root — the nearest `package.json` above the entry module — never the launching client's working directory, and relative `logsPath`/`LOGS_DIR` values resolve against the same root. A package declaring the framework in `dependencies`, `optionalDependencies`, or `peerD+ **Tool contracts:** `runToolContract(definition, input)` from `/testing` validates input/output, invokes the handler, formats content, and returns production-shaped success/error surfaces — the declared `recovery` fill included — without transport auth, telemetry, or `data.requestId`. The one failure it rejects with instead of returning is a handler's `UrlElicitationRequiredError` (`-32042`), which production hands back to the SDK as a protocol error: the rejection is that handed-back `ProtocolE

  • server.json 1 field changed
    version
    0.13.21 0.13.22

  • server.json 1 field changed
    version
    0.13.20 0.13.21

  • server.json 1 field changed
    version
    0.13.19 0.13.20
  • server.json 1 field changed
    version
    0.13.18 0.13.19

  • AGENTS.md +22 −16

    https://raw.githubusercontent.com/cyanheads/mcp-ts-core/HEAD/AGENTS.md

    − **Version:** 0.13.15− **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1 (protocol revisions 2026-07-28 and 2025-*)− **Zod:** ^4.6.5− | `/utils` | formatting, encoding, network (`fetchWithTimeout`, `withRetry` + `deadlineMs`/`defaultIsTransient`, `createPacer`, `httpErrorFromResponse`), pagination, overflow (`outlineOnOverflow`, `OUTLINE_VARIANT`, `selectSections`, `formatOutline`), logging, runtime, telemetry, token counting, parsers†, sanitization†, scheduling† | All utilities (†optional peer deps) |− | `/linter` | `validateDefinitions`, `LintReport`, `LintDiagnostic`, `LintInput`, `LintSeverity` | Definition validation |−       lines.push(`### ${item.name}`);−       lines.push(`**ID:** ${item.id} | **Status:** ${item.status}`);−       if (item.description) lines.push(item.description);− **`enrichment`** (optional): The success-path counterpart to `errors[]` — a `ZodRawShape` of agent-facing context (empty-result notices, query/filter echo, pagination totals, truncation disclosure) that must reach both client surfaces. Populate via `ctx.enrich(...)` (or `ctx.enrich.notice()` / `.total()` / `.echo()` / `.truncated()`) in the handler or service layer. The framework merges it into `structuredContent`, advertises `output.extend(enrichment)` as `outputSchema`, and mirrors it into a `− **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 qua− **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` → `ValidationE− **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 · request <id>)` for whichever of `data.reason` / `data.retryable` / `data.requestId` 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 re− 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.− | `bun run test:all` | Release gate: `rebuild` → `test:coverage` → `test:node` → `test:worker` → `test:integration`. `test:package` and `test:leaks` are separate lanes |− Lane configs live in `tests/config/` (`vitest.integration.ts`, `vitest.worker.ts`, `vitest.worker-bundle.ts`, `vitest.package.ts`, `vitest.benchmark.ts`, `vitest.performance.ts`, `vitest.leaks.ts`, `vitest.leak-sentinels.ts`). Two configs stay at the repo root: `vitest.config.ts`, the root `projects` list a bare `vitest` invocation discovers, and `vitest.config.base.mjs`, the published `./vitest.config` export.− `release-and-publish` here: verification gate (`devcheck`, `rebuild`, `test:all`, `test:package`), merge, tag, push, `bun publish`, `bun run publish-mcp`, then a GitHub Release via `bun run release:github` — no `manifest.json` here so no assets to attach, but the Release surfaces the tag's notes with the correct `v<VERSION>: <subject>` title. **Skip the Docker build/push step** — this framework package is consumed via npm, not as a container image.+ **Version:** 0.13.18+ **MCP SDK:** `@modelcontextprotocol/server` ^2.4.0 (protocol revisions 2026-07-28 and 2025-*)+ **Zod:** ^4.6.7+ | `/utils` | formatting (`markdown()`, `escapeMarkdownInline`, `markdownLink`, `markdownImage`), encoding, network (`fetchWithTimeout`, `withRetry` + `deadlineMs`/`defaultIsTransient`, `createPacer`, `httpErrorFromResponse`), pagination, overflow (`outlineOnOverflow`, `OUTLINE_VARIANT`, `selectSections`, `formatOutline`), logging, runtime, telemetry, token counting, parsers†, sanitization†, scheduling† | All utilities (†optional peer deps) |+ | `/linter` | `validateDefinitions`, `LintReport`, `LintDiagnostic`, `LintInput`, `LintSeverity`, `validateSkillTree`, `SkillProblem` | Definition validation; strict check of a published skill tree (`lint:packaging`) |+   skills: 'skills',                 // serve the published root skills/ over MCP (SEP-2640)+ **`skills`** — a folder of skill folders to serve through the SEP-2640 Skills extension, conventionally `'skills'`, the root tree a server publishes to the agents that use it (`add-skill`). A relative path resolves against the application root (`resolveAppRoot()`), the working directory only when none is found. Read once at startup with the SDK's non-strict loader: a missing or unreadable folder fails startup with a `ConfigurationError` naming the resolved path; a skill the SDK rejects is skippe+ import { escapeMarkdownInline } from '@cyanheads/mcp-ts-core/utils';+       lines.push(`### ${escapeMarkdownInline(item.name)}`);+       lines.push(`**ID:** ${escapeMarkdownInline(item.id)} | **Status:** ${escapeMarkdownInline(item.status)}`);+       if (item.description) lines.push(escapeMarkdownInline(item.description));+ - **Upstream text renders as itself:** interpolate a string the tool did not author through `escapeMarkdownInline()` and an image or link field through `markdownImage()` / `markdownLink()` (`/utils`), so a CommonMark parse reads back the `structuredContent` value. Never defang (`https[:]//`, HTML entities). See `add-tool`.+ **`enrichment`** (optional): The success-path counterpart to `errors[]` — a `ZodRawShape` of agent-facing context (empty-result notices, query/filter echo, pagination totals, truncation disclosure) that must reach both client surfaces. Populate via `ctx.enrich(...)` (or `ctx.enrich.notice()` / `.total()` / `.echo()` / `.truncated()`) in the handler or service layer. The framework merges it into `structuredContent`, advertises `output.extend(enrichment)` as `outputSchema`, and mirrors it into a `+ **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 `string`, `integer`, or `boolean` property (`z.number()` emits `number`, which the spec excludes — use `z.int()`) statically r+ **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; a handler that returns after the abort, partial result included, settles the same way) → `McpError` code (preserved as-is) → SDK `ConnectionClosed` (→ `RequestCancelled`) → engine resource-limit `RangeError` by whole message — stack overflow, maxi+ **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 · request <id>)` for whichever of `data.reason` / `data.retryable` / `data.requestId` is present; the numeric code and `data.issues` stay JSON-only. The message, hint, and reason render through `escapeMarkdownInline`, so a CommonMark parse of `content[]` reads each back exactly as `structur+ 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: the tree a server publishes, authored with `add-skill` and served by the `createApp` `skills` option.+ | `bun run test:soak` | Rebuilds, then sends 25,000 stateless HTTP `tools/call` requests (both protocol eras, 16 concurrent) to a built-package fixture on Bun and fails when the server's post-GC heap grows more than 64 MiB across the measured rounds — catches per-request retention that every correctness lane passes (#786) |+ | `bun run test:all` | Release gate: `rebuild` → `test:coverage` → `test:node` → `test:worker` → `test:integration`. `test:package`, `test:soak`, and `test:leaks` are separate lanes |+ Lane configs live in `tests/config/` (`vitest.integration.ts`, `vitest.worker.ts`, `vitest.worker-bundle.ts`, `vitest.package.ts`, `vitest.benchmark.ts`, `vitest.performance.ts`, `vitest.leaks.ts`, `vitest.leak-sentinels.ts`, `vitest.soak.ts`). Two configs stay at the repo root: `vitest.config.ts`, the root `projects` list a bare `vitest` invocation discovers, and `vitest.config.base.mjs`, the published `./vitest.config` export.+ **Release-age window:** this repo's `bunfig.toml` and `templates/_bunfig.toml` hold installs for the same window (`minimumReleaseAge`, 1 day). Consumers exempt only `@cyanheads/mcp-ts-core`, so its dependencies still go through their guard; with the windows equal, every floor and template pin a release ships is installable the day it ships. Change one and change the other, and when the template's window changes, the release's `agent-notes` tell consumers to set theirs before they update. Assume + `release-and-publish` here: verification gate (`devcheck`, `rebuild`, `test:all`, `test:package`, `test:soak`), merge, tag, push, `bun publish`, `bun run publish-mcp`, then a GitHub Release via `bun run release:github` — no `manifest.json` here so no assets to attach, but the Release surfaces the tag's notes with the correct `v<VERSION>: <subject>` title. **Skip the Docker build/push step** — this framework package is consumed via npm, not as a container image.

  • server.json 1 field changed
    version
    0.13.16 0.13.18

  • server.json 1 field changed
    version
    0.13.15 0.13.16

  • AGENTS.md +3 −3

    https://raw.githubusercontent.com/cyanheads/mcp-ts-core/HEAD/AGENTS.md

    − **Version:** 0.13.14− **MCP SDK:** `@modelcontextprotocol/server` ^2.2.0 (protocol revisions 2026-07-28 and 2025-*)− **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 · request <id>)` for whichever of `data.reason` / `data.retryable` / `data.requestId` 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 re+ **Version:** 0.13.15+ **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1 (protocol revisions 2026-07-28 and 2025-*)+ **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 · request <id>)` for whichever of `data.reason` / `data.retryable` / `data.requestId` 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 re

  • server.json 1 field changed
    version
    0.13.14 0.13.15

  • AGENTS.md +13 −10

    https://raw.githubusercontent.com/cyanheads/mcp-ts-core/HEAD/AGENTS.md

    − **Version:** 0.13.10− **MCP SDK:** `@modelcontextprotocol/server` ^2.1.0 (protocol revisions 2026-07-28 and 2025-*)− **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-par− Opt-in domain-specific logging. Methods: `debug`, `info`, `notice`, `warning`, `error`. Auto-includes `requestId`, `traceId`, `tenantId`, `spanId`. Use `ctx.log` in handlers; global `logger` for startup/shutdown/background.− **Recommended path: declare a typed error contract.** Add `errors: [{ reason, code, when, recovery, retryable?, severity?, thrownBy? }]` to `tool()` / `resource()`. Handler gets `ctx.fail(reason, msg?, data?)` typed against the reason union — typos fail at compile time. Runtime auto-populates `data.reason` for observability; linter enforces conformance against the handler body. `recovery` is required (≥5 words, lint-validated) — the wire hint for its reason: when a failure whose `data.reason` na− **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 · request <id>)` for whichever of `data.reason` / `data.retryable` / `data.requestId` 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 re− | `bun run test` | Every root project — unit, leak-gate, compliance, smoke, fuzz, typecheck (Bun runtime) |− | `bun run test:unit` / `:smoke` / `:fuzz` / `:compliance` / `:typecheck` | One root project via `--project`. `test:typecheck` runs the `.test-d.ts` contracts, whose `@ts-expect-error` cases are the negative assertions |− | `bun run test:package` | Rebuilds, packs the tarball, and consumes it as an external project would (exports, declarations, both runtimes) |− **Every release goes through a gated release PR** — `git-wrapup`'s "Release PR mode", mode `gated`. Three separate runs, never one: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the changelog entry plus a gates section); `release-pr-review` reviews and fixes on that branch (each fix an ordinary commit on top of the stack, pushed plainly — nothing already pushed is ever rewritten, so `main` keeps the record of w+ **Version:** 0.13.14+ **MCP SDK:** `@modelcontextprotocol/server` ^2.2.0 (protocol revisions 2026-07-28 and 2025-*)+ | `/testing/apps` | `renderAppTool`, `RenderAppToolOptions`, `AppRenderReport`, `AppRenderStep`, `AppServerTarget`, `AppHostOptions` | Headless MCP Apps host that renders an app tool's `ui://` view and reports on it (optional peers `@modelcontextprotocol/client`, `@modelcontextprotocol/ext-apps`) |+ **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, a safe integer where a string is expected (`8654467` → `"8654467"`), a string spel+ Opt-in domain-specific logging. Methods: `debug`, `info`, `notice`, `warning`, `error`. Auto-includes `requestId`, `traceId`, `tenantId`, `spanId`. Each record also reaches the client as `notifications/message` — only at or above `MCP_LOG_LEVEL` (RFC 5424 order; a client's own level can only narrow it), with sensitive fields masked as `[REDACTED]` and each `Error` in the data written as `{ type, message }` only (the process log keeps its stack, code, and cause). A data key the logger writes on t+ **Recommended path: declare a typed error contract.** Add `errors: [{ reason, code, when, recovery, retryable?, severity?, thrownBy? }]` to `tool()` / `resource()`. Handler gets `ctx.fail(reason, msg?, data?)` typed against the reason union — typos fail at compile time. Runtime auto-populates `data.reason` for observability; linter enforces conformance against the handler body. `recovery` is required (≥5 words, lint-validated) — the wire hint for its reason: when a failure whose `data.reason` na+ **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 · request <id>)` for whichever of `data.reason` / `data.retryable` / `data.requestId` 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 re+ **App views:** `renderAppTool` from `/testing/apps` connects to a server as an MCP Apps client, calls an app tool, and renders its `ui://` view in `chrome-headless-shell` inside the spec's double-iframe sandbox and CSP. The report carries `initialized`, `errors`, `cspViolations`, every view↔host `messages` entry, the rendered `text`, and `screenshots`; only setup failures (missing peer, no browser, server unreachable, tool missing, UI resource absent or unreadable, a `_meta.ui.csp` entry that is+ | `bun run test` | Every root project — unit, leak-gate, compliance, smoke, contract, fuzz, typecheck (Bun runtime) |+ | `bun run test:unit` / `:smoke` / `:contract` / `:fuzz` / `:compliance` / `:typecheck` | One root project via `--project`. `test:typecheck` runs the `.test-d.ts` contracts, whose `@ts-expect-error` cases are the negative assertions |+ | `bun run test:contract -u` | Accept a client-visible change: the `contract` project pins what a client receives — the handshake, the advertised lists, and a fixed matrix of call outcomes — as committed files under `tests/contract/pins/`, and a missing or changed pin fails (root `update: 'none'`). Review the pin diff and commit it with the change; see `tests/contract/README.md` |+ | `bun run test:package` | Rebuilds, packs the tarball, and consumes it as an external project would (exports, declarations, both runtimes). Then scaffolds with the installed `init` and gates the scaffold on its own scripts: the first-run `lint:mcp` / `lint:packaging` to-do list, `devcheck --no-fix --no-audit --no-deps` against an exact per-check status map, build, and `bun run test` |+ **Every release goes through a gated release PR** — `git-wrapup`'s "Release PR mode", mode `gated`. Three separate runs, never one: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the release digest: theme line, `## Changes`, `## Gates`, changelog link last); `release-pr-review` reviews and fixes on that branch (each fix an ordinary commit on top of the stack, pushed plainly — nothing already pushed is ever rewri

  • server.json 1 field changed
    version
    0.13.13 0.13.14

  • server.json 1 field changed
    version
    0.13.12 0.13.13

  • server.json 2 fields changed
    description
    Agent-native TypeScript framework for MCP servers. Agent-native TypeScript framework for building MCP servers.
    version
    0.13.11 0.13.12

  • server.json 1 field changed
    version
    0.13.10 0.13.11