@cyanheads/mcp-ts-core 0.9.6 → 0.9.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Package:** `@cyanheads/mcp-ts-core`
4
- **Version:** 0.9.6
4
+ **Version:** 0.9.7
5
5
  **Engines:** Bun ≥1.3.0, Node ≥24.0.0
6
6
  **MCP SDK:** `@modelcontextprotocol/sdk` ^1.29.0
7
7
  **Zod:** ^4.4.3
@@ -550,4 +550,4 @@ Badge order when both set: `· ⚠️ Breaking · 🛡️ Security`. Summary > 3
550
550
 
551
551
  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. After pushing, create a GitHub Release on the annotated tag (`gh release create v<VERSION> --verify-tag --notes-from-tag`) — no assets to attach, but the Release surfaces the tag's notes in the repo UI. **Skip the Docker build/push step** — this framework package is consumed via npm, not as a container image.
552
552
 
553
- **Tag annotation subjects must omit the version number.** GitHub prepends `v<VERSION>:` to the release title when using `--notes-from-tag`. A subject like `0.9.5 some change` renders as `v0.9.5: 0.9.5 some change`. Write the subject as just the description: `mcpbignore recursive-match fix, zod to dependencies`.
553
+ **Tag annotations render as GitHub Release bodies** via `--notes-from-tag`. They must be structured markdown never a flat comma-separated string. Subject must omit the version number (GitHub prepends `v<VERSION>:`). Body uses Keep a Changelog sections with bullets. See `changelog/template.md` for the full format reference including an example.
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  [![Framework](https://img.shields.io/badge/Built%20on-@cyanheads/mcp--ts--core-67E8F9?style=flat-square)](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
9
9
 
10
- [![Version](https://img.shields.io/badge/Version-0.9.6-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![MCP Spec](https://img.shields.io/badge/MCP%20Spec-2025--11--25-8A2BE2.svg?style=flat-square)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-11-25/changelog.mdx)
10
+ [![Version](https://img.shields.io/badge/Version-0.9.7-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![MCP Spec](https://img.shields.io/badge/MCP%20Spec-2025--11--25-8A2BE2.svg?style=flat-square)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-11-25/changelog.mdx)
11
11
 
12
12
  [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.29.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![TypeScript](https://img.shields.io/badge/TypeScript-^6.0.3-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.3.0%2B-blueviolet.svg?style=flat-square)](https://bun.sh/)
13
13
 
package/biome.json CHANGED
@@ -45,7 +45,7 @@
45
45
  "noImportCycles": "error",
46
46
  "noUnusedExpressions": "error",
47
47
  "noDeprecatedImports": "error",
48
- "noDuplicateDependencies": "warn"
48
+ "noDuplicateDependencies": "off"
49
49
  },
50
50
  "complexity": {
51
51
  "noUselessUndefined": "error"
@@ -0,0 +1,18 @@
1
+ ---
2
+ summary: "biome noDuplicateDependencies off, structured tag annotation format, release artifact verification, skills updates"
3
+ breaking: false
4
+ ---
5
+
6
+ # 0.9.7 — 2026-05-23
7
+
8
+ ## Changed
9
+
10
+ - `biome.json`: `noDuplicateDependencies` warn → off (zod intentionally in both `dependencies` and `peerDependencies`)
11
+ - `CLAUDE.md`/`AGENTS.md`: tag annotation guidance rewritten — structured markdown format with `changelog/template.md` reference
12
+ - `changelog/template.md`: tag annotation format example added (subject, prose intro, sectioned bullets, dep arrows, test footer)
13
+ - `templates/CLAUDE.md`/`templates/AGENTS.md`: install badge config updated (split `command`/`args`, `env` for API keys), tag annotation guidance added
14
+ - `templates/changelog/template.md`: tag annotation format example synced from core
15
+ - `skills/multi-server-orchestration/references/wrapup-pass.md` v1.2: structured tag annotation format replaces flat-string guidance
16
+ - `skills/release-and-publish/SKILL.md`: Step 9 — verify published artifacts are reachable (npm, MCP Registry, GH Release, GHCR)
17
+ - `skills/multi-server-orchestration/references/greenfield-buildout.md`: recommended additional phases (design gate, test quality review, field test, pre-launch sweep) and 5 new gotchas
18
+ - `skills/polish-docs-meta/references/readme.md`: agent-friendly output subsection added to Features section guidance
@@ -66,6 +66,32 @@ security: false
66
66
  Never speculate on a future number — `#42` for an upcoming PR silently
67
67
  resolves to whatever real item already owns 42, and timeline previews pull
68
68
  in that unrelated item's metadata.
69
+
70
+ TAG ANNOTATIONS — the annotated tag body renders as the GitHub Release body
71
+ via `gh release create --notes-from-tag`. The tag is a derivative of this
72
+ changelog entry — a condensed, scannable version, not a copy. Format:
73
+
74
+ <theme — omit version number, GitHub prepends it>
75
+ ← blank line
76
+ <1-2 sentence context: what this release does>
77
+ ← blank line
78
+ Dependency bumps: ← section header
79
+ ← blank line
80
+ - `@cyanheads/mcp-ts-core` ^0.9.1 → ^0.9.6 ← bullet
81
+ ← blank line
82
+ Changed: ← only sections with entries
83
+ ← blank line
84
+ - `format()` output includes `query` in text mode
85
+ ← blank line
86
+ Added:
87
+ ← blank line
88
+ - `manifest.json` scaffolded for MCPB bundle support
89
+ - Install badges (Claude Desktop, Cursor, VS Code)
90
+ ← blank line
91
+ <N> tests pass; `bun run devcheck` clean. ← footer
92
+
93
+ Never a flat comma-separated string. Always structured markdown with
94
+ sections. The tag must scan well as a rendered GitHub Release page.
69
95
  -->
70
96
 
71
97
  ## Added
@@ -0,0 +1,7 @@
1
+ {"level":40,"time":1779554652742,"env":"testing","version":"0.9.7","pid":56972,"transport":"http","requestId":"5DGIW-LONS7","timestamp":"2026-05-23T16:44:12.741Z","operation":"TransportManager.start","component":"HttpTransportSetup","msg":"MCP_ALLOWED_ORIGINS is not set — CORS is wildcard for CLI clients; browser Origin headers are restricted to loopback. Set MCP_ALLOWED_ORIGINS for production deployments accepting remote browser origins."}
2
+ {"level":40,"time":1779554654681,"env":"testing","version":"0.9.7","pid":56972,"transport":"http","requestId":"5DGIW-LONS7","timestamp":"2026-05-23T16:44:12.741Z","operation":"TransportManager.start","component":"HttpTransportSetup","sessionId":"not-a-real-session-1779554654681","msg":"Session validation failed - invalid or hijacked session"}
3
+ {"level":50,"time":1779554658688,"env":"testing","version":"0.0.0-test","pid":57052,"requestId":"KD5VX-QNDSK","timestamp":"2026-05-23T16:44:18.688Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"eca1f32c9116477ca19cd419874e3d2189fa58a29e6868d4454379f09d08c761","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"eca1f32c9116477ca19cd419874e3d2189fa58a29e6868d4454379f09d08c761","toolName":"scoped_echo","requestId":"KD5VX-QNDSK","timestamp":"2026-05-23T16:44:18.688Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:146:17)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Insufficient permissions.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:184:26)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
4
+ {"level":50,"time":1779554658696,"env":"testing","version":"0.0.0-test","pid":57052,"requestId":"0T35D-7ME6T","timestamp":"2026-05-23T16:44:18.696Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"5c8698220b504698a993704832e28f177cbcea7ed9d4ee20b1755f87ff2be9d8","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"5c8698220b504698a993704832e28f177cbcea7ed9d4ee20b1755f87ff2be9d8","toolName":"scoped_echo","requestId":"0T35D-7ME6T","timestamp":"2026-05-23T16:44:18.696Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:146:17)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Insufficient permissions.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:184:26)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
5
+ {"level":50,"time":1779554659793,"env":"testing","version":"0.9.7","pid":57092,"requestId":"710FM-RJENE","timestamp":"2026-05-23T16:44:19.792Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"710FM-RJENE","timestamp":"2026-05-23T16:44:19.792Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Missing or invalid Authorization header. Bearer scheme required.","originalStack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at authMiddleware (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/authMiddleware.js:64:19)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpTransport.js:232:22)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at cors2 (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/middleware/cors/index.js:79:11)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Missing or invalid Authorization header. Bearer scheme required."}
6
+ {"level":50,"time":1779554659808,"env":"testing","version":"0.9.7","pid":57092,"requestId":"9GLH9-PHGBC","timestamp":"2026-05-23T16:44:19.808Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"9GLH9-PHGBC","timestamp":"2026-05-23T16:44:19.808Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Token has expired.","originalStack":"McpError: Token has expired.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at handleJoseVerifyError (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/claimParser.js:72:11)\n at verify (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/strategies/jwtStrategy.js:91:13)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Token has expired.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Token has expired."}
7
+ {"level":50,"time":1779554659811,"env":"testing","version":"0.9.7","pid":57092,"requestId":"T10IM-N5OHM","timestamp":"2026-05-23T16:44:19.811Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"GET","errorData":{"path":"/mcp","method":"GET","requestId":"T10IM-N5OHM","timestamp":"2026-05-23T16:44:19.811Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Missing or invalid Authorization header. Bearer scheme required.","originalStack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at authMiddleware (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/authMiddleware.js:64:19)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpTransport.js:232:22)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at cors2 (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/middleware/cors/index.js:79:11)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Missing or invalid Authorization header. Bearer scheme required."}
@@ -0,0 +1,5 @@
1
+ {"level":50,"time":1779554658688,"env":"testing","version":"0.0.0-test","pid":57052,"requestId":"KD5VX-QNDSK","timestamp":"2026-05-23T16:44:18.688Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"eca1f32c9116477ca19cd419874e3d2189fa58a29e6868d4454379f09d08c761","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"eca1f32c9116477ca19cd419874e3d2189fa58a29e6868d4454379f09d08c761","toolName":"scoped_echo","requestId":"KD5VX-QNDSK","timestamp":"2026-05-23T16:44:18.688Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["tool:other:read"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:146:17)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Insufficient permissions.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:184:26)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
2
+ {"level":50,"time":1779554658696,"env":"testing","version":"0.0.0-test","pid":57052,"requestId":"0T35D-7ME6T","timestamp":"2026-05-23T16:44:18.696Z","operation":"HandleToolRequest","critical":false,"errorCode":-32005,"originalErrorType":"McpError","finalErrorType":"McpError","sessionId":"5c8698220b504698a993704832e28f177cbcea7ed9d4ee20b1755f87ff2be9d8","toolName":"scoped_echo","tenantId":"authz-tenant","auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"errorData":{"sessionId":"5c8698220b504698a993704832e28f177cbcea7ed9d4ee20b1755f87ff2be9d8","toolName":"scoped_echo","requestId":"0T35D-7ME6T","timestamp":"2026-05-23T16:44:18.696Z","tenantId":"authz-tenant","operation":"HandleToolRequest","auth":{"sub":"authz-user","scopes":["openid","email","profile","offline_access"],"clientId":"authz-client","tenantId":"authz-tenant","token":"[REDACTED]"},"originalErrorName":"McpError","originalMessage":"Insufficient permissions.","originalStack":"McpError: Insufficient permissions.\n at forbidden (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:84:58)\n at withRequiredScopes (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/authUtils.js:68:15)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:146:17)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Insufficient permissions.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/tools/utils/toolHandlerFactory.js:184:26)\n at executeToolHandler (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:231:34)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js:126:43)\n at processTicksAndRejections (native:7:39)","msg":"Error in tool:scoped_echo: Insufficient permissions."}
3
+ {"level":50,"time":1779554659793,"env":"testing","version":"0.9.7","pid":57092,"requestId":"710FM-RJENE","timestamp":"2026-05-23T16:44:19.792Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"710FM-RJENE","timestamp":"2026-05-23T16:44:19.792Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Missing or invalid Authorization header. Bearer scheme required.","originalStack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at authMiddleware (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/authMiddleware.js:64:19)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpTransport.js:232:22)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at cors2 (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/middleware/cors/index.js:79:11)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Missing or invalid Authorization header. Bearer scheme required."}
4
+ {"level":50,"time":1779554659808,"env":"testing","version":"0.9.7","pid":57092,"requestId":"9GLH9-PHGBC","timestamp":"2026-05-23T16:44:19.808Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"POST","errorData":{"path":"/mcp","method":"POST","requestId":"9GLH9-PHGBC","timestamp":"2026-05-23T16:44:19.808Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Token has expired.","originalStack":"McpError: Token has expired.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at handleJoseVerifyError (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/lib/claimParser.js:72:11)\n at verify (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/strategies/jwtStrategy.js:91:13)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Token has expired.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Token has expired."}
5
+ {"level":50,"time":1779554659811,"env":"testing","version":"0.9.7","pid":57092,"requestId":"T10IM-N5OHM","timestamp":"2026-05-23T16:44:19.811Z","operation":"httpErrorHandler","critical":false,"errorCode":-32006,"originalErrorType":"McpError","finalErrorType":"McpError","path":"/mcp","method":"GET","errorData":{"path":"/mcp","method":"GET","requestId":"T10IM-N5OHM","timestamp":"2026-05-23T16:44:19.811Z","operation":"httpErrorHandler","originalErrorName":"McpError","originalMessage":"Missing or invalid Authorization header. Bearer scheme required.","originalStack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at unauthorized (/Users/casey/Developer/github/mcp-ts-core/dist/types-global/errors.js:86:61)\n at authMiddleware (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/auth/authMiddleware.js:64:19)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpTransport.js:232:22)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:22:23)\n at cors2 (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/middleware/cors/index.js:79:11)\n at processTicksAndRejections (native:7:39)"},"stack":"McpError: Missing or invalid Authorization header. Bearer scheme required.\n at handleError (/Users/casey/Developer/github/mcp-ts-core/dist/utils/internal/error-handler/errorHandler.js:170:23)\n at <anonymous> (/Users/casey/Developer/github/mcp-ts-core/dist/mcp-server/transports/http/httpErrorHandler.js:59:39)\n at dispatch (/Users/casey/Developer/github/mcp-ts-core/node_modules/hono/dist/compose.js:26:25)\n at processTicksAndRejections (native:7:39)","msg":"Error in httpTransport: Missing or invalid Authorization header. Bearer scheme required."}
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyanheads/mcp-ts-core",
3
- "version": "0.9.6",
3
+ "version": "0.9.7",
4
4
  "mcpName": "io.github.cyanheads/mcp-ts-core",
5
5
  "description": "Agent-native TypeScript framework for building MCP servers. Declarative definitions with auth, multi-backend storage, OpenTelemetry, and first-class support for Bun/Node/Cloudflare Workers.",
6
6
  "main": "dist/core/index.js",
@@ -179,6 +179,24 @@ Run only after the user authorizes. Same shape as Phase 5: per-target sub-agent
179
179
 
180
180
  Version bumps live with the change that warrants them per the global protocol's git rules — don't manufacture extra commits.
181
181
 
182
+ ## Recommended additional phases
183
+
184
+ The 13-phase pattern above covers scaffold-to-ship. Experience from multi-server pipeline runs surfaces additional phases that earn their cost. Orchestrators building a prescriptive pipeline layer on top of this reference should consider inserting these:
185
+
186
+ **Design gate (after Phase 3).** A third agent per target reads ONLY `docs/design.md` (no idea.md, no skill) and gives a pass/fail verdict. The point is cold-read validation: does the design make sense on its own? Filter out noise — only flag genuine structural issues, not style preferences. If all pass, proceed to build. If any fail, spawn focused fix agents.
187
+
188
+ **Test quality review (after Phase 11/12).** Dedicated agents that audit test coverage and quality: are all tools tested? Are error paths exercised? Are edge cases covered? Build agents write tests as a secondary concern; dedicated test agents treat it as primary.
189
+
190
+ **Field test (after test quality review).** Run the `field-test` skill per target — exercise tools with real inputs, report issues. May be blocked by API key requirements or rate limits. If blocked, note it and skip. If field tests surface issues, spawn fix agents and re-field-test.
191
+
192
+ **Pre-launch sweep (before final wrap-up).** Singular-purpose verification agents per target:
193
+ 1. **Metadata sync** — descriptions aligned across package.json, manifest.json, server.json (100-char condensed), README header, GH repo description. Keywords/topics include baseline MCP terms. Versions consistent.
194
+ 2. **README accuracy** — README matches current code (tool names, parameter lists, examples, config table, feature list).
195
+ 3. **PII/secrets sweep** — no hardcoded credentials, API keys, personal emails, internal paths, or sensitive context in tracked files.
196
+ 4. **Error code semantic audit** — InvalidParams only for malformed JSON-RPC params shape; ValidationError for domain validation; NotFound for missing entities.
197
+
198
+ These agents fix, not just report. Re-run devcheck + test after each.
199
+
182
200
  ## Gotchas specific to greenfield build-out
183
201
 
184
202
  | # | Gotcha | Mitigation |
@@ -190,6 +208,11 @@ Version bumps live with the change that warrants them per the global protocol's
190
208
  | 5 | `init` defaults package name to the cwd; if the project was scaffolded inside an outer dir, the name is wrong | Verify in Phase 4 prompt: "Check the substituted package name in `package.json`, `server.json`, and the agent protocol matches the intended server name." |
191
209
  | 6 | Sub-agent creates GH repo public by default if `gh repo create` omits `--private` | Restate the scenario hard rule in every fanout that touches `gh`: "Repo must be private before any push." |
192
210
  | 7 | Wrap-up agents have been observed reporting "pushed" while the remote ref didn't actually land — the push only succeeded on a subsequent op | Verify after every Bash-git fanout: `git ls-remote --tags origin` and `gh repo view --json defaultBranchRef` per target |
211
+ | 8 | LICENSE file missing from scaffolded projects despite package.json declaring Apache-2.0 | Copy from `node_modules/@cyanheads/mcp-ts-core/LICENSE` during scaffold phase |
212
+ | 9 | Description drift across surfaces — agents set descriptions independently, creating inconsistencies across package.json, manifest.json, server.json, README, and GH repo description | Pre-launch sweep phase catches this; or define the canonical description once in docs/design.md and have agents reference it |
213
+ | 10 | `server.json` `isRequired` flags set without verifying upstream API reality | Pre-launch sweep should verify each env var's `isRequired` against actual API behavior (does it work without the key?) |
214
+ | 11 | Build agents exhaust context and leave tests half-written — finish agents inherit the gap but don't prioritize test coverage | Dedicated test quality review phase after build/finish is the backstop |
215
+ | 12 | Field-test agents can't run if the API requires a key the user hasn't provided | Check API key requirements before spawning field-test agents; skip with a note if blocked |
193
216
 
194
217
  ## Checklist
195
218
 
@@ -202,6 +225,7 @@ The orchestrator's checklist for a full N-target greenfield build:
202
225
  - [ ] Phase 1 — `docs/idea.md` authored per target
203
226
  - [ ] Phase 2 — design fanout — `docs/design.md` with Decisions Log per target
204
227
  - [ ] Phase 3 — critical review fanout — `docs/design.md` hardened per target
228
+ - [ ] **Recommended: design gate — cold-read pass/fail per target**
205
229
  - [ ] Phase 4 — setup + repo fanout — repos created private, working tree unstaged, no commits
206
230
  - [ ] Phase 5 — v0.1.0 wrap-up fanout (Bash git only) — commits + annotated tags + pushes; verified via `git log` and `git ls-remote --tags origin`
207
231
  - [ ] Phase 6 — polish docs/meta fanout against named gold-standard
@@ -211,6 +235,9 @@ The orchestrator's checklist for a full N-target greenfield build:
211
235
  - [ ] Phase 10 — build fanout — implementation + tests + green devcheck per target
212
236
  - [ ] **If any Phase 10 agent didn't finish:** Phase 11 — narrow-scope finish fanout per incomplete target
213
237
  - [ ] Phase 12 — simplify fanout — `code-simplifier` pass per target
238
+ - [ ] **Recommended: test quality review — dedicated test coverage and quality audit**
239
+ - [ ] **Recommended: field test — `field-test` skill per target (skip if API key blocker)**
240
+ - [ ] **Recommended: pre-launch sweep — metadata sync, README accuracy, PII sweep, error code audit**
214
241
  - [ ] All targets: green `bun run devcheck` and `bun run test`
215
242
  - [ ] Phase 13 — final wrap-up fanout (Bash git only) — version bump, changelog file, regenerated rollup, commit + annotated tag + push; verified
216
243
  - [ ] Final read-only verification: tags pushed, repos still private, no stray uncommitted work
@@ -4,7 +4,7 @@ description: >
4
4
  Multi-server-orchestration reference for git wrap-up passes — distilled from `git-mcp-server`'s `git_wrapup_instructions` protocol. Drives parallel verification → optional doc review → wrap-up (version bump, changelog, commit, annotated tag — Bash git, local only, no push) → roll-up across N MCP server projects. Stops at "committed and tagged locally". No push, no publish — those are separate, separately-authorized workflows.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.1"
7
+ version: "1.2"
8
8
  audience: internal
9
9
  type: reference
10
10
  ---
@@ -110,7 +110,14 @@ One sub-agent per target. The agent executes the seven acceptance criteria via B
110
110
  > 4. **Regenerate derived artifacts.** `bun run changelog:build` (rebuilds `CHANGELOG.md` rollup from per-version files); `bun run tree` (regenerates `docs/tree.md` if the file structure changed).
111
111
  > 5. **Verification gate.** `bun run devcheck` must pass against the tree being committed. `bun run test:all` if it exists, otherwise `bun run test`. Both green. Halt if not green — do NOT bypass.
112
112
  > 6. **Atomic Conventional Commits.** Version bumps ride with the change that warrants them. For a focused patch, this is ONE commit covering the work + the version bump + changelog + regenerated artifacts. Subject form: `feat: <version> — <one-line theme>` or `chore(release): v<version> — <theme>`. Plain `-m` only — no heredoc, no `Co-authored-by`, no `Generated with` trailers. No marketing adjectives.
113
- > 7. **Annotated tag** `v<version>` (`-a`, NOT lightweight) with terse message: release theme, notable changes, dep arrows in `pkg ^old ^new` form if applicable. Not a CHANGELOG copy. Length is earned.
113
+ > 7. **Annotated tag** `v<version>` (`-a`, NOT lightweight). The tag body renders as the GitHub Release page via `--notes-from-tag` it must be structured markdown, never a flat comma-separated string. Format:
114
+ > - **Subject line:** release theme — omit version number (GitHub prepends `v<VERSION>:`)
115
+ > - **Prose intro:** 1-2 sentences — what this release does, why it matters
116
+ > - **Sections:** Keep a Changelog names (Dependency bumps / Added / Changed / Fixed / Removed) — only sections with entries, each with bulleted items
117
+ > - **Dep arrows:** `pkg ^old → ^new` form, one per line
118
+ > - **Footer:** test count + devcheck status
119
+ > - **Backlinks:** link framework issues where relevant (`cyanheads/mcp-ts-core#50`)
120
+ > - Not a CHANGELOG copy — terse, scannable. No marketing adjectives. Length is earned.
114
121
  >
115
122
  > Constraints:
116
123
  > - **Bash `git` only.** Do not use `git-mcp-server` (`mcp__git-mcp-server__*`) tools — session state leaks across parallel agents in the same orchestrator session.
@@ -152,7 +159,7 @@ Then produces a consolidated report:
152
159
  | 1 | Sub-agent pushes prematurely despite "local only" instruction | Restate "Do NOT push" in the Phase 4 prompt verbatim; verify post-fanout that `git log @{u}..HEAD` shows the wrap-up commit (i.e., still ahead of origin) |
153
160
  | 2 | Version bump skipped a file (`server.json`'s per-package entries, README badge, Dockerfile OCI labels, `manifest.json`) | Phase 4 prompt enumerates every version-bearing file; the `grep -rn "<current-version>"` step catches stragglers |
154
161
  | 3 | Tag at `v<version>` already exists (leftover from failed prior run, or two orchestrator runs against the same target) | Sub-agent halts and surfaces conflict; never `git tag -d` without orchestrator authorization. Inspect with `git tag -l "v<version>"` and `git show v<version>` |
155
- | 4 | Annotated tag message bloats into a CHANGELOG copy | Restate in Phase 4 prompt: terse theme + notable changes + dep arrows in `pkg ^old ^new` form. Length is earned |
162
+ | 4 | Annotated tag message bloats into a CHANGELOG copy, or collapses into a flat comma-separated string | Phase 4 prompt has explicit structured format: subject, prose intro, sectioned bullets, dep arrows, test footer. Both extremes (too long, too flat) are format violations |
156
163
  | 5 | Marketing adjectives slip into commit/tag messages | Restate the no-marketing rule in the prompt body; orchestrator spot-checks during Phase 5 (`git log --format='%s%n%b' -1`, `git show v<version>`) |
157
164
  | 6 | Sub-agent uses `git-mcp-server` instead of Bash git | Phase 4 prompt restates Hard Rule 1 from parent SKILL.md; session-state leak across parallel agents is real |
158
165
  | 7 | Verification gate skipped because "the change is small" | Restate "Halt if not green — do NOT bypass" in the prompt; the wrap-up protocol forbids bypass and the orchestrator confirms green status in Phase 5 |
@@ -182,7 +182,7 @@ Derive all tool/resource/prompt rows directly from the actual definitions. Use t
182
182
 
183
183
  ### Features
184
184
 
185
- Two subsection groups: framework capabilities, then domain-specific capabilities. Bullet lists, not prose.
185
+ Three subsection groups: framework capabilities, domain-specific capabilities, then agent-friendly output design. Bullet lists, not prose.
186
186
 
187
187
  ```markdown
188
188
  ## Features
@@ -201,8 +201,21 @@ Acme-specific:
201
201
  - Type-safe client for the Acme v2 API
202
202
  - Automatic cleaning and simplification of API responses for agent consumption
203
203
  - Workflow tools parallelize related sub-requests under a configurable concurrency limit
204
+
205
+ Agent-friendly output:
206
+
207
+ - Provenance on every response — source labels, effective-query echo, and confidence/coverage caveats so agents can reason about trust
208
+ - Graceful partial failure — batch tools return per-item success/error rows instead of failing the request, with structured status codes and actionable next-step text
209
+ - Discriminated output contracts — typed status and source fields let callers branch on data, not string parsing
204
210
  ```
205
211
 
212
+ The **Agent-friendly output** subsection documents output-design choices that make the server work well as an AI-agent backend. Include it when the server exhibits at least two of these patterns. Write bullets grounded in the server's actual behavior — not aspirational framework capabilities. Examples of what fits:
213
+
214
+ - Provenance: source labels (`viaSource`, `source`), license/access-level fields, effective-query echo, best-effort warnings on lossy tiers
215
+ - Partial failure: per-item status in batch operations, structured error rows alongside successes, recovery hints ("Next Step" text)
216
+ - Discriminated outputs: union types on `source` or `status` fields, typed `unavailable` reasons, per-tier outcome traces (`triedTiers`)
217
+ - Response shaping: stripping upstream noise, normalizing inconsistent schemas, deduplicating nested structures
218
+
206
219
  ### Getting Started
207
220
 
208
221
  Lead with the lowest-friction option. If a public hosted instance exists, show that first. Then the **three standard install configs in order — `bunx`, `npx`, `docker run`** — followed by the HTTP one-liner quickstart, then prerequisites and install steps.
@@ -184,6 +184,17 @@ Print clickable URLs for every destination that succeeded:
184
184
 
185
185
  Skip any destination that was skipped in its step.
186
186
 
187
+ ### 9. Verify artifacts are reachable
188
+
189
+ Confirm each published artifact is actually live — don't rely on a successful push exit code alone. For each destination that succeeded:
190
+
191
+ - **npm**: `npm view <package.json#name>@<version> version` — must return the version string
192
+ - **MCP Registry**: `curl -s "https://registry.modelcontextprotocol.io/v0/servers?search=<mcpName>"` — response must include `<version>` for the `io.github.cyanheads/<repo>` entry
193
+ - **GitHub Release**: `gh release view v<VERSION> -R <OWNER>/<REPO> --json assets --jq '.assets[].name'` — must list the `.mcpb` file
194
+ - **GHCR**: fetch an anonymous bearer token, then `curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $TOKEN" "https://ghcr.io/v2/<OWNER>/<REPO>/manifests/<VERSION>"` — must return HTTP 200
195
+
196
+ If any check fails, halt and report which destination is unreachable. A successful `docker push` or `bun publish` exit code does not guarantee the artifact is queryable — registry propagation delays, auth scoping, and partial failures all exist.
197
+
187
198
  ## Checklist
188
199
 
189
200
  - [ ] Working tree clean; HEAD tagged `v<version>`
@@ -196,4 +207,5 @@ Skip any destination that was skipped in its step.
196
207
  - [ ] `bun run publish-mcp` succeeds (if `server.json` present)
197
208
  - [ ] `bun run bundle` + `gh release create --verify-tag --notes-from-tag` succeeds (if `manifest.json` present)
198
209
  - [ ] Docker buildx multi-arch push succeeds (if `Dockerfile` present)
210
+ - [ ] All published artifacts verified reachable (npm, MCP Registry, GH Release asset, GHCR manifest)
199
211
  - [ ] Deployed artifact URLs reported to the user
@@ -330,16 +330,22 @@ When you complete a skill's checklist, check the boxes and add a completion time
330
330
 
331
331
  Both install links route through HTTPS endpoints (`cursor.com/en/install-mcp` and `vscode.dev/redirect`) — GitHub-rendered markdown strips non-HTTP URL schemes from anchors, so a raw `cursor://` or `vscode:` link won't click through from github.com.
332
332
 
333
- Generate the encoded configs (replace `<PACKAGE_NAME>` and adjust the install command — `npx -y …` is the standard shape for an npm-published stdio server):
333
+ Generate the encoded configs (replace `<PACKAGE_NAME>` and add env vars for any required API keys):
334
334
 
335
335
  ```bash
336
- # Cursor: base64-encoded JSON. `command` is a single string (the full invocation), not split into command/args.
337
- echo -n '{"command":"npx -y <PACKAGE_NAME>"}' | base64
338
-
339
- # VS Code: URL-encoded JSON. Split into command + args.
340
- node -p 'encodeURIComponent(JSON.stringify({name:"<PACKAGE_NAME>",command:"npx",args:["-y","<PACKAGE_NAME>"]}))'
336
+ # Cursor: base64-encoded JSON. Split command/args, add env when keys are needed.
337
+ echo -n '{"command":"npx","args":["-y","<PACKAGE_NAME>"],"env":{"API_KEY":"your-api-key"}}' | base64
338
+ # Without env (no required keys):
339
+ echo -n '{"command":"npx","args":["-y","<PACKAGE_NAME>"]}' | base64
340
+
341
+ # VS Code: URL-encoded JSON. Same shape plus a `name` field.
342
+ node -p 'encodeURIComponent(JSON.stringify({name:"<SHORT_NAME>",command:"npx",args:["-y","<PACKAGE_NAME>"],env:{API_KEY:"your-api-key"}}))'
343
+ # Without env:
344
+ node -p 'encodeURIComponent(JSON.stringify({name:"<SHORT_NAME>",command:"npx",args:["-y","<PACKAGE_NAME>"]}))'
341
345
  ```
342
346
 
347
+ Both clients use the same `{command, args, env}` shape (matching `mcp.json` schema). VS Code adds a top-level `name` field. Omit `env` entirely when no API keys are needed — don't include empty objects or framework-only vars like `MCP_TRANSPORT_TYPE`.
348
+
343
349
  The Claude Desktop badge requires the bundle to ship with a stable filename — `bun run bundle` outputs `dist/<PACKAGE_NAME>.mcpb`, and `release-and-publish` attaches that file to the GitHub Release. `releases/latest/download/<PACKAGE_NAME>.mcpb` then redirects to the most recent release.
344
350
 
345
351
  ---
@@ -365,6 +371,8 @@ security: false # optional — true flags security fi
365
371
 
366
372
  **Section order** (Keep a Changelog): Added, Changed, Deprecated, Removed, Fixed, Security. Include only sections with entries — don't ship empty headers.
367
373
 
374
+ **Tag annotations** render as GitHub Release bodies via `--notes-from-tag`. They must be structured markdown — never a flat comma-separated string. Subject omits the version number (GitHub prepends it). See `changelog/template.md` for the full format reference.
375
+
368
376
  ---
369
377
 
370
378
  ## Imports
@@ -330,16 +330,22 @@ When you complete a skill's checklist, check the boxes and add a completion time
330
330
 
331
331
  Both install links route through HTTPS endpoints (`cursor.com/en/install-mcp` and `vscode.dev/redirect`) — GitHub-rendered markdown strips non-HTTP URL schemes from anchors, so a raw `cursor://` or `vscode:` link won't click through from github.com.
332
332
 
333
- Generate the encoded configs (replace `<PACKAGE_NAME>` and adjust the install command — `npx -y …` is the standard shape for an npm-published stdio server):
333
+ Generate the encoded configs (replace `<PACKAGE_NAME>` and add env vars for any required API keys):
334
334
 
335
335
  ```bash
336
- # Cursor: base64-encoded JSON. `command` is a single string (the full invocation), not split into command/args.
337
- echo -n '{"command":"npx -y <PACKAGE_NAME>"}' | base64
338
-
339
- # VS Code: URL-encoded JSON. Split into command + args.
340
- node -p 'encodeURIComponent(JSON.stringify({name:"<PACKAGE_NAME>",command:"npx",args:["-y","<PACKAGE_NAME>"]}))'
336
+ # Cursor: base64-encoded JSON. Split command/args, add env when keys are needed.
337
+ echo -n '{"command":"npx","args":["-y","<PACKAGE_NAME>"],"env":{"API_KEY":"your-api-key"}}' | base64
338
+ # Without env (no required keys):
339
+ echo -n '{"command":"npx","args":["-y","<PACKAGE_NAME>"]}' | base64
340
+
341
+ # VS Code: URL-encoded JSON. Same shape plus a `name` field.
342
+ node -p 'encodeURIComponent(JSON.stringify({name:"<SHORT_NAME>",command:"npx",args:["-y","<PACKAGE_NAME>"],env:{API_KEY:"your-api-key"}}))'
343
+ # Without env:
344
+ node -p 'encodeURIComponent(JSON.stringify({name:"<SHORT_NAME>",command:"npx",args:["-y","<PACKAGE_NAME>"]}))'
341
345
  ```
342
346
 
347
+ Both clients use the same `{command, args, env}` shape (matching `mcp.json` schema). VS Code adds a top-level `name` field. Omit `env` entirely when no API keys are needed — don't include empty objects or framework-only vars like `MCP_TRANSPORT_TYPE`.
348
+
343
349
  The Claude Desktop badge requires the bundle to ship with a stable filename — `bun run bundle` outputs `dist/<PACKAGE_NAME>.mcpb`, and `release-and-publish` attaches that file to the GitHub Release. `releases/latest/download/<PACKAGE_NAME>.mcpb` then redirects to the most recent release.
344
350
 
345
351
  ---
@@ -365,6 +371,8 @@ security: false # optional — true flags security fi
365
371
 
366
372
  **Section order** (Keep a Changelog): Added, Changed, Deprecated, Removed, Fixed, Security. Include only sections with entries — don't ship empty headers.
367
373
 
374
+ **Tag annotations** render as GitHub Release bodies via `--notes-from-tag`. They must be structured markdown — never a flat comma-separated string. Subject omits the version number (GitHub prepends it). See `changelog/template.md` for the full format reference.
375
+
368
376
  ---
369
377
 
370
378
  ## Imports
@@ -66,6 +66,32 @@ security: false
66
66
  Never speculate on a future number — `#42` for an upcoming PR silently
67
67
  resolves to whatever real item already owns 42, and timeline previews pull
68
68
  in that unrelated item's metadata.
69
+
70
+ TAG ANNOTATIONS — the annotated tag body renders as the GitHub Release body
71
+ via `gh release create --notes-from-tag`. The tag is a derivative of this
72
+ changelog entry — a condensed, scannable version, not a copy. Format:
73
+
74
+ <theme — omit version number, GitHub prepends it>
75
+ ← blank line
76
+ <1-2 sentence context: what this release does>
77
+ ← blank line
78
+ Dependency bumps: ← section header
79
+ ← blank line
80
+ - `@cyanheads/mcp-ts-core` ^0.9.1 → ^0.9.6 ← bullet
81
+ ← blank line
82
+ Changed: ← only sections with entries
83
+ ← blank line
84
+ - `format()` output includes `query` in text mode
85
+ ← blank line
86
+ Added:
87
+ ← blank line
88
+ - `manifest.json` scaffolded for MCPB bundle support
89
+ - Install badges (Claude Desktop, Cursor, VS Code)
90
+ ← blank line
91
+ <N> tests pass; `bun run devcheck` clean. ← footer
92
+
93
+ Never a flat comma-separated string. Always structured markdown with
94
+ sections. The tag must scan well as a rendered GitHub Release page.
69
95
  -->
70
96
 
71
97
  ## Added