# Reported issues for mcp-ts-core

Pod holds 24 of 331 problems reported by people outside the maintainer team. Issues filed by the project's own owners, members and collaborators are excluded entirely — a maintainer's release checklist is not a warning to a prospective user.

Back to [mcp-ts-core](/mcp/mcp-ts-core).

## Most discussed

### bug(tool): error structuredContent fails strict SDK clients' output-schema validation on isError

When a tool declares an `output` schema, an `isError: true` response carrying `structuredContent: { error: … }` makes a validating MCP client throw `-32602 "Structured content does not match the tool's output schema"` instead of returning the `isError` result. The client validates the error envelope against the **success** schema, which it can never satisfy.

Exposure is every tool on every consuming server: `output` is required on `ToolDefinition`, so every tool advertises an `outputSchema`, an

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/241) · 2026-06-18 · closed · 11 comments

### feat(sdk): TypeScript SDK v2 / protocol 2026-07-28 — adoption impact audit

The framework pins [`@modelcontextprotocol/sdk`](https://github.com/modelcontextprotocol/typescript-sdk) `^1.29.0` (v1, current `latest`). Upstream has split v2 into `@modelcontextprotocol/server` / `client` / `node` (GA — `2.0.0`, published 2026-07-27) and published the migration path for the **2026-07-28 protocol revision**: [support-2026-07-28.md](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/migration/support-2026-07-28.md) (prerequisite: [upgrade-to-v2.md](https://gi

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/305) · 2026-07-27 · closed · 7 comments

### feat(canvas): prevent auth-none enumeration and capacity starvation

### Use case

Related: #225, #275, #305

Under `MCP_AUTH_MODE=none`, every request intentionally resolves to the `default` tenant. A stateful HTTP session ID is not an authentication or tenant boundary, and two serving modes carry no session ID at all: stateless legacy HTTP, and every request under the per-request 2026-07-28 protocol revision. Session-derived tenancy therefore cannot promise confidentiality.

The collapsed tenant still creates two framework-level problems:

1. **Ambient enumerat

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/253) · 2026-07-02 · open · 7 comments

### bug(transport): HTTP per-request McpServer cleanup leaks ~30 KB/req on heap

### mcp-ts-core version

0.6.10 (reproduced against 0.6.16 — `src/mcp-server/transports/http/httpTransport.ts` cleanup path is unchanged between the two)

### Runtime

Bun

### Runtime version

Bun 1.3.2 (`oven/bun:1` production image)

### Transport

http (Streamable HTTP)

### OS

Debian 13 (Docker container)

### Description

The HTTP transport's per-request cleanup path — a fire-and-forget `queueMicrotask` calling `transport.close()` and `server.close()` after non-SSE responses — leaves ~30 

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/50) · 2026-04-24 · closed · 7 comments

### Error-path parity: auto-mirror `data.recovery.hint` into error `content[]` text

## Problem

The framework enforces parity on success responses via the `format-parity` linter rule: every field in `output` must appear in `format()`'s rendered `content[]`, so `structuredContent`-readers and `content[]`-readers see the same data.

**Errors have no equivalent guarantee.** Handler-thrown errors are converted at `src/mcp-server/tools/utils/toolHandlerFactory.ts:196–205`:

```ts
return {
  isError: true,
  content: [{ type: 'text', text: `Error: ${mcpError.message}` }],
  _meta: {

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/84) · 2026-04-29 · closed · 6 comments

### Audit helpfulness patterns across cyanheads MCP servers (empirical foundation)

## Goal

Catalog how existing servers express **agent-helpful augmentations** today — recovery hints, "did you mean" suggestions, next-step pointers, post-action state snapshots, partial-success reporting — to inform whether the framework should bless any of these patterns as primitives.

The point is **evidence before abstraction.** We've been considering an augmentation lifecycle (\`augment.onEmpty\` / \`augment.onSuccess\`) but it's premature without seeing what shapes actually emerge in prac

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/81) · 2026-04-29 · closed · 6 comments

### feat(tool): flatten SDK input-validation error text and move issues to error.data (follow-up to #55)

> **Status: resolved by SDK v2 — verify and close at the v2 migration (#305 Phase 1). Nothing ships in this framework.**
> The v2 `tools/call` input validator formats failures as flat `path: message` sentences joined by commas (`formatIssue` in `packages/core-internal/src/util/standardSchema.ts`), so the serialized `ZodIssue[]` blob in `content[0].text` that this issue targets is gone on v2. The structured half of the original proposal (surfacing raw issues under `_meta.error.data.issues`) is **

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/66) · 2026-04-24 · closed · 6 comments

### bug(telemetry): every tool call in a process shares one traceId — tool spans parent to a leaked boot span

### mcp-ts-core version

0.10.15

### Runtime

Bun

### Runtime version

Bun 1.3

### Transport

HTTP (Streamable HTTP)

### OS

Linux / any

### Description

Under HTTP transport with telemetry enabled, tool spans could inherit the startup span's async context and share its trace ID across process lifetime.

### Reproduction

Start an HTTP server with telemetry enabled, invoke a tool from multiple sessions, and compare emitted trace IDs.

### Actual behavior

Tool calls could be parented to a l

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/294) · 2026-07-26 · closed · 5 comments

## Most recent

### feat(exports): advertisedOutputSchema is unreachable from any export subpath, so consumers cannot snapshot advertised wire shapes

Related: #241, #232

### Use case

[0.12.0](https://github.com/cyanheads/mcp-ts-core/blob/main/changelog/0.12.x/0.12.0.md) changed the bytes a tool advertises: `inputSchema` gained `additionalProperties: false`, the dialect moved to JSON Schema 2020-12, and `outputSchema` now declares the error envelope with success fields widened to optional. Its `agent-notes` tells downstream servers that "wire-shape snapshots need re-recording" as step 7 of the upgrade.

A consumer cannot actually do that. `a

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/396) · 2026-08-25 · open · 0 comments

### bug(landing): connect card's curl snippet returns 400 — modern protocol header paired with a legacy initialize body

The landing page's `curl` tab publishes a request that fails on first paste. `connect.js` interpolates `manifest.protocol.latestVersion` into **both** the `MCP-Protocol-Version` header and the `initialize` body's `protocolVersion`. Now that `latestVersion` is `2026-07-28`, that pairs a modern header with a legacy handshake — a combination the transport explicitly rejects.

There is a second, independent defect in the same snippet: it omits `Accept`.

## Reproduction

Verified against a stock HTT

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/395) · 2026-08-25 · open · 0 comments

### bug(tool): strictening a default-mode input silently discards its .meta(), dropping anyOf from the advertised inputSchema

### mcp-ts-core version

0.12.3

### Runtime

Bun

### Runtime version

1.4.0

### Transport

http

### Description

Related: #232

`tool()` applies `.strict()` to a default-mode input. Zod's `.strict()` returns a **fresh schema instance**, and that instance is not in the metadata registry, so anything attached with `.meta()` is silently lost. The advertised `inputSchema` loses the metadata with no error, no warning, and no lint finding.

The failure is invisible in the obvious place. Registrati

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/394) · 2026-08-25 · open · 0 comments

### bug(skills): git-wrapup's version-sync list omits .claude-plugin/.codex-plugin plugin.json, so releases silently leave them behind

### mcp-ts-core version

0.12.3

### Runtime

Bun

### Runtime version

Bun 1.4.0

### Transport

N/A (release tooling)

### OS

macOS 15 (darwin arm64)

### Description

`skills/git-wrapup/SKILL.md` enumerates the files a release must re-version, and the list omits `.claude-plugin/plugin.json` and `.codex-plugin/plugin.json` — both of which declare a `version` that is expected to track `package.json`.

The list appears three times, consistently incomplete:

- the version-sync section names `pac

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/393) · 2026-08-25 · open · 0 comments

### bug(templates): multi-arch Docker build fails on linux/amd64 under Bun 1.4.0

### Description

`templates/Dockerfile` runs its build stage on the target platform. Since the template moved to `oven/bun:1.4.0`, the `linux/amd64` leg of a multi-arch `docker buildx build` on an arm64 host aborts partway through `bun run build`: Bun's JavaScriptCore runs out of memory under QEMU x86_64 emulation and dies with SIGABRT.

The same Dockerfile shape built cleanly on `oven/bun:1.3.14`, so adopting the 1.4.0 pin is what exposes it. Any server that takes the template pin and publishes

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/392) · 2026-08-25 · open · 1 comment

### bug(skills): field-test helper aborts on a stateless server — a missing Mcp-Session-Id is read as init failure

### mcp-ts-core version

0.12.3

### Runtime

Bun

### Runtime version

Bun 1.4.0

### Transport

http

### OS

macOS 15 (darwin arm64)

### Description

`skills/field-test/SKILL.md`'s `mcp_start` treats a missing `Mcp-Session-Id` response header as a fatal init failure:

```bash
local sid; sid=$(grep -i '^mcp-session-id:' "$hdr" | awk '{print $2}' | tr -d '\r\n')
if [ -z "$sid" ]; then
  echo "init failed — HTTP $code, no Mcp-Session-Id header returned" >&2
```

A server running `MCP_SESSION_MO

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/391) · 2026-08-25 · open · 0 comments

### bug(devcheck): audit classifier splits paths on U+203A, but Bun 1.4 emits ASCII '>' — every transitive advisory reads as direct

### mcp-ts-core version

0.12.3

### Runtime

Bun

### Runtime version

Bun 1.4.0

### Transport

N/A (build tooling)

### OS

macOS 15 (darwin arm64)

### Description

`classifyAuditVulns` in `scripts/devcheck.ts` decides direct-vs-transitive by looking for `›` (U+203A) in each dependency path:

```ts
const isDirect = DIRECT_DEPS.has(pkgName) || paths.some((p) => !p.includes('›'));
```

Bun 1.4.0 emits ASCII `>` as the path separator, not `›`. Every path therefore fails the `includes` check, `i

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/390) · 2026-08-25 · open · 2 comments

### bug(tool): generated error.data.reason description runs contract entries together

### mcp-ts-core version

0.12.3

### Runtime

Bun

### Runtime version

1.4.0

### Transport

stdio

### OS

macOS 26.1

### Description

`toolErrorEnvelopeSchema` in `src/mcp-server/tools/utils/toolHandlerFactory.ts` builds the advertised description for `error.data.reason` by joining each declared contract entry with a single space and then appending a trailing sentence with no separator:

```ts
.describe(`Machine-readable failure mode. Declared by this tool: ${contract
  .map((entry) => `\`${

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/389) · 2026-08-25 · open · 0 comments

### feat(linter): read truncationAllowlist from devcheck.config.json

`validateDefinitions()` accepts `LintInput.truncationAllowlist`, but `templates/scripts/lint-mcp.ts` never passes it. A consumer server that needs a legitimate `capped-list-no-truncation` exemption is therefore left with only the `MCP_LINT_TRUNCATION_ALLOWLIST` env var — and because `devcheck` spawns the linter as a child process, the var has to be duplicated across two `package.json` script entries:

```json
"devcheck": "MCP_LINT_TRUNCATION_ALLOWLIST=<tool> bun run scripts/devcheck.ts",
"lint:m

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/388) · 2026-08-25 · open · 0 comments

### bug(http): server card omits transport.sessionMode, contrary to #357's acceptance criteria

Related: #357

### Description

`buildServerCard()` never emits `sessionMode`. The SEP-1649 discovery document served at `/.well-known/mcp.json` carries `mcp_version`, `server_name`, `server_version`, `server_description`, `endpoints`, `capabilities`, `authentication`, `extensions`, `documentation`, and `generated_at` — and nothing from `manifest.transport` beyond the endpoint URL it builds from `endpointPath`.

#357 closed with an acceptance criterion that reads "the landing page **and server c

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/387) · 2026-08-25 · open · 0 comments

### bug(errors): client disconnect mid-call is reported as -32603 InternalError (FetchAborted + SdkError "Connection closed")

### mcp-ts-core version

0.12.3

### Runtime

Bun

### Runtime version

Bun 1.4.x

### Transport

HTTP (Streamable HTTP)

### OS

Linux (Docker)

### Description

When a client drops its HTTP connection while a tool call is in flight, the failure is reported as `-32603 InternalError` and logged at error level with a full stack, on two separate paths:

| Path | Where | What it produces |
|:---|:---|:---|
| Upstream fetch in progress | `fetchWithTimeout` → `abortedFailure()` (`src/utils/network/fe

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/386) · 2026-08-25 · open · 0 comments

### bug(testing): zodToArbitrary ignores .regex(), so pattern-validated inputs never reach the handler under fuzz

### Description

`arbitraryForZodString` in `src/testing/fuzz.ts` derives a string arbitrary from `format`, `minLength`, and `maxLength` only. A `.regex()` constraint is ignored, so every generated sample for a pattern-validated string field is rejected at `input.parse` before the handler runs. `fuzzTool` still reports a clean run — `crashes: []`, `leaks: []` — but the handler was never reached, so the report attests to nothing.

This is the same class as #319 and #320: a constraint `zodToArbitr

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/385) · 2026-08-25 · open · 0 comments

### bug(templates): .env.example documents MCP_SESSION_MODE default as stateful and omits auto

### mcp-ts-core version

0.12.3

### Runtime

Bun

### Description

`templates/.env.example:17` documents `MCP_SESSION_MODE` as:

```
# MCP_SESSION_MODE=stateful         # stateful | stateless (default: stateful)
```

Both halves are wrong. The enum has three members, not two, and the default is `auto`:

```ts
// src/config/index.ts:156
mcpSessionMode: z.preprocess(
  emptyStringAsUndefined,
  z.enum(['stateless', 'stateful', 'auto']).default('auto'),
),
```

`auto` resolves to `stateful` at rea

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/384) · 2026-08-25 · open · 0 comments

### bug(templates): templates/bunfig.toml is missing from the published tarball, so init scaffolds without the supply-chain guard

`templates/bunfig.toml` (added in 114edd03, tracked, under `files: ["templates/"]`) is not in the published `@cyanheads/mcp-ts-core@0.12.3` tarball, so `bunx @cyanheads/mcp-ts-core init <name>` produces a project with no `bunfig.toml` — no `minimumReleaseAge`, no Socket install scanner. Every other `templates/` file ships; only this one is dropped.

### Repro

```sh
npm pack @cyanheads/mcp-ts-core@0.12.3
tar tzf cyanheads-mcp-ts-core-0.12.3.tgz | grep templates/bunfig   # → nothing
npm pack --dr

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/383) · 2026-08-22 · open · 0 comments

### bug(templates): CLAUDE.md/AGENTS.md checklist says plugin displayName = package name, which lint:packaging rejects for scoped packages

The scaffolded agent-protocol checklist tells an agent to set the plugin manifests' display fields from the **package name**, while `lint-packaging.ts` check 10 requires the **unscoped** machine name in exactly those fields. For a scoped package the two disagree, so an agent that follows the checklist writes a manifest that then fails `devcheck`.

## Where

`templates/CLAUDE.md` and `templates/AGENTS.md`, both at lines 411–412 (identical text):

```
- [ ] `.codex-plugin/plugin.json` populated — 

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/382) · 2026-08-22 · open · 0 comments

### bug(core): logger drops every record emitted from a server's setup() hook

`composeServices` awaits the consumer's `setup(coreServices)` hook at `src/core/app.ts:395`, but `logger.initialize(...)` does not run until `src/core/app.ts:634`. `Logger.log` returns early while `!this.initialized` (`src/utils/internal/logger.ts:617`), so **every `core.logger.*` call made inside a server's `setup()` is silently discarded** — no sink, at any log level, on either transport, with no warning that a record was dropped.

Not a regression: `v0.11.0` has the same ordering (`setup` at 

[Read the thread](https://github.com/cyanheads/mcp-ts-core/issues/381) · 2026-08-22 · open · 0 comments

The remaining reports are on [the project's issue tracker](https://github.com/cyanheads/mcp-ts-core/issues).
