@cyanheads/mcp-ts-core 0.8.18 → 0.8.20
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 +50 -47
- package/README.md +35 -37
- package/changelog/0.8.x/0.8.19.md +33 -0
- package/changelog/0.8.x/0.8.20.md +26 -0
- package/changelog/template.md +71 -44
- package/dist/cli/init.js +12 -5
- package/dist/cli/init.js.map +1 -1
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +11 -0
- package/dist/config/index.js.map +1 -1
- package/dist/logs/combined.log +7 -12
- package/dist/logs/error.log +5 -8
- package/dist/mcp-server/transports/auth/authFactory.d.ts.map +1 -1
- package/dist/mcp-server/transports/auth/authFactory.js +4 -1
- package/dist/mcp-server/transports/auth/authFactory.js.map +1 -1
- package/dist/mcp-server/transports/auth/lib/authUtils.d.ts +3 -0
- package/dist/mcp-server/transports/auth/lib/authUtils.d.ts.map +1 -1
- package/dist/mcp-server/transports/auth/lib/authUtils.js +7 -0
- package/dist/mcp-server/transports/auth/lib/authUtils.js.map +1 -1
- package/dist/mcp-server/transports/auth/lib/checkScopes.d.ts +4 -0
- package/dist/mcp-server/transports/auth/lib/checkScopes.d.ts.map +1 -1
- package/dist/mcp-server/transports/auth/lib/checkScopes.js +7 -0
- package/dist/mcp-server/transports/auth/lib/checkScopes.js.map +1 -1
- package/dist/mcp-server/transports/auth/lib/claimParser.d.ts +5 -1
- package/dist/mcp-server/transports/auth/lib/claimParser.d.ts.map +1 -1
- package/dist/mcp-server/transports/auth/lib/claimParser.js +24 -8
- package/dist/mcp-server/transports/auth/lib/claimParser.js.map +1 -1
- package/package.json +14 -12
- package/scripts/build-changelog.ts +27 -18
- package/skills/api-auth/SKILL.md +37 -3
- package/skills/api-config/SKILL.md +2 -1
- package/skills/api-telemetry/SKILL.md +222 -0
- package/skills/api-utils/SKILL.md +3 -1
- package/skills/maintenance/SKILL.md +16 -6
- package/skills/polish-docs-meta/references/package-meta.md +1 -1
- package/skills/report-issue-framework/SKILL.md +5 -3
- package/skills/report-issue-local/SKILL.md +5 -3
- package/skills/security-pass/SKILL.md +3 -2
- package/skills/setup/SKILL.md +10 -9
- package/skills/tool-defs-analysis/SKILL.md +2 -2
- package/templates/AGENTS.md +20 -10
- package/templates/CLAUDE.md +20 -10
- package/templates/Dockerfile +2 -2
- package/templates/changelog/template.md +71 -44
- package/templates/package.json +2 -2
package/CLAUDE.md
CHANGED
|
@@ -1,9 +1,28 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Developer Protocol
|
|
2
2
|
|
|
3
|
-
**Package:** `@cyanheads/mcp-ts-core`
|
|
4
|
-
**
|
|
3
|
+
**Package:** `@cyanheads/mcp-ts-core`
|
|
4
|
+
**Version:** 0.8.20
|
|
5
|
+
**Engines:** Bun ≥1.3.0, Node ≥24.0.0
|
|
6
|
+
**MCP SDK:** `@modelcontextprotocol/sdk` ^1.29.0
|
|
7
|
+
**Zod:** ^4.4.3
|
|
8
|
+
**GitHub:** [cyanheads/mcp-ts-core](https://github.com/cyanheads/mcp-ts-core)
|
|
9
|
+
**npm:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
|
|
10
|
+
**Docker:** [ghcr.io/cyanheads/mcp-ts-core](https://ghcr.io/cyanheads/mcp-ts-core)
|
|
5
11
|
|
|
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.
|
|
12
|
+
> **Developer note:** Never assume. Read related files and docs before making changes. Read full file content for context. Never try to edit a file before reading it.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Consumers
|
|
17
|
+
|
|
18
|
+
This package serves two consumer paths. When making changes, know which audience your change affects:
|
|
19
|
+
|
|
20
|
+
| Path | On-ramp | Affected by changes to |
|
|
21
|
+
|:--|:--|:--|
|
|
22
|
+
| **Direct package import** — existing project pulls in the package | `bun add @cyanheads/mcp-ts-core` → `import { createApp, tool, z } from '@cyanheads/mcp-ts-core'` | Public API surface (`src/`) — existing consumers feel changes immediately on upgrade |
|
|
23
|
+
| **Init-scaffolded server** — fresh project bootstrapped from this repo's templates | `bunx @cyanheads/mcp-ts-core init [name]` copies `templates/` into the new directory | `templates/` — only affects newly scaffolded servers, not existing ones |
|
|
24
|
+
|
|
25
|
+
Both paths end up consuming the same public API. The init script bootstraps the project structure: starter `package.json`, `tsconfig`, `biome.json`, `vitest.config.ts`, `.env.example`, `Dockerfile`, `CLAUDE.md`/`AGENTS.md`, and example tool/resource/prompt definitions. Files in `templates/` prefixed with `_` (e.g. `_.gitignore`, `_tsconfig.json`) have the prefix stripped on copy. After init, agents should consult the `setup` skill.
|
|
7
26
|
|
|
8
27
|
---
|
|
9
28
|
|
|
@@ -13,7 +32,7 @@
|
|
|
13
32
|
- **Full-stack observability.** The framework automatically instruments every tool/resource call — OTel span, duration/payload/memory metrics, structured completion log. Use `ctx.log` for additional domain-specific logging within handlers (external API calls, multi-step operations, business events). `requestId`, `traceId`, `tenantId` auto-correlated. No `console` calls.
|
|
14
33
|
- **Unified Context.** Handlers receive `ctx` with logging (`ctx.log`), tenant-scoped storage (`ctx.state`), optional protocol capabilities (`ctx.elicit`, `ctx.sample`), and cancellation (`ctx.signal`).
|
|
15
34
|
- **Decoupled storage.** `ctx.state` for tenant-scoped KV. Never access persistence backends directly.
|
|
16
|
-
- **Canvas tokens are capabilities, not tenant-scoped state.** A `canvasId` is a 10-char URL-safe token; possession grants full read/write/drop on that canvas. Tokens are designed to be shareable — between agents in one session, and across users in single-tenant deployments (
|
|
35
|
+
- **Canvas tokens are capabilities, not tenant-scoped state.** A `canvasId` is a 10-char URL-safe token; possession grants full read/write/drop on that canvas. Tokens are designed to be shareable — between agents in one session, and across users in single-tenant deployments (see tenant resolution table under `ctx.state`). Tools should accept the token in `input` (or omit to create fresh) and return it in `output`; collaboration is opt-in via explicit token exchange.
|
|
17
36
|
- **Runtime parity.** All features work with `stdio`/`http` and Worker bundle. Guard non-portable deps via `runtimeCaps` from `@cyanheads/mcp-ts-core/utils` — a frozen capability object (`isNode`, `isBun`, `isWorkerLike`, `hasBuffer`, `hasProcess`, etc.) computed once at module load. Prefer runtime-agnostic abstractions (Hono + `@hono/mcp`, Fetch APIs).
|
|
18
37
|
- **Startup validation.** `createApp()` runs the definition linter before proceeding — errors (spec violations) throw `ConfigurationError` and block startup; warnings are logged. Also available standalone via `bun run lint:mcp` and as a devcheck step. Every diagnostic links to the rule reference in `api-linter` skill; see that skill for the full rule catalog.
|
|
19
38
|
- **Elicitation for missing input.** Use `ctx.elicit` when the client supports it.
|
|
@@ -151,10 +170,6 @@ src/
|
|
|
151
170
|
|
|
152
171
|
**File suffixes:** `.tool.ts` (standard or task), `.resource.ts`, `.prompt.ts`, `.app-tool.ts` (UI-enabled), `.app-resource.ts` (UI resource linked to app tool).
|
|
153
172
|
|
|
154
|
-
**Scaffold a new server:** `npx @cyanheads/mcp-ts-core init [name]` copies `templates/` into a new project. After running, consult the `setup` skill.
|
|
155
|
-
|
|
156
|
-
**`templates/` directory:** Scaffolding source for the init CLI. Contents are copied into new consumer servers — includes starter `package.json`, `tsconfig`, `biome.json`, `vitest.config.ts`, `.env.example`, `Dockerfile`, `CLAUDE.md`/`AGENTS.md`, and example tool/resource/prompt definitions. Files prefixed with `_` (e.g. `_.gitignore`, `_tsconfig.json`) are renamed on copy (strip `_` prefix). Changes here affect every newly scaffolded server.
|
|
157
|
-
|
|
158
173
|
---
|
|
159
174
|
|
|
160
175
|
## Adding a Tool
|
|
@@ -199,9 +214,14 @@ export const myTool = tool('my_tool', {
|
|
|
199
214
|
|
|
200
215
|
**Schema constraint:** Input/output schemas must use JSON-Schema-serializable Zod types only. The MCP SDK converts schemas to JSON Schema for `tools/list` — non-serializable types (`z.custom()`, `z.date()`, `z.transform()`, `z.bigint()`, `z.symbol()`, `z.void()`, `z.map()`, `z.set()`, `z.function()`, `z.nan()`) cause a hard runtime failure. Use structural equivalents instead (e.g., `z.string()` with `.describe('ISO 8601 date')` instead of `z.date()`). The linter validates this at startup.
|
|
201
216
|
|
|
202
|
-
**Form-client safety:** Form-based
|
|
217
|
+
**Form-client safety:** Form-based clients (MCP Inspector, web UIs) send optional fields as empty strings, not `undefined`. Don't reject with `.min(1)` on optional fields — guard for meaningful values in the handler (`if (input.dateRange?.minDate && input.dateRange?.maxDate)`). Test with both omitted and empty-value payloads. When schema-level constraints (regex/length) need to surface in the JSON Schema, wrap in a union with a `z.literal('')` sentinel: `z.union([z.literal(''), z.string().regex(...).describe(...)])` — the linter exempts the literal variant from `describe-on-fields`.
|
|
218
|
+
|
|
219
|
+
**`format`**: Maps output to MCP `content[]`. Different clients forward different surfaces to the agent — some (Claude Code) read `structuredContent` from `output`, others (Claude Desktop) read `content[]` from `format()`. `format()` is the markdown twin of `structuredContent`, not a reduced summary.
|
|
203
220
|
|
|
204
|
-
|
|
221
|
+
- **Parity is enforced.** Every terminal field in `output` must appear in `format()`'s rendered text (via sentinel injection), or startup fails with a `format-parity` lint error.
|
|
222
|
+
- **Primary fix:** render the missing field in `format()`. Use `z.discriminatedUnion` for list/detail variants — each branch is validated separately.
|
|
223
|
+
- **Escape hatch:** if the schema was over-typed for a genuinely dynamic upstream API, relax it (`z.object({}).passthrough()`) — passthrough still flows data to `structuredContent`.
|
|
224
|
+
- **Fallback:** omit `format` for JSON stringify. Additional formatters in `/utils`: `markdown()` (builder), `diffFormatter` (async), `tableFormatter`, `treeFormatter`.
|
|
205
225
|
|
|
206
226
|
**Task tools:** Add `task: true` for long-running async operations. Framework manages lifecycle: creates task → returns ID immediately → runs handler in background with `ctx.progress` → stores result/error → `ctx.signal` for cancellation. See `add-tool` skill for full example.
|
|
207
227
|
|
|
@@ -396,11 +416,7 @@ For HTTP responses from upstream APIs, use `httpErrorFromResponse(response, { se
|
|
|
396
416
|
|
|
397
417
|
**Auto-classification.** Plain `Error`, `ZodError`, and any other thrown value are caught and classified automatically. Resolution order: `McpError` code (preserved as-is) → JS constructor name (`TypeError` → `ValidationError`) → provider patterns (HTTP status codes, AWS errors, DB errors) → common message patterns → `AbortError` name → `InternalError` fallback.
|
|
398
418
|
|
|
399
|
-
**Error-path parity.** Tool errors
|
|
400
|
-
- `content[]` (read by clients like Claude Desktop) — markdown rendering of the error, with `data.recovery.hint` mirrored into the text when present
|
|
401
|
-
- `structuredContent.error` (read by clients like Claude Code) — JSON `{ code, message, data? }` carrying the error code, message, and any structured data from the thrown `McpError` or `ZodError`
|
|
402
|
-
|
|
403
|
-
`_meta.error` is **not** emitted on tool errors — error data lives on `structuredContent.error`. Resources re-throw to the SDK, which routes them through the JSON-RPC error envelope (no parity wiring needed there).
|
|
419
|
+
**Error-path parity.** Tool errors apply the same client-surface parity as success: `content[]` carries a markdown rendering with `data.recovery.hint` mirrored in, `structuredContent.error` carries `{ code, message, data? }`. `_meta.error` is not emitted. Resources re-throw to the SDK and route through the JSON-RPC error envelope (no parity wiring there).
|
|
404
420
|
|
|
405
421
|
The startup linter checks handler bodies for `prefer-mcp-error-in-handler`, `prefer-error-factory`, `preserve-cause-on-rethrow`, `no-stringify-upstream-error`, plus contract conformance (`error-contract-conformance` for undeclared non-baseline codes, `error-contract-prefer-fail` for declared codes thrown directly instead of via `ctx.fail`) — all warnings, surfaced in `bun run devcheck`.
|
|
406
422
|
|
|
@@ -423,6 +439,8 @@ Pick one convention per server and stay consistent. Verbs are typically `read`,
|
|
|
423
439
|
|
|
424
440
|
**Modes** (`MCP_AUTH_MODE`): `none` (default) | `jwt` (local secret via `MCP_AUTH_SECRET_KEY`) | `oauth` (JWKS via `OAUTH_ISSUER_URL`, `OAUTH_AUDIENCE`). See `api-auth` skill for claims, CORS, and detailed config.
|
|
425
441
|
|
|
442
|
+
**Granted scopes** are unioned from `scp`, `scope`, and `mcp_tool_scopes` JWT claims. The `mcp_tool_scopes` custom claim is the supported escape hatch for OIDC providers (Authentik, Keycloak < 26.5, Zitadel) that ignore property mappings overriding `scope` in `authorization_code` flow. For deployments where no custom claim can be injected, `MCP_AUTH_DISABLE_SCOPE_CHECKS=true` bypasses both `withRequiredScopes` and `checkScopes` after the auth-context presence check (signature/audience/issuer/expiry validation intact). A `WARNING` is logged at startup whenever the bypass is active.
|
|
443
|
+
|
|
426
444
|
---
|
|
427
445
|
|
|
428
446
|
## Configuration
|
|
@@ -434,7 +452,7 @@ Managed by `@cyanheads/mcp-ts-core`. Validated via Zod. Precedence: `createApp()
|
|
|
434
452
|
| Category | Key Variables |
|
|
435
453
|
|:---------|:-------------|
|
|
436
454
|
| Transport | `MCP_TRANSPORT_TYPE` (`stdio`\|`http`), `MCP_HTTP_PORT`, `MCP_HTTP_HOST`, `MCP_HTTP_ENDPOINT_PATH` |
|
|
437
|
-
| Auth | `MCP_AUTH_MODE`, `MCP_AUTH_SECRET_KEY`, `OAUTH_*` |
|
|
455
|
+
| Auth | `MCP_AUTH_MODE`, `MCP_AUTH_SECRET_KEY`, `MCP_AUTH_DISABLE_SCOPE_CHECKS`, `OAUTH_*` |
|
|
438
456
|
| Storage | `STORAGE_PROVIDER_TYPE` (`in-memory`\|`filesystem`\|`supabase`\|`cloudflare-r2`\|`cloudflare-kv`\|`cloudflare-d1`) |
|
|
439
457
|
| LLM | `OPENROUTER_API_KEY`, `OPENROUTER_APP_URL/NAME`, `LLM_DEFAULT_*` |
|
|
440
458
|
| Telemetry | `OTEL_ENABLED`, `OTEL_SERVICE_NAME/VERSION`, `OTEL_EXPORTER_OTLP_*` |
|
|
@@ -494,7 +512,8 @@ Each `skills/<name>/SKILL.md` carries a `metadata.version` string in its frontma
|
|
|
494
512
|
|
|
495
513
|
| Skill | Path | Covers |
|
|
496
514
|
|:------|:-----|:-------|
|
|
497
|
-
| `api-utils` | `skills/api-utils/SKILL.md` | formatting, parsing, security, network, pagination, runtime, scheduling, types, logger, requestContext, errorHandler, telemetry |
|
|
515
|
+
| `api-utils` | `skills/api-utils/SKILL.md` | formatting, parsing, security, network, pagination, runtime, scheduling, types, logger, requestContext, errorHandler, telemetry helpers (`withSpan`, `createCounter`, …) |
|
|
516
|
+
| `api-telemetry` | `skills/api-telemetry/SKILL.md` | OTel catalog: span names, metric names + attributes, completion log fields, env config, runtime support, cardinality rules |
|
|
498
517
|
| `api-services` | `skills/api-services/SKILL.md` | LLM (OpenRouter), Speech (ElevenLabs TTS, Whisper STT), Graph (CRUD, traversal, pathfinding) |
|
|
499
518
|
| `api-context` | `skills/api-context/SKILL.md` | Context interface, createContext, ContextLogger/State/Progress |
|
|
500
519
|
| `api-errors` | `skills/api-errors/SKILL.md` | McpError, JsonRpcErrorCode, error handling patterns |
|
|
@@ -535,7 +554,7 @@ Each `skills/<name>/SKILL.md` carries a `metadata.version` string in its frontma
|
|
|
535
554
|
- **JSDoc:** `@fileoverview` + `@module` required on every file
|
|
536
555
|
- **No fabricated signal:** Don't invent synthetic scores or arbitrary "confidence percentages." Surface real signal.
|
|
537
556
|
- **Builders:** `tool()`/`resource()`/`prompt()` with correct fields (`handler`, `input`, `output`, `format`, `auth`, `args`)
|
|
538
|
-
- **`format()` completeness:**
|
|
557
|
+
- **`format()` completeness:** must carry the same data as `output` (parity is lint-enforced — see Adding a Tool)
|
|
539
558
|
- **Auth:** via `auth: ['scope']` on definitions (not HOF wrapper)
|
|
540
559
|
- **Presence checks:** `ctx.elicit`/`ctx.sample` checked before use
|
|
541
560
|
- **Task tools:** use `task: true` flag
|
|
@@ -547,14 +566,6 @@ Each `skills/<name>/SKILL.md` carries a `metadata.version` string in its frontma
|
|
|
547
566
|
|
|
548
567
|
---
|
|
549
568
|
|
|
550
|
-
## Git
|
|
551
|
-
|
|
552
|
-
**Safety:** NEVER `git stash`. NEVER destructive commands (`reset --hard`, `checkout -- .`, `restore .`, `clean -f`) unless user explicitly requests. Read-only is always safe.
|
|
553
|
-
|
|
554
|
-
**Commits:** Plain `-m` strings, no heredoc/command substitution. [Conventional Commits](https://www.conventionalcommits.org/): `feat|fix|refactor|chore|docs|test|build(scope): message`. Group related changes atomically.
|
|
555
|
-
|
|
556
|
-
---
|
|
557
|
-
|
|
558
569
|
## Commands
|
|
559
570
|
|
|
560
571
|
| Command | Purpose |
|
|
@@ -576,7 +587,9 @@ After `bun update --latest`, run the `maintenance` skill to investigate changelo
|
|
|
576
587
|
|
|
577
588
|
## Changelog
|
|
578
589
|
|
|
579
|
-
Directory-based, grouped by minor series
|
|
590
|
+
Directory-based, grouped by minor series via the `.x` semver-wildcard convention. Source of truth is `changelog/<major.minor>.x/<version>.md` — one standalone file per release (e.g. `changelog/0.5.x/0.5.4.md`). Per-version files ship in the npm package so agents can read a specific version directly from `node_modules` without parsing a monolithic file.
|
|
591
|
+
|
|
592
|
+
At release time, author the per-version file with a concrete version and date, then run `bun run changelog:build` to regenerate the rollup. `changelog/template.md` is a format reference — never edited; consult it for frontmatter and section layout when scaffolding a new file. Be concise and accurate.
|
|
580
593
|
|
|
581
594
|
`CHANGELOG.md` is a **navigation index**, not a copy of bodies — each entry is a clickable header + one-line summary pulled from the per-version file's frontmatter. Regenerated by `bun run changelog:build`. Devcheck runs `changelog:check` and hard-fails on drift. Never hand-edit `CHANGELOG.md` — edit the per-version file and rerun the build.
|
|
582
595
|
|
|
@@ -584,14 +597,13 @@ Directory-based, grouped by minor series using the `.x` semver-wildcard conventi
|
|
|
584
597
|
|
|
585
598
|
```markdown
|
|
586
599
|
---
|
|
587
|
-
summary: One-line headline, ≤250 chars, no markdown # required
|
|
588
|
-
breaking: false
|
|
600
|
+
summary: "One-line headline, ≤250 chars, no markdown" # required
|
|
601
|
+
breaking: false # optional, default false
|
|
602
|
+
security: false # optional, default false
|
|
589
603
|
---
|
|
590
604
|
|
|
591
605
|
# 0.5.4 — 2026-04-20
|
|
592
606
|
|
|
593
|
-
Optional narrative intro (1-3 sentences).
|
|
594
|
-
|
|
595
607
|
## Added
|
|
596
608
|
|
|
597
609
|
- ...
|
|
@@ -601,10 +613,13 @@ Optional narrative intro (1-3 sentences).
|
|
|
601
613
|
|
|
602
614
|
| Field | Required | Purpose |
|
|
603
615
|
|:------|:---------|:--------|
|
|
604
|
-
| `summary` | yes | Rollup index line.
|
|
616
|
+
| `summary` | yes | Rollup index line. ≤250 chars, no markdown, single line. Write like a GitHub Release title. |
|
|
605
617
|
| `breaking` | no (default `false`) | Flags releases with breaking changes. Renders as `· ⚠️ Breaking` badge in the rollup. Agents running the `maintenance` skill read this to prioritize review. |
|
|
618
|
+
| `security` | no (default `false`) | Flags releases with security fixes. Renders as `· 🛡️ Security` badge in the rollup so users can triage upgrade urgency. Pairs with the `## Security` body section. |
|
|
606
619
|
|
|
607
|
-
Summary > 250 chars or malformed
|
|
620
|
+
When both flags are set, badges render in fixed order: `· ⚠️ Breaking · 🛡️ Security`. Summary > 250 chars or a malformed boolean fails `changelog:check`. Missing `summary` emits a warning and renders the rollup entry as header-only.
|
|
621
|
+
|
|
622
|
+
**Section order** (Keep a Changelog): Added, Changed, Deprecated, Removed, Fixed, Security. Include only sections with entries — don't ship empty headers.
|
|
608
623
|
|
|
609
624
|
Pre-release versions (`0.6.0-beta.1`, `0.6.0-rc.1`, etc.) are consolidated as `##`/`###` sub-headers inside the final version's per-version file (`changelog/0.6.x/0.6.0.md`) when the final ships — they share the final version's frontmatter, no separate files per pre-release.
|
|
610
625
|
|
|
@@ -612,16 +627,4 @@ Pre-release versions (`0.6.0-beta.1`, `0.6.0-rc.1`, etc.) are consolidated as `#
|
|
|
612
627
|
|
|
613
628
|
## Publishing
|
|
614
629
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
```bash
|
|
618
|
-
bun publish --access public
|
|
619
|
-
|
|
620
|
-
docker buildx build --platform linux/amd64,linux/arm64 \
|
|
621
|
-
-t ghcr.io/cyanheads/mcp-ts-core:<version> \
|
|
622
|
-
-t ghcr.io/cyanheads/mcp-ts-core:latest \
|
|
623
|
-
--push .
|
|
624
|
-
|
|
625
|
-
mcp-publisher publish
|
|
626
|
-
```
|
|
627
|
-
|
|
630
|
+
If the user requests it, run the `release-and-publish` skill — it runs the verification gate (`devcheck`, `rebuild`, `test:all`), pushes commits and tags, and publishes to every applicable destination. The full reference:
|
package/README.md
CHANGED
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
|
|
16
16
|
## What is this?
|
|
17
17
|
|
|
18
|
-
`@cyanheads/mcp-ts-core` is the infrastructure layer for TypeScript MCP servers. Install it as a dependency — don't fork it.
|
|
18
|
+
`@cyanheads/mcp-ts-core` is the infrastructure layer for TypeScript MCP servers. Install it as a dependency — don't fork it. Your agent collaborates with you to design and build the tools, resources, and prompts for your server.
|
|
19
|
+
|
|
20
|
+
The framework handles the plumbing: transports, auth, config, logging, telemetry, & more. Define your domain logic with the builders and let the framework take care of the rest.
|
|
19
21
|
|
|
20
22
|
```ts
|
|
21
23
|
import { createApp, tool, z } from '@cyanheads/mcp-ts-core';
|
|
@@ -23,15 +25,21 @@ import { createApp, tool, z } from '@cyanheads/mcp-ts-core';
|
|
|
23
25
|
const greet = tool('greet', {
|
|
24
26
|
description: 'Greet someone by name and return a personalized message.',
|
|
25
27
|
annotations: { readOnlyHint: true },
|
|
26
|
-
input: z.object({
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
input: z.object({
|
|
29
|
+
name: z.string().describe('Name of the person to greet'),
|
|
30
|
+
}),
|
|
31
|
+
output: z.object({
|
|
32
|
+
message: z.string().describe('The greeting message'),
|
|
33
|
+
}),
|
|
34
|
+
handler: async (input) => ({
|
|
35
|
+
message: `Hello, ${input.name}!`,
|
|
36
|
+
}),
|
|
29
37
|
});
|
|
30
38
|
|
|
31
39
|
await createApp({ tools: [greet] });
|
|
32
40
|
```
|
|
33
41
|
|
|
34
|
-
That's a complete MCP server. Every tool call is automatically logged with duration, payload sizes,
|
|
42
|
+
That's a complete MCP server. Every tool call is automatically logged with duration, payload sizes, and request correlation — no instrumentation code needed. `createApp()` handles config parsing, logger init, transport startup, signal handlers, and graceful shutdown.
|
|
35
43
|
|
|
36
44
|
## Quick start
|
|
37
45
|
|
|
@@ -43,7 +51,7 @@ bun install
|
|
|
43
51
|
|
|
44
52
|
You get a scaffolded project with `CLAUDE.md`, Agent Skills, and a `src/` tree ready for your tools. Infrastructure — transports, auth, storage, telemetry, lifecycle, linting — lives in `node_modules`. What's left is domain: which APIs to wrap, which workflows to expose.
|
|
45
53
|
|
|
46
|
-
Start your coding agent (Claude Code, Codex
|
|
54
|
+
Start your coding agent (i.e. Claude Code, Codex) and describe what you want. The agent knows what to do from there. The included Agent Skills cover the full cycle: `setup`, `design-mcp-server`, scaffolding, testing, `security-pass`, `release-and-publish`, `maintenance`, & more.
|
|
47
55
|
|
|
48
56
|
### What you get
|
|
49
57
|
|
|
@@ -58,7 +66,9 @@ export const search = tool('search', {
|
|
|
58
66
|
query: z.string().describe('Search query'),
|
|
59
67
|
limit: z.number().default(10).describe('Max results'),
|
|
60
68
|
}),
|
|
61
|
-
output: z.object({
|
|
69
|
+
output: z.object({
|
|
70
|
+
items: z.array(z.string()).describe('Search results'),
|
|
71
|
+
}),
|
|
62
72
|
async handler(input) {
|
|
63
73
|
const results = await doSearch(input.query, input.limit);
|
|
64
74
|
return { items: results };
|
|
@@ -73,7 +83,9 @@ import { resource, z } from '@cyanheads/mcp-ts-core';
|
|
|
73
83
|
|
|
74
84
|
export const itemData = resource('items://{itemId}', {
|
|
75
85
|
description: 'Retrieve item data by ID.',
|
|
76
|
-
params: z.object({
|
|
86
|
+
params: z.object({
|
|
87
|
+
itemId: z.string().describe('Item ID'),
|
|
88
|
+
}),
|
|
77
89
|
async handler(params, ctx) {
|
|
78
90
|
return await getItem(params.itemId);
|
|
79
91
|
},
|
|
@@ -96,32 +108,17 @@ It also works on Cloudflare Workers with `createWorkerHandler()` — same defini
|
|
|
96
108
|
|
|
97
109
|
## Features
|
|
98
110
|
|
|
99
|
-
- **Declarative definitions** — `tool()`, `resource()`, `prompt()` builders with Zod schemas
|
|
111
|
+
- **Declarative definitions** — `tool()`, `resource()`, `prompt()` builders with Zod schemas; `appTool()`/`appResource()` add interactive HTML UIs.
|
|
100
112
|
- **Unified Context** — one `ctx` for logging, tenant-scoped storage, elicitation, sampling, cancellation, and task progress.
|
|
101
|
-
- **
|
|
113
|
+
- **Auth** — `auth: ['scope']` on definitions, checked before dispatch (no wrapper code). Modes: `none`, `jwt`, or `oauth` (local secret or JWKS).
|
|
102
114
|
- **Task tools** — `task: true` for long-running ops; framework manages create/poll/progress/complete/cancel.
|
|
103
|
-
- **Definition linter** — validates names, schemas, auth scopes,
|
|
104
|
-
- **Typed error contracts** — declare `errors: [{ reason, code, when, retryable? }]`
|
|
105
|
-
- **Multi-backend storage** — `in-memory`, filesystem, Supabase, Cloudflare D1/KV/R2. Swap
|
|
115
|
+
- **Definition linter** — validates names, schemas, auth scopes, annotations, and format-parity at startup. Standalone via `lint:mcp` or devcheck.
|
|
116
|
+
- **Typed error contracts** — declare `errors: [{ reason, code, when, retryable? }]` and handlers get a typed `ctx.fail(reason, …)`. Contracts publish in `tools/list` so clients preview failure modes; the linter cross-checks the handler. Factories (`notFound()`, `httpErrorFromResponse()`, …) cover ad-hoc throws; plain `Error` auto-classifies.
|
|
117
|
+
- **Multi-backend storage** — `in-memory`, filesystem, Supabase, Cloudflare D1/KV/R2. Swap via env var; handlers don't change.
|
|
106
118
|
- **DataCanvas (optional)** — Tier 3 SQL/analytical workspace backed by DuckDB. Register tabular data from upstream APIs, run SQL across registered tables, export CSV/Parquet/JSON. Token-sharing model (opaque `canvas_id`) for multi-agent collaboration; sliding TTL + per-tenant scoping. Opt-in via `CANVAS_PROVIDER_TYPE=duckdb`; fails closed on Workers.
|
|
107
|
-
- **
|
|
108
|
-
- **
|
|
109
|
-
- **
|
|
110
|
-
- **Tiered dependencies** — parsers, OTEL SDK, Supabase, and OpenAI are optional peers. Install what you use.
|
|
111
|
-
- **Agent-first DX** — ships `CLAUDE.md` with the full exports catalog so AI agents ramp up without prompting.
|
|
112
|
-
|
|
113
|
-
### Storage Behavior Snapshot
|
|
114
|
-
|
|
115
|
-
Provider behavior is intentionally normalized at the interface, but backend limits still matter:
|
|
116
|
-
|
|
117
|
-
| Provider | Delete count accuracy | List TTL filtering | Notes |
|
|
118
|
-
|:---------|:----------------------|:-------------------|:------|
|
|
119
|
-
| `in-memory` | Exact | Exact | Volatile process memory |
|
|
120
|
-
| `filesystem` | Exact | Exact | Node/Bun only |
|
|
121
|
-
| `supabase` | Exact | Exact | Requires configured Supabase client |
|
|
122
|
-
| `cloudflare-d1` | Exact | Exact | Workers D1 binding |
|
|
123
|
-
| `cloudflare-kv` | Idempotent API success | Native/eventual | Delete cannot prove prior existence |
|
|
124
|
-
| `cloudflare-r2` | Idempotent API success | Not applied during list | Expired envelopes are removed on read |
|
|
119
|
+
- **Observability** — Pino logging + optional OpenTelemetry traces/metrics. Request correlation and tool metrics automatic.
|
|
120
|
+
- **Tiered dependencies** — parsers, OTEL SDK, Supabase, OpenAI as optional peers. Install what you use.
|
|
121
|
+
- **Agent-first DX** — ships `CLAUDE.md` / `AGENTS.md` with the codebase documented throughout Agent Skills.
|
|
125
122
|
|
|
126
123
|
## Server structure
|
|
127
124
|
|
|
@@ -181,7 +178,6 @@ See [CLAUDE.md](CLAUDE.md) for the full configuration reference.
|
|
|
181
178
|
| `prompt(name, options)` | Define a prompt with `generate(args)` |
|
|
182
179
|
| `appTool(name, options)` | Define an MCP Apps tool with auto-populated `_meta.ui` |
|
|
183
180
|
| `appResource(uriTemplate, options)` | Define an MCP Apps HTML resource with the correct MIME type and `_meta.ui` mirroring for read content |
|
|
184
|
-
| `disabledTool(def, meta)` | Mark a tool present-in-manifest but skipped at registration — clients can't invoke; landing page renders it muted with the operator-facing reason and optional hint. Compose with feature-flag conditionals at definition time. |
|
|
185
181
|
|
|
186
182
|
### Context
|
|
187
183
|
|
|
@@ -215,7 +211,7 @@ import { createMockContext } from '@cyanheads/mcp-ts-core/testing';
|
|
|
215
211
|
import { fuzzTool, fuzzResource, fuzzPrompt } from '@cyanheads/mcp-ts-core/testing/fuzz';
|
|
216
212
|
```
|
|
217
213
|
|
|
218
|
-
See [CLAUDE.md](CLAUDE.md) for the complete exports reference.
|
|
214
|
+
See [CLAUDE.md/AGENTS.md](CLAUDE.md) for the complete exports reference.
|
|
219
215
|
|
|
220
216
|
## Examples
|
|
221
217
|
|
|
@@ -261,16 +257,18 @@ Also exports `fuzzResource`, `fuzzPrompt`, `zodToArbitrary`, and `ADVERSARIAL_ST
|
|
|
261
257
|
|
|
262
258
|
## Documentation
|
|
263
259
|
|
|
264
|
-
- **[CLAUDE.md](CLAUDE.md)** — Framework reference: exports catalog, patterns, Context interface, error codes, auth, config, testing. Ships in the npm package.
|
|
265
|
-
- **[
|
|
260
|
+
- **[CLAUDE.md/AGENTS.md](CLAUDE.md)** — Framework reference: exports catalog, patterns, Context interface, error codes, auth, config, testing. Ships in the npm package.
|
|
261
|
+
- **[docs/telemetry/observability.md](docs/telemetry/observability.md)** — OpenTelemetry catalog: every span, metric, and attribute the framework emits, plus the env vars to wire export.
|
|
262
|
+
- **[docs/telemetry/dashboards.md](docs/telemetry/dashboards.md)** — Example Grafana dashboard JSON and vendor-agnostic query recipes (Datadog, New Relic, Honeycomb).
|
|
263
|
+
- **[CHANGELOG.md](CHANGELOG.md)** — Version history - Directory based for easier parsing by agents. Each entry includes a summary, migration notes, and links to commits/issues.
|
|
266
264
|
|
|
267
265
|
## Development
|
|
268
266
|
|
|
269
267
|
```bash
|
|
270
268
|
bun run rebuild # clean + build (scripts/clean.ts + scripts/build.ts)
|
|
271
|
-
bun run devcheck # lint
|
|
269
|
+
bun run devcheck # full gate: lint/format, typecheck, MCP defs, framework antipatterns, docs/skills/changelog sync, tests, audit, outdated, secrets/TODO scan
|
|
272
270
|
bun run lint:mcp # validate MCP definitions against spec
|
|
273
|
-
bun run test:all # vitest
|
|
271
|
+
bun run test:all # vitest: unit + Workers pool + integration
|
|
274
272
|
```
|
|
275
273
|
|
|
276
274
|
## Contributing
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Telemetry visualization docs ([#125](https://github.com/cyanheads/mcp-ts-core/issues/125)) — example Grafana dashboard JSON, vendor-agnostic query recipes, new `api-telemetry` skill. Engines bumped to Bun ≥1.3.0 / Node ≥24.0.0."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.8.19 — 2026-05-08
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **`docs/telemetry/dashboards.md` + `mcp-ts-core-dashboard.json`** ([#125](https://github.com/cyanheads/mcp-ts-core/issues/125)) — example Grafana dashboard (54 panels, schemaVersion 39) plus an import quickstart and equivalent query recipes for Datadog / NRQL / Honeycomb. Single `$service` template variable, `(?:@[^/]+/)?` regex strips any npm-org prefix, no publisher-specific names. `docs/telemetry/observability.md` cross-links it. Framework-source-only — `docs/` is intentionally not shipped in the npm `dist`.
|
|
12
|
+
- **`api-telemetry` skill** (v1.0) — catalog of every span, metric, attribute, completion-log field, env var, and runtime caveat the framework emits. Cross-linked from `api-utils` (which now scopes to the helper API only) and from CLAUDE.md/AGENTS.md skill index.
|
|
13
|
+
- **Changelog frontmatter `security: boolean`** — pairs with the `## Security` section to render a `🛡️ Security` badge in the rollup. Both flags render in fixed order (`· ⚠️ Breaking · 🛡️ Security`) when set. `scripts/build-changelog.ts` parses and validates `security` like `breaking` (must be literal `true`/`false`).
|
|
14
|
+
- **`init` template substitutions** — `{{MCP_SDK_VERSION}}` and `{{ZOD_VERSION}}` now resolve from the framework `package.json` `dependencies` map. Joins existing `{{PACKAGE_NAME}}` and `{{FRAMEWORK_VERSION}}`.
|
|
15
|
+
|
|
16
|
+
## Changed
|
|
17
|
+
|
|
18
|
+
- **Engines bumped:** Bun `>=1.2.0` → `>=1.3.0`, Node `>=22.0.0` → `>=24.0.0`. Mirrored in `templates/package.json` and `skills/polish-docs-meta/references/package-meta.md`.
|
|
19
|
+
- **Docker base image:** `oven/bun:1` → `oven/bun:1.3` (build + runtime stages, root and template Dockerfiles).
|
|
20
|
+
- **CLAUDE.md / AGENTS.md** — restructured: new "Consumers" section explains the package-import vs init-scaffolded paths up front; tool/format guidance condensed; standalone Git section dropped (the global protocol covers it). Now reflects 0.8.19 + Bun ≥1.3.0 / Node ≥24.0.0 in the header.
|
|
21
|
+
- **`changelog/template.md` / `templates/changelog/template.md`** — rewritten authoring guide: bold-the-symbol bullet style, Keep-a-Changelog section order, scaffolded `Deprecated` / `Removed` / `Security` sections (commented out by default).
|
|
22
|
+
- **`README.md`** — leaner feature list, reordered storage detail behind a single `node_modules` link, added cross-links to `docs/telemetry/observability.md` and `dashboards.md`.
|
|
23
|
+
- **Skill bumps:** `setup` 1.6 → 1.7 (rebrands `npx` examples to `bunx`, replaces `{{PACKAGE_NAME}}` placeholder guidance with substituted-name verification, adds `release-and-publish` to the rough progression). `maintenance` 2.0 → 2.1 — Phase C now also resyncs pristine reference files (`templates/changelog/template.md` → consumer `changelog/template.md`) on content-hash mismatch. `report-issue-framework` 1.5 → 1.6, `report-issue-local` 1.4 → 1.5 — terser issue-writing guidance, "cite cross-references once," Bun `1.3.x` examples. `api-utils` 2.1 → 2.2 — telemetry section header points readers to the new `api-telemetry` skill for the catalog.
|
|
24
|
+
- **Keywords:** added `bun`, `mcp-framework`; removed `edge`.
|
|
25
|
+
- **Dependency refresh:**
|
|
26
|
+
- `@hono/node-server` `^2.0.1` → `^2.0.2`
|
|
27
|
+
- `@cloudflare/vitest-pool-workers` `^0.16.0` → `^0.16.3`
|
|
28
|
+
- `@cloudflare/workers-types` `^4.20260506.1` → `^4.20260508.1`
|
|
29
|
+
- `@hono/otel` `^1.1.1` → `^1.1.2`
|
|
30
|
+
- `@types/node` `^25.6.0` → `^25.6.2`
|
|
31
|
+
- `openai` `^6.36.0` → `^6.37.0`
|
|
32
|
+
- `vite` `8.0.10` → `8.0.11`
|
|
33
|
+
- `fast-xml-parser` `^5.7.3` (new dev dep)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "`mcp_tool_scopes` claim union + `MCP_AUTH_DISABLE_SCOPE_CHECKS` bypass ([#128](https://github.com/cyanheads/mcp-ts-core/issues/128)) — operator escape hatches for OIDC providers that can't inject scopes into `scope`."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.8.20 — 2026-05-09
|
|
8
|
+
|
|
9
|
+
Two operator escape hatches for the standard OIDC reality. Authentik, Keycloak < 26.5, and Zitadel issue `authorization_code` tokens whose `scope` claim is fixed at `openid email profile offline_access` — property mappings can't override it, so per-tool scopes can't be injected the standard way. The framework now reads granted scopes from a 3-claim union (`scp` + `scope` + new `mcp_tool_scopes`), and ships an explicit bypass for deployments where no claim-injection path exists at all.
|
|
10
|
+
|
|
11
|
+
## Added
|
|
12
|
+
|
|
13
|
+
- **`mcp_tool_scopes` JWT claim** ([#128](https://github.com/cyanheads/mcp-ts-core/issues/128)) — operator-defined custom claim parsed alongside `scp` and `scope`. Accepts space-delimited string (`"tool:foo:read tool:bar:write"`) or array form. Empty-string entries are dropped; arrays containing any non-string entry cause the claim to be ignored entirely. Documented in the `api-auth` skill as the OIDC operator setup path.
|
|
14
|
+
- **`MCP_AUTH_DISABLE_SCOPE_CHECKS` env var** (`mcpAuthDisableScopeChecks`, default `false`) — when `true`, bypasses both `withRequiredScopes` (declared `auth: [...]`) and `checkScopes` (runtime-computed scopes inside handlers, including tenant patterns like `team:${input.teamId}:write`) after the auth-context presence check. Token signature, audience, issuer, and expiry validation remain intact. `authFactory` logs a `WARNING` whenever the bypass is active under a non-`none` auth mode. Combine with server-side ACLs — without an in-handler ACL, every authenticated user effectively has every scope.
|
|
15
|
+
- **`extractStringScopes` helper** in `claimParser.ts` — shared parser for the three claim sites; handles both array and space-delimited string forms, drops empty-string array entries, ignores arrays with non-string members.
|
|
16
|
+
|
|
17
|
+
## Changed
|
|
18
|
+
|
|
19
|
+
- **Granted scopes are now a union of `scp`, `scope`, and `mcp_tool_scopes`** — previously `scp` took precedence and `scope` was only read as a fallback. Operator-visible behavior change for tokens that populate both `scp` and `scope`: those tokens now receive the union of both claims. The Okta-only and OIDC-only cases (the common shapes) are unaffected.
|
|
20
|
+
- **`authFactory`** — OAuth-mode startup log upgraded from `debug` to `info` with claim-resolution guidance and a pointer to the bypass flag for ops without claim-injection control.
|
|
21
|
+
- **`tool-defs-analysis` skill** 1.0 → 1.1 ([#127](https://github.com/cyanheads/mcp-ts-core/issues/127)) — drops "or env vars" from the recovery-hints smell list. Env var names are actionable: the agent can name the var the user must set or rotate. Internal class names and file paths remain smells.
|
|
22
|
+
- **Skill bumps:** `api-auth` 1.0 → 1.1 (claims mapping table, OIDC operator setup section, bypass flag docs), `api-config` 1.3 → 1.4 (env-var table row), `security-pass` 1.3 → 1.4 (Axis 2 check item: bypass-in-production warning).
|
|
23
|
+
|
|
24
|
+
## Fixed
|
|
25
|
+
|
|
26
|
+
- **`extractStringScopes` no longer leaks empty-string entries** through array-form claims. Pre-change array handling preserved `['', 'real-scope']` as-is, leaving an empty entry in the granted set. Practical exploitability was bounded (required scopes are non-empty static strings, so `Set.has('')` returned `false`), but the inconsistency vs. the string-path's `.trim()` check is now closed.
|
package/changelog/template.md
CHANGED
|
@@ -1,56 +1,71 @@
|
|
|
1
1
|
---
|
|
2
|
-
# FORMAT REFERENCE —
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# Keep the double quotes around the value — unquoted YAML treats `:` (colon-space)
|
|
12
|
-
# inside the string as a key separator, which fails GitHub's strict YAML parser.
|
|
2
|
+
# FORMAT REFERENCE — do not edit. Copy this file to
|
|
3
|
+
# `changelog/<major.minor>.x/<version>.md` (e.g. `changelog/0.8.x/0.8.6.md`)
|
|
4
|
+
# to author a new release. Set that file's H1 to `# <version> — YYYY-MM-DD`
|
|
5
|
+
# with a concrete date.
|
|
6
|
+
|
|
7
|
+
# Required. One-line GitHub Release-style headline. ~250 character soft cap.
|
|
8
|
+
# Default short and scannable. Don't pad, don't stitch unrelated changes with
|
|
9
|
+
# semicolons — pick the headline. Quotes required: unquoted YAML treats `: `
|
|
10
|
+
# inside the value as a key separator and fails GitHub's strict parser.
|
|
13
11
|
summary: ""
|
|
14
12
|
|
|
15
|
-
# Set
|
|
16
|
-
# changes, config renames,
|
|
17
|
-
# Flagged as
|
|
13
|
+
# Set `true` when consumers must change code to upgrade: API removals,
|
|
14
|
+
# signature changes, config renames, behavior changes that break existing
|
|
15
|
+
# usage. Flagged as `Breaking` in the rollup.
|
|
18
16
|
breaking: false
|
|
17
|
+
|
|
18
|
+
# Set `true` if this release contains any security fix. Pairs with the
|
|
19
|
+
# `## Security` section below. Flagged as `Security` in the rollup so
|
|
20
|
+
# users can triage upgrade urgency at a glance.
|
|
21
|
+
security: false
|
|
19
22
|
---
|
|
20
23
|
|
|
21
24
|
# <version> — YYYY-MM-DD
|
|
22
25
|
|
|
23
26
|
<!--
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
and
|
|
27
|
+
AUTHORING GUIDE — applies to the new per-version file you create from this
|
|
28
|
+
template.
|
|
29
|
+
|
|
30
|
+
Audience: someone scanning release notes to decide what affects them. Lead
|
|
31
|
+
each bullet with the symbol or concept name in **bold** so they can skip
|
|
32
|
+
what's irrelevant and zoom in on what's not.
|
|
33
|
+
|
|
34
|
+
Tone: terse, fact-dense, not verbose. Default to one sentence per bullet —
|
|
35
|
+
name the symbol, state what changed, stop. Use a second sentence only when
|
|
36
|
+
it carries weight. If a bullet feels long, it is.
|
|
37
|
+
|
|
38
|
+
Cut: mechanism walkthroughs (those belong in JSDoc, AGENTS.md, or the
|
|
39
|
+
relevant skill), ceremonial framings ("This release introduces…",
|
|
40
|
+
backwards-compat paragraphs), file-by-file test enumerations, internal
|
|
41
|
+
implementation notes. Prefer code/symbol names over English re-explanations.
|
|
42
|
+
|
|
43
|
+
Narrative intro: skip by default. Add one short sentence only when the
|
|
44
|
+
release theme genuinely needs framing the bullets can't carry.
|
|
45
|
+
|
|
46
|
+
Sections: Keep a Changelog order — Added, Changed, Deprecated, Removed,
|
|
47
|
+
Fixed, Security. Include only sections with entries; delete the rest
|
|
48
|
+
(including the commented-out scaffolding below). Don't ship empty headers.
|
|
49
|
+
|
|
50
|
+
Include: every distinct fact a reader needs to adopt or audit the release —
|
|
51
|
+
new exports, signatures, lint rule IDs, env vars, breaking changes, version
|
|
52
|
+
bumps on shipped skills. Nothing more.
|
|
53
|
+
|
|
54
|
+
Links: link issues, PRs, docs, or skills where they help a reader jump to
|
|
55
|
+
context. Once per item per entry — don't re-link the same issue in summary,
|
|
56
|
+
narrative, and bullet. Skip links for inline symbol names; code spans speak
|
|
57
|
+
for themselves.
|
|
58
|
+
|
|
59
|
+
Issue/PR URLs: use full URLs. GitHub's bare `#NN` auto-link only resolves
|
|
60
|
+
inside its own UI, not in npm reads or local editors.
|
|
61
|
+
|
|
62
|
+
[#38](https://github.com/cyanheads/mcp-ts-core/issues/38) ← issue
|
|
63
|
+
[#42](https://github.com/cyanheads/mcp-ts-core/pull/42) ← PR
|
|
64
|
+
|
|
65
|
+
Verify numbers exist before linking (`gh issue view NN`, `gh pr view NN`).
|
|
66
|
+
Never speculate on a future number — `#42` for an upcoming PR silently
|
|
67
|
+
resolves to whatever real item already owns 42, and timeline previews pull
|
|
68
|
+
in that unrelated item's metadata.
|
|
54
69
|
-->
|
|
55
70
|
|
|
56
71
|
## Added
|
|
@@ -61,6 +76,18 @@ breaking: false
|
|
|
61
76
|
|
|
62
77
|
-
|
|
63
78
|
|
|
79
|
+
<!-- ## Deprecated
|
|
80
|
+
|
|
81
|
+
- -->
|
|
82
|
+
|
|
83
|
+
<!-- ## Removed
|
|
84
|
+
|
|
85
|
+
- -->
|
|
86
|
+
|
|
64
87
|
## Fixed
|
|
65
88
|
|
|
66
89
|
-
|
|
90
|
+
|
|
91
|
+
<!-- ## Security
|
|
92
|
+
|
|
93
|
+
- -->
|
package/dist/cli/init.js
CHANGED
|
@@ -66,10 +66,16 @@ function init() {
|
|
|
66
66
|
mkdirSync(dest, { recursive: true });
|
|
67
67
|
}
|
|
68
68
|
console.log(`\n Scaffolding${name ? ` ${name}` : ''} in ${dest}\n`);
|
|
69
|
+
const substitutions = {
|
|
70
|
+
PACKAGE_NAME: packageName,
|
|
71
|
+
FRAMEWORK_VERSION: PACKAGE_JSON.version,
|
|
72
|
+
MCP_SDK_VERSION: PACKAGE_JSON.dependencies?.['@modelcontextprotocol/sdk'] ?? '',
|
|
73
|
+
ZOD_VERSION: PACKAGE_JSON.dependencies?.zod ?? '',
|
|
74
|
+
};
|
|
69
75
|
const created = [];
|
|
70
76
|
const skipped = [];
|
|
71
77
|
// Step 1: Copy templates
|
|
72
|
-
copyTemplates(dest,
|
|
78
|
+
copyTemplates(dest, substitutions, created, skipped);
|
|
73
79
|
// Step 2: Copy scripts
|
|
74
80
|
copyScripts(dest, created, skipped);
|
|
75
81
|
// Step 3: Copy external skills
|
|
@@ -78,7 +84,7 @@ function init() {
|
|
|
78
84
|
printSummary(created, skipped, name);
|
|
79
85
|
}
|
|
80
86
|
// ── Template copying ──────────────────────────────────────────────────
|
|
81
|
-
function copyTemplates(dest,
|
|
87
|
+
function copyTemplates(dest, substitutions, created, skipped) {
|
|
82
88
|
const entries = walkDir(TEMPLATES_DIR);
|
|
83
89
|
for (const srcPath of entries) {
|
|
84
90
|
let relPath = relative(TEMPLATES_DIR, srcPath);
|
|
@@ -94,9 +100,10 @@ function copyTemplates(dest, name, frameworkVersion, created, skipped) {
|
|
|
94
100
|
continue;
|
|
95
101
|
}
|
|
96
102
|
mkdirSync(dirname(destPath), { recursive: true });
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
.
|
|
103
|
+
let content = readFileSync(srcPath, 'utf-8');
|
|
104
|
+
for (const [key, value] of Object.entries(substitutions)) {
|
|
105
|
+
content = content.replaceAll(`{{${key}}}`, value);
|
|
106
|
+
}
|
|
100
107
|
writeFileSync(destPath, content);
|
|
101
108
|
created.push(relPath);
|
|
102
109
|
}
|