@cyanheads/mcp-ts-core 0.9.5 → 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 +4 -2
- package/README.md +1 -1
- package/biome.json +1 -1
- package/changelog/0.9.x/0.9.6.md +20 -0
- package/changelog/0.9.x/0.9.7.md +18 -0
- package/changelog/template.md +26 -0
- package/dist/logs/combined.log +7 -7
- package/dist/logs/error.log +5 -5
- package/package.json +1 -1
- package/scripts/lint-packaging.ts +62 -34
- package/skills/multi-server-orchestration/SKILL.md +2 -1
- package/skills/multi-server-orchestration/references/greenfield-buildout.md +27 -0
- package/skills/multi-server-orchestration/references/maintenance-pass.md +21 -3
- package/skills/multi-server-orchestration/references/release-and-publish-pass.md +7 -3
- package/skills/multi-server-orchestration/references/wrapup-pass.md +11 -4
- package/skills/polish-docs-meta/SKILL.md +6 -2
- package/skills/polish-docs-meta/references/readme.md +14 -1
- package/skills/release-and-publish/SKILL.md +14 -2
- package/templates/AGENTS.md +14 -6
- package/templates/CLAUDE.md +14 -6
- package/templates/changelog/template.md +26 -0
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.
|
|
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
|
|
@@ -548,4 +548,6 @@ Badge order when both set: `· ⚠️ Breaking · 🛡️ Security`. Summary > 3
|
|
|
548
548
|
|
|
549
549
|
## Publishing
|
|
550
550
|
|
|
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.
|
|
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
|
+
|
|
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
|
[](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
|
|
9
9
|
|
|
10
|
-
[](./CHANGELOG.md) [](./LICENSE) [](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-11-25/changelog.mdx)
|
|
11
11
|
|
|
12
12
|
[](https://modelcontextprotocol.io/) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
13
13
|
|
package/biome.json
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "lint-packaging validates manifest name scope and user_config fields; multi-server-orchestration and polish-docs-meta skill gaps from pipeline run 2"
|
|
3
|
+
breaking: false
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 0.9.6 — 2026-05-23
|
|
7
|
+
|
|
8
|
+
## Added
|
|
9
|
+
|
|
10
|
+
- **lint-packaging** validates `manifest.json` `name` doesn't contain an npm scope prefix (`@scope/`) — catches the scoped-name copy from `package.json` that renders in the mcpb install dialog ([#148](https://github.com/cyanheads/mcp-ts-core/issues/148))
|
|
11
|
+
- **lint-packaging** validates every `user_config` entry has `title` and `type` fields — catches the missing-field failures that `mcpb pack` would otherwise surface at release time ([#149](https://github.com/cyanheads/mcp-ts-core/issues/149))
|
|
12
|
+
- **lint-packaging** manifest-only checks (name scope, user_config fields) now run independently of `server.json`; cross-validation checks remain conditional on `server.json` presence
|
|
13
|
+
|
|
14
|
+
## Changed
|
|
15
|
+
|
|
16
|
+
- **polish-docs-meta** v2.2: cross-file consistency section expanded — scope-stripped manifest name, `user_config` field completeness, `isRequired` accuracy, description alignment across 5 surfaces, baseline `package.json` keywords/topics
|
|
17
|
+
- **multi-server-orchestration** v1.2: parent gotcha table adds `--notes-from-tag` + `--repo` incompatibility
|
|
18
|
+
- **maintenance-pass** v1.2: pre-flight checks for `publish-mcp` script and `manifest.json` existence; Phase 3.5 double-check/normalization pass; gotchas for `internal`-audience skill sync, scoped manifest name, missing `user_config` fields
|
|
19
|
+
- **release-and-publish-pass** v1.3: Phase 5 adds MCPB bundle step (`bun run bundle` + `gh release create` with `.mcpb` asset); gotchas for `--notes-from-tag`+`--repo`, post-version tag movement, `user_config` field validation
|
|
20
|
+
- **wrapup-pass** v1.1: Phase 4 version bump step notes manifest name scope check
|
|
@@ -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
|
package/changelog/template.md
CHANGED
|
@@ -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
|
package/dist/logs/combined.log
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
{"level":40,"time":
|
|
2
|
-
{"level":40,"time":
|
|
3
|
-
{"level":50,"time":
|
|
4
|
-
{"level":50,"time":
|
|
5
|
-
{"level":50,"time":
|
|
6
|
-
{"level":50,"time":
|
|
7
|
-
{"level":50,"time":
|
|
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."}
|
package/dist/logs/error.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
{"level":50,"time":
|
|
2
|
-
{"level":50,"time":
|
|
3
|
-
{"level":50,"time":
|
|
4
|
-
{"level":50,"time":
|
|
5
|
-
{"level":50,"time":
|
|
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."}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyanheads/mcp-ts-core",
|
|
3
|
-
"version": "0.9.
|
|
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",
|
|
@@ -8,12 +8,16 @@
|
|
|
8
8
|
* `npm run lint:packaging`.
|
|
9
9
|
*
|
|
10
10
|
* Checks:
|
|
11
|
-
* 1.
|
|
11
|
+
* 1. Manifest `name` must not contain a scope prefix (`@scope/`).
|
|
12
|
+
* 2. Every `user_config` entry must include `title` and `type` fields.
|
|
13
|
+
* 3. Every `${user_config.X}` reference in manifest `mcp_config.env` must
|
|
12
14
|
* appear in server.json stdio `environmentVariables[]` (the registry
|
|
13
15
|
* advertises the configurable knob the bundle surfaces).
|
|
14
|
-
*
|
|
16
|
+
* 4. Every required stdio env var in server.json (no default) must appear
|
|
15
17
|
* as a key in manifest `mcp_config.env` (the bundle can receive it).
|
|
16
18
|
*
|
|
19
|
+
* Checks 1–2 run with `manifest.json` alone; 3–4 require `server.json`.
|
|
20
|
+
*
|
|
17
21
|
* Skips cleanly when `manifest.json` is absent — consumers who deleted it for
|
|
18
22
|
* an HTTP-only deploy should not fail this check.
|
|
19
23
|
*
|
|
@@ -37,9 +41,16 @@ interface ServerJson {
|
|
|
37
41
|
packages?: ServerJsonPackage[];
|
|
38
42
|
}
|
|
39
43
|
|
|
44
|
+
interface ManifestUserConfigEntry {
|
|
45
|
+
title?: unknown;
|
|
46
|
+
type?: unknown;
|
|
47
|
+
[key: string]: unknown;
|
|
48
|
+
}
|
|
49
|
+
|
|
40
50
|
interface Manifest {
|
|
51
|
+
name?: string;
|
|
41
52
|
server?: { mcp_config?: { env?: Record<string, string> } };
|
|
42
|
-
user_config?: Record<string,
|
|
53
|
+
user_config?: Record<string, ManifestUserConfigEntry>;
|
|
43
54
|
}
|
|
44
55
|
|
|
45
56
|
const USER_CONFIG_REF = /^\$\{user_config\.([\w-]+)\}$/;
|
|
@@ -67,42 +78,59 @@ function main(): void {
|
|
|
67
78
|
process.exit(1);
|
|
68
79
|
}
|
|
69
80
|
|
|
70
|
-
const serverJson = tryReadJson<ServerJson>(resolve('server.json'));
|
|
71
|
-
if (!serverJson) {
|
|
72
|
-
console.log('No server.json — skipping cross-validation.');
|
|
73
|
-
process.exit(0);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const manifestEnv = manifest.server?.mcp_config?.env ?? {};
|
|
77
|
-
const manifestEnvKeys = new Set(Object.keys(manifestEnv));
|
|
78
|
-
|
|
79
|
-
const manifestUserConfigKeys = new Set(
|
|
80
|
-
Object.entries(manifestEnv)
|
|
81
|
-
.filter(([, v]) => typeof v === 'string' && USER_CONFIG_REF.test(v))
|
|
82
|
-
.map(([k]) => k),
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
const stdioEnvVars = (serverJson.packages ?? [])
|
|
86
|
-
.filter((p) => p.transport?.type === 'stdio')
|
|
87
|
-
.flatMap((p) => p.environmentVariables ?? []);
|
|
88
|
-
const stdioEnvNames = new Set(stdioEnvVars.map((v) => v.name));
|
|
89
|
-
const requiredStdioEnvNames = new Set(
|
|
90
|
-
stdioEnvVars.filter((v) => v.isRequired === true && v.default == null).map((v) => v.name),
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
const missingInServerJson = [...manifestUserConfigKeys].filter((k) => !stdioEnvNames.has(k));
|
|
94
|
-
const missingInManifest = [...requiredStdioEnvNames].filter((k) => !manifestEnvKeys.has(k));
|
|
95
|
-
|
|
96
81
|
const errors: string[] = [];
|
|
97
|
-
|
|
82
|
+
|
|
83
|
+
if (manifest.name?.includes('/')) {
|
|
98
84
|
errors.push(
|
|
99
|
-
`manifest.json
|
|
85
|
+
`manifest.json "name" contains a scope prefix ("${manifest.name}") — use the bare package name (e.g. "${manifest.name.split('/').pop()}")`,
|
|
100
86
|
);
|
|
101
87
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
88
|
+
|
|
89
|
+
const userConfig = manifest.user_config ?? {};
|
|
90
|
+
for (const [key, entry] of Object.entries(userConfig)) {
|
|
91
|
+
if (typeof entry !== 'object' || entry === null) continue;
|
|
92
|
+
const missing = (['title', 'type'] as const).filter(
|
|
93
|
+
(f) => typeof entry[f] !== 'string' || (entry[f] as string).length === 0,
|
|
94
|
+
);
|
|
95
|
+
if (missing.length > 0) {
|
|
96
|
+
errors.push(
|
|
97
|
+
`manifest.json user_config["${key}"] is missing required field(s): ${missing.join(', ')} — mcpb pack will reject this`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const serverJson = tryReadJson<ServerJson>(resolve('server.json'));
|
|
103
|
+
if (serverJson) {
|
|
104
|
+
const manifestEnv = manifest.server?.mcp_config?.env ?? {};
|
|
105
|
+
const manifestEnvKeys = new Set(Object.keys(manifestEnv));
|
|
106
|
+
|
|
107
|
+
const manifestUserConfigKeys = new Set(
|
|
108
|
+
Object.entries(manifestEnv)
|
|
109
|
+
.filter(([, v]) => typeof v === 'string' && USER_CONFIG_REF.test(v))
|
|
110
|
+
.map(([k]) => k),
|
|
105
111
|
);
|
|
112
|
+
|
|
113
|
+
const stdioEnvVars = (serverJson.packages ?? [])
|
|
114
|
+
.filter((p) => p.transport?.type === 'stdio')
|
|
115
|
+
.flatMap((p) => p.environmentVariables ?? []);
|
|
116
|
+
const stdioEnvNames = new Set(stdioEnvVars.map((v) => v.name));
|
|
117
|
+
const requiredStdioEnvNames = new Set(
|
|
118
|
+
stdioEnvVars.filter((v) => v.isRequired === true && v.default == null).map((v) => v.name),
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const missingInServerJson = [...manifestUserConfigKeys].filter((k) => !stdioEnvNames.has(k));
|
|
122
|
+
const missingInManifest = [...requiredStdioEnvNames].filter((k) => !manifestEnvKeys.has(k));
|
|
123
|
+
|
|
124
|
+
if (missingInServerJson.length > 0) {
|
|
125
|
+
errors.push(
|
|
126
|
+
`manifest.json references user_config env var(s) not advertised in server.json stdio environmentVariables[]: ${missingInServerJson.join(', ')}`,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
if (missingInManifest.length > 0) {
|
|
130
|
+
errors.push(
|
|
131
|
+
`server.json declares required stdio env var(s) without default missing from manifest.json mcp_config.env: ${missingInManifest.join(', ')}`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
106
134
|
}
|
|
107
135
|
|
|
108
136
|
if (errors.length === 0) {
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Orchestrate parallel sub-agent fanouts across one or more MCP server projects — the same workflow run independently per target. Use for greenfield builds across N new servers, maintenance passes across N existing ones, or any repeatable workflow that benefits from fresh-context per-target sub-agents. Encodes the orient template every sub-agent needs (CLAUDE.md chain + list-skills + spec artifacts), the universal hard rules around git tooling and authorization, common gotchas that bite across runs, and a router into per-scenario references for the phase pattern.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.2"
|
|
8
8
|
audience: internal
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -118,6 +118,7 @@ These commonly bite across all scenarios. Scenario-specific gotchas live in thei
|
|
|
118
118
|
| 6 | Independent agents diverge on incidental conventions (scoping, scripts, README hero) | Plan an explicit normalization pass; don't expect alignment for free |
|
|
119
119
|
| 7 | Sub-agent skips the orient block and proceeds with pattern-matched defaults | Put orient as a numbered prerequisite in the prompt with "Only after that, begin the task"; spot-check the first tool calls in the agent's response |
|
|
120
120
|
| 8 | Sub-agent runs `git stash` to "test something safely" | Restate the global rule verbatim in every sub-agent prompt that may touch git: "NEVER `git stash` for any reason." |
|
|
121
|
+
| 9 | `gh release create --notes-from-tag` fails when combined with `--repo` flag | `gh` CLI limitation. Always `cd` into the target repo dir for `gh release` commands; don't use `--repo` with `--notes-from-tag` |
|
|
121
122
|
|
|
122
123
|
## Extending the pattern
|
|
123
124
|
|
|
@@ -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 maintenance passes. Drives parallel `bun update --latest`, changelog investigation (via the changelog skill), framework adoption per the maintenance skill's auto-adopt rule, skill/script sync (Phase A/B/C), and verification across N existing MCP server projects. Optional Bash-git wrap-up fanout commits adoptions after user authorization.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.2"
|
|
8
8
|
audience: internal
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -27,7 +27,9 @@ Before spawning any sub-agents:
|
|
|
27
27
|
2. **Verify each target is a git repo with a clean working tree.** Run `git status` per target. Halt if any is dirty — user fixes locally and re-invokes.
|
|
28
28
|
3. **Verify each target has `scripts/list-skills.ts` and the `list-skills` package script.** Both ship via `init`; older projects pick them up on the next `maintenance` Phase C sync — which is what's about to run. If missing at this moment, that's fine; note it and the sub-agent will pick them up.
|
|
29
29
|
4. **Verify each target has the `maintenance` skill available** (in `skills/` or `.claude/skills/`). If missing, that's a sign the project hasn't been initialized from a recent framework version — flag it; the sub-agent can still work from the package's copy at `node_modules/@cyanheads/mcp-ts-core/skills/maintenance/SKILL.md`.
|
|
30
|
-
5. **
|
|
30
|
+
5. **Check `publish-mcp` script presence** when `server.json` exists. If missing, flag it — the maintenance sub-agent or a follow-up agent should add it.
|
|
31
|
+
6. **Check `manifest.json` existence.** If missing and the project has `server.json`, flag it — the maintenance sub-agent should scaffold it from `templates/manifest.json` during framework adoption (Step 6).
|
|
32
|
+
7. **Clarify the user authorization scope.** Do they want sub-agents to commit/push at the end, or stop at "changes applied, working tree dirty, awaiting review"? Default is the latter.
|
|
31
33
|
|
|
32
34
|
## Phase pattern
|
|
33
35
|
|
|
@@ -83,6 +85,19 @@ The orchestrator collects each sub-agent's Step 8 summary and produces a consoli
|
|
|
83
85
|
|
|
84
86
|
Wait for user direction before Phase 4 — they may want to inspect diffs locally first.
|
|
85
87
|
|
|
88
|
+
### Phase 3.5: Double-check / normalization pass (recommended)
|
|
89
|
+
|
|
90
|
+
Independent maintenance agents diverge on incidental choices and miss adoption sites under context pressure. A lightweight follow-up fanout catches gaps before wrap-up:
|
|
91
|
+
|
|
92
|
+
- **Adoption gaps** — features the updated skills say to do that weren't applied (error code semantic audit, missing scaffolding files like `manifest.json`/`.mcpbignore`, `publish-mcp` script)
|
|
93
|
+
- **Audience compliance** — only skills with `metadata.audience: external` should be in project `skills/`; agents sometimes sync `internal`-audience skills
|
|
94
|
+
- **Content accuracy** — `isRequired` flags in `server.json` match the upstream API's actual requirement; `manifest.json` `name` doesn't include the npm scope prefix; `user_config` entries have required `title` and `type` fields
|
|
95
|
+
- **Cross-target consistency** — if a feature shows up in 3 of 5 Step 8 summaries, the other 2 likely missed it
|
|
96
|
+
|
|
97
|
+
These agents should **fix, not just report** — analysis-only agents create unnecessary follow-up. After fixing, re-run `bun run rebuild && bun run devcheck && bun run test`.
|
|
98
|
+
|
|
99
|
+
Use a lighter model (e.g. Sonnet) for this pass — it's verification and targeted fixes, not deep adoption work.
|
|
100
|
+
|
|
86
101
|
### Phase 4: Wrap-up fanout (optional, Bash git only)
|
|
87
102
|
|
|
88
103
|
Run only after explicit user authorization. Per-target commit decisions are driven by the change shape:
|
|
@@ -110,10 +125,13 @@ If the maintenance pass should drive a version bump (breaking framework upgrade,
|
|
|
110
125
|
| 5 | Sub-agent runs write git commands despite instruction (commit/push/reset/stash/etc.) | Restate the no-write-git list + the no-`stash` rule in the prompt body; verify via `git log --oneline -1` per target after Phase 2 — should show no new commits since pre-flight |
|
|
111
126
|
| 6 | Targets at the same framework version produce inconsistent adoption choices | Usually means one agent missed a site; spot-check the Step 8 "Features adopted" lists across targets. If a feature shows up for 3 of 4, the 4th likely missed it — spawn a narrow finish-pass agent for that target |
|
|
112
127
|
| 7 | Big monorepo or many adoptions cause context exhaustion in a sub-agent | Narrow the prompt: if a target has many breaking framework changes, split the sub-agent's work into "update deps + verify" and "adopt features" as two phases against that target |
|
|
128
|
+
| 8 | Agent syncs `internal`-audience skills into project `skills/` | Restate in the prompt: "Only sync skills with `metadata.audience: external`." The maintenance skill's Phase A step 2 says this, but agents miss it under context pressure |
|
|
129
|
+
| 9 | `manifest.json` scaffolded with scoped name from `package.json` (e.g. `@cyanheads/bls-mcp-server`) — renders in the mcpb install dialog | Double-check pass should verify `manifest.json` `name` doesn't contain `/`; the `polish-docs-meta` skill's cross-file consistency section covers this |
|
|
130
|
+
| 10 | `manifest.json` `user_config` entries missing required `title`/`type` fields — `mcpb pack` fails at release time | Agents scaffold from memory, not the template. Double-check pass should verify all `user_config` entries have `title` and `type` |
|
|
113
131
|
|
|
114
132
|
## Checklist
|
|
115
133
|
|
|
116
|
-
- [ ] Pre-flight: target list confirmed, clean working trees verified, `list-skills` presence noted per target, `maintenance` skill availability noted per target, auth scope clarified with user
|
|
134
|
+
- [ ] Pre-flight: target list confirmed, clean working trees verified, `list-skills` presence noted per target, `maintenance` skill availability noted per target, `publish-mcp` and `manifest.json` presence noted, auth scope clarified with user
|
|
117
135
|
- [ ] Phase 2: maintenance fanout spawned; each sub-agent returned a Step 8 summary
|
|
118
136
|
- [ ] All targets: green `bun run devcheck` and `bun run test` post-adoption
|
|
119
137
|
- [ ] Phase 3: consolidated roll-up presented to user with per-target headlines, cross-target patterns, open decisions, outliers
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Multi-server-orchestration reference for release-and-publish passes — the end-to-end ship workflow combining wrap-up (version bump, changelog, commit, annotated tag) and publish (push + npm + MCP Registry + GHCR) across N MCP server projects. Drives parallel verification → README polish → wrap-up (Bash git, local only — distilled from `git_wrapup_instructions`) → publish (via the standalone `release-and-publish` skill per target) → optional GH issue closure. Phase 5 runs serial when npm 2FA prompts interactively; parallel when bypass is configured. Disambiguated from the single-target `release-and-publish` skill it invokes per target.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.3"
|
|
8
8
|
audience: internal
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -137,8 +137,9 @@ Each target invokes the `release-and-publish` skill end-to-end. Execution mode d
|
|
|
137
137
|
> 3. Push commits and tags to origin via Bash git
|
|
138
138
|
> 4. `bun publish --access public` (npm — uses the configured bypass token)
|
|
139
139
|
> 5. `bun run publish-mcp` if `server.json` exists (MCP Registry)
|
|
140
|
-
> 6. `
|
|
141
|
-
> 7.
|
|
140
|
+
> 6. `bun run bundle` + `gh release create v<version> --verify-tag --notes-from-tag <dist/*.mcpb>` if `manifest.json` exists (MCPB GitHub Release). Must run from inside the repo dir — `--notes-from-tag` is incompatible with `--repo`.
|
|
141
|
+
> 7. `docker buildx build --platform linux/amd64,linux/arm64 --push ...` if `Dockerfile` exists (GHCR)
|
|
142
|
+
> 8. Report deployed artifact URLs (npm, MCP Registry, GitHub Release, GHCR)
|
|
142
143
|
>
|
|
143
144
|
> Honor the skill's retry/halt protocol — transient network errors retry up to 2× with backoff; idempotent-success signals ("version already exists", "cannot publish duplicate version") are treated as success and proceed. Bash git for all git ops. Never skip the verification gate.
|
|
144
145
|
|
|
@@ -176,6 +177,9 @@ Skip Phase 6 for any target whose Phase 5 didn't complete — there's nothing to
|
|
|
176
177
|
| 8 | Annotated tag message bloats into a CHANGELOG copy | Restate the rule in Phase 4 prompt: terse release theme + notable changes + dep arrows in `pkg ^old → ^new` form. Length is earned |
|
|
177
178
|
| 9 | Sub-agent uses `git-mcp-server` instead of Bash git in Phase 4 or 5 | Hard Rule 1 restated explicitly in every fanout prompt that touches git |
|
|
178
179
|
| 10 | Failed publish leaves a tag pushed but no npm package — looks shipped, isn't | Collect per-destination status per target in Phase 5 roll-up; surface partial-state targets explicitly to the user before Phase 6 |
|
|
180
|
+
| 11 | `gh release create --notes-from-tag` fails with `--repo` flag | `gh` CLI limitation. Always `cd` into the target repo dir for `gh release` commands instead of using `--repo` |
|
|
181
|
+
| 12 | Post-version doc changes (install badges, description fixes) land after the version commit — tag points at stale content | Move the tag forward: delete remote release, delete remote+local tag, recreate tag at new HEAD with same annotation, re-push, recreate release with `.mcpb`. This is authorized within the pipeline for same-day follow-ups |
|
|
182
|
+
| 13 | `mcpb pack` fails because `manifest.json` `user_config` entries are missing `title`/`type` fields | Verify `manifest.json` `user_config` entries during Phase 3 or a pre-Phase-5 check; the `polish-docs-meta` skill's cross-file consistency section covers this |
|
|
179
183
|
|
|
180
184
|
## Checklist
|
|
181
185
|
|
|
@@ -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.
|
|
7
|
+
version: "1.2"
|
|
8
8
|
audience: internal
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -100,7 +100,7 @@ One sub-agent per target. The agent executes the seven acceptance criteria via B
|
|
|
100
100
|
> 2. **Version bump.** Start from current `package.json` `version`; apply the bump intent (`[patch/minor/major or explicit string]`). Bump every place version is declared:
|
|
101
101
|
> - `package.json` `version`
|
|
102
102
|
> - `server.json` `version` at the top level AND every `packages[].version` entry
|
|
103
|
-
> - `manifest.json` (if present) `version`
|
|
103
|
+
> - `manifest.json` (if present) `version`. Also verify `name` doesn't include the npm scope prefix — it should be the bare package name (e.g. `bls-mcp-server`, not `@cyanheads/bls-mcp-server`)
|
|
104
104
|
> - README version badge and any hero pinning
|
|
105
105
|
> - Dockerfile OCI labels (if pinned to version)
|
|
106
106
|
> - Any agent-instruction file (`CLAUDE.md`, `AGENTS.md`) that pins the version
|
|
@@ -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)
|
|
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
|
|
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 |
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Finalize documentation and project metadata for a ship-ready MCP server. Use after implementation is complete, tests pass, and devcheck is clean. Safe to run at any stage — each step checks current state and only acts on what still needs work.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "2.
|
|
7
|
+
version: "2.2"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -181,7 +181,11 @@ If the project ships as an `.mcpb` bundle for Claude Desktop (check for `manifes
|
|
|
181
181
|
|
|
182
182
|
- `manifest.json` version matches `package.json` version
|
|
183
183
|
- Env var names in `manifest.json` (`mcp_config.env` + `user_config`) match `server.json` `environmentVariables` — `lint:packaging` enforces this, but verify the set is complete
|
|
184
|
-
- `manifest.json` `name`
|
|
184
|
+
- `manifest.json` `name` matches `package.json` name **without the npm scope prefix** (e.g. `bls-mcp-server`, not `@cyanheads/bls-mcp-server`); `description` matches `package.json`
|
|
185
|
+
- `manifest.json` `user_config` entries must include `title` and `type` fields — `mcpb pack` validates these
|
|
186
|
+
- `server.json` env var `isRequired` must match the upstream API's actual requirement — if the API works without the value (rate-limited, DEMO_KEY fallback, polite pool), mark `isRequired: false` and describe the tradeoff in the description
|
|
187
|
+
- Server description aligned across all surfaces: `package.json`, `manifest.json`, `server.json` (condensed, hard 100-char limit), README header `<p><b>`, and GitHub repo description (`gh repo edit --description`)
|
|
188
|
+
- `package.json` `keywords` include baseline terms: `mcp`, `mcp-server`, `model-context-protocol`, `typescript`, `bun`, `stdio`, `streamable-http`, plus data-domain terms. GitHub repo topics (`gh repo edit --add-topic`) should match.
|
|
185
189
|
|
|
186
190
|
**README install badges:**
|
|
187
191
|
|
|
@@ -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
|
-
|
|
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.
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Ship a release end-to-end across every registry the project targets (npm, MCP Registry, GitHub Releases for `.mcpb` bundles, GHCR). Runs the final verification gate, pushes commits and tags, then publishes to each applicable destination. Assumes git wrapup (version bumps, changelog, commit, annotated tag) is already complete — this skill is the post-wrapup publish workflow. Retries transient network failures on publish steps; halts with a partial-state report when retries are exhausted or the failure is terminal.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "2.
|
|
7
|
+
version: "2.5"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -131,7 +131,7 @@ Halt on any publisher error other than "cannot publish duplicate version".
|
|
|
131
131
|
|
|
132
132
|
Only if `manifest.json` exists at the repo root (otherwise skip).
|
|
133
133
|
|
|
134
|
-
Build the bundle, then create a Release on the existing annotated tag and attach the `.mcpb`. The Release sits on top of the tag from wrapup — `--verify-tag` enforces that the tag already exists on the remote and prevents `gh` from creating a lightweight tag that would shadow the annotated one. `--notes-from-tag` pulls release notes directly from the annotated tag message (no duplication).
|
|
134
|
+
Build the bundle, then create a Release on the existing annotated tag and attach the `.mcpb`. The Release sits on top of the tag from wrapup — `--verify-tag` enforces that the tag already exists on the remote and prevents `gh` from creating a lightweight tag that would shadow the annotated one. `--notes-from-tag` pulls release notes directly from the annotated tag message (no duplication). Note: GitHub prepends `v<VERSION>:` to the release title, so the tag annotation subject must omit the version number to avoid stutter.
|
|
135
135
|
|
|
136
136
|
```bash
|
|
137
137
|
bun run bundle # produces dist/<name>.mcpb (stable filename, no version)
|
|
@@ -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
|
package/templates/AGENTS.md
CHANGED
|
@@ -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
|
|
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.
|
|
337
|
-
echo -n '{"command":"npx
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
package/templates/CLAUDE.md
CHANGED
|
@@ -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
|
|
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.
|
|
337
|
-
echo -n '{"command":"npx
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|