@cyanheads/mcp-ts-core 0.12.9 → 0.13.1
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/AGENTS.md +22 -11
- package/CLAUDE.md +22 -11
- package/README.md +1 -1
- package/biome.json +1 -1
- package/changelog/0.13.x/0.13.0.md +48 -0
- package/changelog/0.13.x/0.13.1.md +56 -0
- package/changelog/template.md +7 -24
- package/{tsconfig.base.json → config/tsconfig.base.json} +2 -2
- package/dist/cli/init.js +2 -2
- package/dist/cli/init.js.map +1 -1
- package/dist/config/envValue.d.ts +18 -0
- package/dist/config/envValue.d.ts.map +1 -0
- package/dist/config/envValue.js +35 -0
- package/dist/config/envValue.js.map +1 -0
- package/dist/config/index.d.ts +8 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +13 -7
- package/dist/config/index.js.map +1 -1
- package/dist/config/parseEnvConfig.d.ts +7 -0
- package/dist/config/parseEnvConfig.d.ts.map +1 -1
- package/dist/config/parseEnvConfig.js +9 -1
- package/dist/config/parseEnvConfig.js.map +1 -1
- package/dist/core/app.d.ts +82 -2
- package/dist/core/app.d.ts.map +1 -1
- package/dist/core/app.js +129 -6
- package/dist/core/app.js.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/worker.d.ts +6 -1
- package/dist/core/worker.d.ts.map +1 -1
- package/dist/core/worker.js.map +1 -1
- package/dist/linter/rules/resource-rules.js +9 -2
- package/dist/linter/rules/resource-rules.js.map +1 -1
- package/dist/linter/rules/tool-rules.js +4 -1
- package/dist/linter/rules/tool-rules.js.map +1 -1
- package/dist/linter/validate.js +2 -2
- package/dist/linter/validate.js.map +1 -1
- package/dist/mcp-server/types.d.ts +10 -3
- package/dist/mcp-server/types.d.ts.map +1 -1
- package/dist/mcp-server/types.js +4 -3
- package/dist/mcp-server/types.js.map +1 -1
- package/dist/services/canvas/core/sqlGate.d.ts.map +1 -1
- package/dist/services/canvas/core/sqlGate.js +27 -2
- package/dist/services/canvas/core/sqlGate.js.map +1 -1
- package/dist/utils/pagination/pagination.d.ts.map +1 -1
- package/dist/utils/pagination/pagination.js +4 -1
- package/dist/utils/pagination/pagination.js.map +1 -1
- package/dist/utils/parsing/frontmatterParser.d.ts +8 -7
- package/dist/utils/parsing/frontmatterParser.d.ts.map +1 -1
- package/dist/utils/parsing/frontmatterParser.js +91 -16
- package/dist/utils/parsing/frontmatterParser.js.map +1 -1
- package/framework-skills/README.md +40 -0
- package/{skills → framework-skills}/add-app-tool/SKILL.md +2 -2
- package/{skills → framework-skills}/add-resource/SKILL.md +2 -2
- package/{skills → framework-skills}/add-service/SKILL.md +2 -2
- package/{skills → framework-skills}/add-test/SKILL.md +2 -2
- package/{skills → framework-skills}/add-tool/SKILL.md +5 -5
- package/{skills → framework-skills}/api-config/SKILL.md +21 -3
- package/{skills → framework-skills}/api-context/SKILL.md +5 -3
- package/{skills → framework-skills}/api-linter/SKILL.md +4 -4
- package/{skills → framework-skills}/api-telemetry/SKILL.md +13 -10
- package/{skills → framework-skills}/code-simplifier/SKILL.md +12 -6
- package/{skills → framework-skills}/design-mcp-server/SKILL.md +2 -2
- package/{skills → framework-skills}/maintenance/SKILL.md +30 -21
- package/{skills → framework-skills}/orchestrations/SKILL.md +2 -2
- package/{skills → framework-skills}/orchestrations/workflows/field-test-fix.md +8 -8
- package/{skills → framework-skills}/orchestrations/workflows/fix-wrapup-release.md +5 -5
- package/{skills → framework-skills}/orchestrations/workflows/greenfield-build.md +11 -11
- package/{skills → framework-skills}/orchestrations/workflows/maintenance-release.md +12 -12
- package/{skills → framework-skills}/polish-docs-meta/SKILL.md +18 -10
- package/{skills → framework-skills}/polish-docs-meta/references/agent-protocol.md +1 -1
- package/{skills → framework-skills}/polish-docs-meta/references/readme.md +93 -73
- package/{skills → framework-skills}/release-and-publish/SKILL.md +12 -3
- package/{skills → framework-skills}/release-pr-review/SKILL.md +2 -2
- package/{skills → framework-skills}/report-issue-framework/SKILL.md +26 -25
- package/{skills → framework-skills}/report-issue-local/SKILL.md +28 -24
- package/{skills → framework-skills}/setup/SKILL.md +10 -8
- package/package.json +13 -13
- package/scripts/build.ts +2 -2
- package/scripts/check-framework-antipatterns.ts +1 -1
- package/scripts/check-skill-versions.ts +16 -9
- package/scripts/check-skills-sync.ts +64 -13
- package/scripts/clean-mcpb.ts +3 -3
- package/scripts/devcheck.ts +18 -15
- package/scripts/lint-packaging.ts +158 -24
- package/scripts/list-skills.ts +2 -2
- package/templates/.claude-plugin/plugin.json +5 -1
- package/templates/.env.example +5 -2
- package/templates/.github/CONTRIBUTING.md +4 -5
- package/templates/.github/ISSUE_TEMPLATE/bug_report.yml +5 -4
- package/templates/.github/ISSUE_TEMPLATE/config.yml +6 -1
- package/templates/.github/ISSUE_TEMPLATE/feature_request.yml +1 -2
- package/templates/AGENTS.md +31 -14
- package/templates/CLAUDE.md +31 -14
- package/templates/_.mcpbignore +1 -1
- package/templates/changelog/template.md +7 -24
- package/templates/package.json +3 -2
- package/templates/src/index.ts +10 -0
- package/templates/src/mcp-server/resources/definitions/echo-app-ui.app-resource.ts +1 -1
- package/skills/README.md +0 -38
- /package/{skills → framework-skills}/add-export/SKILL.md +0 -0
- /package/{skills → framework-skills}/add-prompt/SKILL.md +0 -0
- /package/{skills → framework-skills}/add-provider/SKILL.md +0 -0
- /package/{skills → framework-skills}/api-auth/SKILL.md +0 -0
- /package/{skills → framework-skills}/api-canvas/SKILL.md +0 -0
- /package/{skills → framework-skills}/api-errors/SKILL.md +0 -0
- /package/{skills → framework-skills}/api-mirror/SKILL.md +0 -0
- /package/{skills → framework-skills}/api-services/SKILL.md +0 -0
- /package/{skills → framework-skills}/api-services/references/graph.md +0 -0
- /package/{skills → framework-skills}/api-services/references/llm.md +0 -0
- /package/{skills → framework-skills}/api-services/references/speech.md +0 -0
- /package/{skills → framework-skills}/api-testing/SKILL.md +0 -0
- /package/{skills → framework-skills}/api-utils/SKILL.md +0 -0
- /package/{skills → framework-skills}/api-utils/references/formatting.md +0 -0
- /package/{skills → framework-skills}/api-utils/references/parsing.md +0 -0
- /package/{skills → framework-skills}/api-utils/references/security.md +0 -0
- /package/{skills → framework-skills}/api-workers/SKILL.md +0 -0
- /package/{skills → framework-skills}/field-test/SKILL.md +0 -0
- /package/{skills → framework-skills}/git-wrapup/SKILL.md +0 -0
- /package/{skills → framework-skills}/polish-docs-meta/references/package-meta.md +0 -0
- /package/{skills → framework-skills}/polish-docs-meta/references/server-json.md +0 -0
- /package/{skills → framework-skills}/security-pass/SKILL.md +0 -0
- /package/{skills → framework-skills}/techniques/SKILL.md +0 -0
- /package/{skills → framework-skills}/techniques/references/outline-on-overflow.md +0 -0
- /package/{skills → framework-skills}/tool-defs-analysis/SKILL.md +0 -0
package/AGENTS.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Package:** `@cyanheads/mcp-ts-core`
|
|
4
|
-
**Version:** 0.
|
|
4
|
+
**Version:** 0.13.1
|
|
5
5
|
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
6
6
|
**MCP SDK:** `@modelcontextprotocol/server` ^2.0.0 (protocol revisions 2026-07-28 and 2025-*)
|
|
7
|
-
**Zod:** ^4.6.
|
|
7
|
+
**Zod:** ^4.6.1
|
|
8
8
|
**GitHub:** [cyanheads/mcp-ts-core](https://github.com/cyanheads/mcp-ts-core)
|
|
9
9
|
**npm:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
|
|
10
10
|
**Docker:** [ghcr.io/cyanheads/mcp-ts-core](https://ghcr.io/cyanheads/mcp-ts-core)
|
|
@@ -22,7 +22,7 @@ This package serves two consumer paths. When making changes, know which audience
|
|
|
22
22
|
| **Direct package import** — existing project pulls in the package | `bun add @cyanheads/mcp-ts-core` → `import { createApp, tool, z } from '@cyanheads/mcp-ts-core'` | Public API surface (`src/`) — existing consumers feel changes immediately on upgrade |
|
|
23
23
|
| **Init-scaffolded server** — fresh project bootstrapped from this repo's templates | `bunx @cyanheads/mcp-ts-core init [name]` copies `templates/` into the new directory | `templates/` — only affects newly scaffolded servers, not existing ones |
|
|
24
24
|
|
|
25
|
-
Both paths share the same public API. Init copies starter `package.json`, configs (`tsconfig`, `biome.json`, `vitest.config.ts`, `devcheck.config.json`, `bunfig.toml`), `.env.example`, `Dockerfile`, `LICENSE`, `.gitattributes`, `CLAUDE.md`/`AGENTS.md`, `.github/` (issue forms, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`), example definitions and tests, framework `scripts/`, and external-audience `skills/`. `_`-prefixed files (e.g. `_.gitignore`) drop the prefix on copy. Existing files are never overwritten; `init` without a name scaffolds in place (upgrade flow). After init, consult the `setup` skill.
|
|
25
|
+
Both paths share the same public API. Init copies starter `package.json`, configs (`tsconfig`, `biome.json`, `vitest.config.ts`, `devcheck.config.json`, `bunfig.toml`), `.env.example`, `Dockerfile`, `LICENSE`, `.gitattributes`, `CLAUDE.md`/`AGENTS.md`, `.github/` (issue forms, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`), example definitions and tests, framework `scripts/`, and external-audience `framework-skills/`. `_`-prefixed files (e.g. `_.gitignore`) drop the prefix on copy. Existing files are never overwritten; `init` without a name scaffolds in place (upgrade flow). After init, consult the `setup` skill.
|
|
26
26
|
|
|
27
27
|
---
|
|
28
28
|
|
|
@@ -45,13 +45,13 @@ Both paths share the same public API. Init copies starter `package.json`, config
|
|
|
45
45
|
|
|
46
46
|
| Subpath | Key Exports | Purpose |
|
|
47
47
|
|:--------|:------------|:--------|
|
|
48
|
-
| `@cyanheads/mcp-ts-core` | `createApp`, `tool`, `resource`, `prompt`, `appTool`, `appResource`, `APP_RESOURCE_MIME_TYPE`, `headerParam`, `Context`, `createFail`, `createRecoveryFor`, `TypedFail`, `TypedRecoveryFor`, `ReasonOf`, `HandlerContext`, `Enrich`, `EnrichHelpers`, `TypedEnrich`, `ContentCollect`, `ContentBlock`, `z`, `completable`, `isCompletable`, `CompleteCallback`, `CompleteResourceTemplateCallback`, `CacheHint`, `CacheHints`, `CacheScope` | Main entry point |
|
|
48
|
+
| `@cyanheads/mcp-ts-core` | `createApp`, `tool`, `resource`, `prompt`, `appTool`, `appResource`, `APP_RESOURCE_MIME_TYPE`, `headerParam`, `Context`, `createFail`, `createRecoveryFor`, `TypedFail`, `TypedRecoveryFor`, `ReasonOf`, `HandlerContext`, `Enrich`, `EnrichHelpers`, `TypedEnrich`, `ContentCollect`, `ContentBlock`, `z`, `inputRequired`, `completable`, `isCompletable`, `CompleteCallback`, `CompleteResourceTemplateCallback`, `CacheHint`, `CacheHints`, `CacheScope`, `SessionMode`, `ResolvedSessionMode` | Main entry point |
|
|
49
49
|
| `/worker` | `createWorkerHandler`, `CloudflareBindings` | Cloudflare Workers entry |
|
|
50
50
|
| `/tools` | `ToolDefinition`, `AnyToolDefinition`, `ToolAnnotations` | Tool definition types |
|
|
51
51
|
| `/resources` | `ResourceDefinition`, `AnyResourceDefinition` | Resource definition types |
|
|
52
52
|
| `/prompts` | `PromptDefinition` | Prompt definition type |
|
|
53
53
|
| `/errors` | `McpError`, `JsonRpcErrorCode`, `notFound`, `validationError`, `unauthorized`, ... | Error types, codes, and factory functions |
|
|
54
|
-
| `/config` | `AppConfig`, `config`, `parseConfig`, `parseEnvConfig`, `resetConfig`, `ConfigSchema`, `FRAMEWORK_NAME`, `FRAMEWORK_VERSION` | Zod-validated config, framework identity, env-var
|
|
54
|
+
| `/config` | `AppConfig`, `config`, `parseConfig`, `parseEnvConfig`, `resetConfig`, `normalizeEnv`, `ConfigSchema`, `FRAMEWORK_NAME`, `FRAMEWORK_VERSION` | Zod-validated config, framework identity, env-var helpers |
|
|
55
55
|
| `/auth` | `checkScopes` | Dynamic scope checking |
|
|
56
56
|
| `/storage` | `StorageService` | Storage abstraction |
|
|
57
57
|
| `/storage/types` | `IStorageProvider` | Provider interface |
|
|
@@ -106,9 +106,13 @@ await createApp({
|
|
|
106
106
|
extensions: { // SEP-2133 extensions advertised in capabilities
|
|
107
107
|
'vendor/my-extension': { /* extension config */ },
|
|
108
108
|
},
|
|
109
|
+
sessionMode: 'stateless', // session posture in code, not in a Dockerfile
|
|
109
110
|
setup(core) { // runs after core services init, before transport starts
|
|
110
111
|
initMyService(core.config, core.storage);
|
|
111
112
|
},
|
|
113
|
+
async teardown(core) { // the setup() counterpart — runs on every shutdown path
|
|
114
|
+
await closeMyService();
|
|
115
|
+
},
|
|
112
116
|
});
|
|
113
117
|
```
|
|
114
118
|
|
|
@@ -116,6 +120,10 @@ await createApp({
|
|
|
116
120
|
|
|
117
121
|
**Identity fields** — Optional `title`, `websiteUrl`, `description`, `icons` (SEP-973) pass through to the SDK's `initialize` serverInfo and to the server manifest, keeping the `/.well-known/mcp.json` server card and landing page consistent with what `initialize` reports. Explicit `description` wins over `MCP_SERVER_DESCRIPTION`/package.json.
|
|
118
122
|
|
|
123
|
+
**`sessionMode`** — `SessionMode | { default?: SessionMode; require?: 'stateful' }`; the bare string is shorthand for `{ default }`. Declares the HTTP session posture in `src/` instead of leaving it to a deployment's `MCP_SESSION_MODE`, which still wins whenever it carries a meaningful value — an empty string and a whole-value unsubstituted `${…}` placeholder read as unset, so both fall through to the option rather than to the schema default (`auto`). `require: 'stateful'` fails startup with a `ConfigurationError` when the resolved HTTP mode is `stateless`: declare it on a server whose tools ask the caller for input mid-handler, since under `stateless` a 2025-era client's round trip is refused unconditionally and the tool is unusable rather than merely guarded. Never refuses a stdio start. Workers are outside this contract (`MCP_SESSION_MODE` is not in `CORE_ENV_BINDINGS`).
|
|
124
|
+
|
|
125
|
+
**`teardown(core)`** — the `setup()` counterpart, awaited inside `shutdown()` after the transport stops accepting requests and before core services are disposed and the logger closes, so the hook can still log and still reach `core.storage`. Runs exactly once per shutdown, on the signal path, the stdin-EOF path, and a direct `ServerHandle.shutdown()` alike; an error it raises is logged and never blocks the exit, and on the signal and EOF paths a hook that never settles is cut by the shutdown ceiling (exit 1 on a signal, 0 on EOF). Register whatever the framework cannot see — a `fs.watch`, an open socket, a `setInterval` nobody `unref()`'d. Node/Bun only; `createWorkerHandler` does not accept it, because an isolate is evicted without notice.
|
|
126
|
+
|
|
119
127
|
**Also available** — `landing` (`LandingConfig`, HTTP transport only: landing-page config, all fields optional), `context: { exposeStatelessSessionId }` (populate `ctx.sessionId` from the SDK's per-request token in stateless HTTP mode; default `false`), `eventBus` (the `ServerEventBus` backing `subscriptions/listen`; defaults to an in-process bus — supply one for a multi-isolate or multi-process runtime, Workers most of all), and `cacheHints` (2026-07-28 `ttlMs`/`cacheScope` per cacheable operation — see Adding a Resource for the per-resource override).
|
|
120
128
|
|
|
121
129
|
### Cloudflare Workers — `createWorkerHandler(options)`
|
|
@@ -151,7 +159,7 @@ interface CoreServices {
|
|
|
151
159
|
canvas?: DataCanvas; // present when CANVAS_PROVIDER_TYPE=duckdb; never on Workers
|
|
152
160
|
llmProvider?: ILlmProvider;
|
|
153
161
|
speechService?: SpeechService;
|
|
154
|
-
supabase?:
|
|
162
|
+
supabase?: SupabaseClientHandle;
|
|
155
163
|
}
|
|
156
164
|
|
|
157
165
|
interface ServerHandle {
|
|
@@ -160,6 +168,8 @@ interface ServerHandle {
|
|
|
160
168
|
}
|
|
161
169
|
```
|
|
162
170
|
|
|
171
|
+
**Exit contract.** `shutdown()` is exit-free and unbounded: it also serves the startup-failure rollback and direct calls from embedders and tests, so ending the process belongs to the handlers. `SIGTERM`, `SIGINT`, and stdin EOF each run that same shutdown and then exit explicitly. A signal exits 0 once the shutdown settles and 1 when the 10 s ceiling fires, after a warning naming the step that never settled; stdin EOF exits 0 either way, unchanged. The ceiling bounds the shutdown as a whole rather than any single await, so a step that settles inside it is never truncated. A second signal mid-shutdown reaches no handler (shutdown detaches them as it starts) and terminates on the OS default, 143 / 130 — the operator's force-kill escape hatch. `uncaughtException` / `unhandledRejection` exit 1.
|
|
172
|
+
|
|
163
173
|
---
|
|
164
174
|
|
|
165
175
|
## Server Structure
|
|
@@ -404,7 +414,7 @@ Available factories: `invalidParams`, `invalidRequest`, `notFound`, `forbidden`,
|
|
|
404
414
|
|
|
405
415
|
For HTTP responses from upstream APIs, use `httpErrorFromResponse(response, { service, data })` from `/utils` — maps the full status table (401/403/408/422/429/5xx) and captures body + `Retry-After`.
|
|
406
416
|
|
|
407
|
-
**Auto-classification.** Plain `Error`, `ZodError`, and any other thrown value are caught and classified automatically. Resolution order: `McpError` code (preserved as-is) → JS constructor name (`TypeError` → `ValidationError`) → provider patterns (HTTP status codes, AWS errors, DB errors) → common message patterns → `AbortError` name → `InternalError` fallback.
|
|
417
|
+
**Auto-classification.** Plain `Error`, `ZodError`, and any other thrown value are caught and classified automatically. Resolution order: `McpError` code (preserved as-is) → SDK `ConnectionClosed` (→ `RequestCancelled`) → JS constructor name (`TypeError` → `ValidationError`) → provider patterns (HTTP status codes, AWS errors, DB errors) → common message patterns → `AbortError` name (→ `Timeout`) → `InternalError` fallback.
|
|
408
418
|
|
|
409
419
|
**Error-path parity.** Tool errors: `content[]` carries markdown with `data.recovery.hint`; `structuredContent.error` carries `{ code, message, data? }`. No `_meta.error`. Resources re-throw via JSON-RPC error envelope.
|
|
410
420
|
|
|
@@ -506,11 +516,11 @@ Detailed method signatures, options, and examples live in skill files. Read the
|
|
|
506
516
|
|
|
507
517
|
### Skill versioning
|
|
508
518
|
|
|
509
|
-
Each `skills/<name>/SKILL.md` carries `metadata.version` in frontmatter. The `maintenance` skill's Phase A uses this to sync consumer copies — replaces the **entire skill directory** as one unit. Without a version bump, Phase A skips the skill (content-hash backstop catches drift, but noisier).
|
|
519
|
+
Each `framework-skills/<name>/SKILL.md` carries `metadata.version` in frontmatter. The `maintenance` skill's Phase A uses this to sync consumer copies — replaces the **entire skill directory** as one unit. Without a version bump, Phase A skips the skill (content-hash backstop catches drift, but noisier).
|
|
510
520
|
|
|
511
|
-
**Policy:** Bump `metadata.version` when changing any file under `skills/<name>/` — SKILL.md is the single version knob for the directory. Typo/whitespace fixes exempt. One bump per release cycle suffices. Enforced by `bun run devcheck` (`scripts/check-skill-versions.ts`): a SKILL.md body change vs `HEAD` without a `metadata.version` bump surfaces as a warning; whitespace-only edits never trigger it, and a genuine typo fix opts out via `devcheck.config.json` `skillVersions.ignore`.
|
|
521
|
+
**Policy:** Bump `metadata.version` when changing any file under `framework-skills/<name>/` — SKILL.md is the single version knob for the directory. Typo/whitespace fixes exempt. One bump per release cycle suffices. Enforced by `bun run devcheck` (`scripts/check-skill-versions.ts`): a SKILL.md body change vs `HEAD` without a `metadata.version` bump surfaces as a warning; whitespace-only edits never trigger it, and a genuine typo fix opts out via `devcheck.config.json` `skillVersions.ignore`.
|
|
512
522
|
|
|
513
|
-
Skills live in `skills/<name>/SKILL.md`; the full list is discoverable via the agent's skill registry at session start.
|
|
523
|
+
Skills live in `framework-skills/<name>/SKILL.md`; the full list is discoverable via the agent's skill registry at session start. The directory is deliberately not `skills/`: Claude Code and Codex auto-load a plugin's root `skills/`, and these are development-time skills, not skills for the agents that use a server. `skills/` stays free for that second kind.
|
|
514
524
|
|
|
515
525
|
---
|
|
516
526
|
|
|
@@ -542,7 +552,8 @@ Skills live in `skills/<name>/SKILL.md`; the full list is discoverable via the a
|
|
|
542
552
|
| `bun run build` | Build library output (`scripts/build.ts`) |
|
|
543
553
|
| `bun run rebuild` | Clean and rebuild (`scripts/clean.ts` + `build`) |
|
|
544
554
|
| `bun run devcheck` | **Use often.** Biome lint/format, typecheck, MCP definition + packaging lint, docs/skills/changelog sync checks, secrets + antipattern scans, `bun audit`, `bun outdated` |
|
|
545
|
-
| `bun run audit:
|
|
555
|
+
| `bun run audit:fix` | `bun audit fix` — upgrade vulnerable packages to the lowest safe version within existing ranges (`--dry-run` previews, `--latest` rewrites ranges). First response to a transitive advisory; then `bun update <name>`, then `bun dedupe` |
|
|
556
|
+
| `bun run audit:refresh` | Delete `bun.lock` and reinstall. Last resort after `audit:fix`, `bun update <name>`, and `bun dedupe` — re-resolves every ranged dep and rewrites the lockfile as `lockfileVersion: 2` |
|
|
546
557
|
| `bun run lint:mcp` | Validate MCP definitions against spec |
|
|
547
558
|
| `bun run format` | Auto-fix Biome lint/format issues (safe fixes only) |
|
|
548
559
|
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior, not just formatting |
|
package/CLAUDE.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Package:** `@cyanheads/mcp-ts-core`
|
|
4
|
-
**Version:** 0.
|
|
4
|
+
**Version:** 0.13.1
|
|
5
5
|
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
6
6
|
**MCP SDK:** `@modelcontextprotocol/server` ^2.0.0 (protocol revisions 2026-07-28 and 2025-*)
|
|
7
|
-
**Zod:** ^4.6.
|
|
7
|
+
**Zod:** ^4.6.1
|
|
8
8
|
**GitHub:** [cyanheads/mcp-ts-core](https://github.com/cyanheads/mcp-ts-core)
|
|
9
9
|
**npm:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
|
|
10
10
|
**Docker:** [ghcr.io/cyanheads/mcp-ts-core](https://ghcr.io/cyanheads/mcp-ts-core)
|
|
@@ -22,7 +22,7 @@ This package serves two consumer paths. When making changes, know which audience
|
|
|
22
22
|
| **Direct package import** — existing project pulls in the package | `bun add @cyanheads/mcp-ts-core` → `import { createApp, tool, z } from '@cyanheads/mcp-ts-core'` | Public API surface (`src/`) — existing consumers feel changes immediately on upgrade |
|
|
23
23
|
| **Init-scaffolded server** — fresh project bootstrapped from this repo's templates | `bunx @cyanheads/mcp-ts-core init [name]` copies `templates/` into the new directory | `templates/` — only affects newly scaffolded servers, not existing ones |
|
|
24
24
|
|
|
25
|
-
Both paths share the same public API. Init copies starter `package.json`, configs (`tsconfig`, `biome.json`, `vitest.config.ts`, `devcheck.config.json`, `bunfig.toml`), `.env.example`, `Dockerfile`, `LICENSE`, `.gitattributes`, `CLAUDE.md`/`AGENTS.md`, `.github/` (issue forms, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`), example definitions and tests, framework `scripts/`, and external-audience `skills/`. `_`-prefixed files (e.g. `_.gitignore`) drop the prefix on copy. Existing files are never overwritten; `init` without a name scaffolds in place (upgrade flow). After init, consult the `setup` skill.
|
|
25
|
+
Both paths share the same public API. Init copies starter `package.json`, configs (`tsconfig`, `biome.json`, `vitest.config.ts`, `devcheck.config.json`, `bunfig.toml`), `.env.example`, `Dockerfile`, `LICENSE`, `.gitattributes`, `CLAUDE.md`/`AGENTS.md`, `.github/` (issue forms, `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`), example definitions and tests, framework `scripts/`, and external-audience `framework-skills/`. `_`-prefixed files (e.g. `_.gitignore`) drop the prefix on copy. Existing files are never overwritten; `init` without a name scaffolds in place (upgrade flow). After init, consult the `setup` skill.
|
|
26
26
|
|
|
27
27
|
---
|
|
28
28
|
|
|
@@ -45,13 +45,13 @@ Both paths share the same public API. Init copies starter `package.json`, config
|
|
|
45
45
|
|
|
46
46
|
| Subpath | Key Exports | Purpose |
|
|
47
47
|
|:--------|:------------|:--------|
|
|
48
|
-
| `@cyanheads/mcp-ts-core` | `createApp`, `tool`, `resource`, `prompt`, `appTool`, `appResource`, `APP_RESOURCE_MIME_TYPE`, `headerParam`, `Context`, `createFail`, `createRecoveryFor`, `TypedFail`, `TypedRecoveryFor`, `ReasonOf`, `HandlerContext`, `Enrich`, `EnrichHelpers`, `TypedEnrich`, `ContentCollect`, `ContentBlock`, `z`, `completable`, `isCompletable`, `CompleteCallback`, `CompleteResourceTemplateCallback`, `CacheHint`, `CacheHints`, `CacheScope` | Main entry point |
|
|
48
|
+
| `@cyanheads/mcp-ts-core` | `createApp`, `tool`, `resource`, `prompt`, `appTool`, `appResource`, `APP_RESOURCE_MIME_TYPE`, `headerParam`, `Context`, `createFail`, `createRecoveryFor`, `TypedFail`, `TypedRecoveryFor`, `ReasonOf`, `HandlerContext`, `Enrich`, `EnrichHelpers`, `TypedEnrich`, `ContentCollect`, `ContentBlock`, `z`, `inputRequired`, `completable`, `isCompletable`, `CompleteCallback`, `CompleteResourceTemplateCallback`, `CacheHint`, `CacheHints`, `CacheScope`, `SessionMode`, `ResolvedSessionMode` | Main entry point |
|
|
49
49
|
| `/worker` | `createWorkerHandler`, `CloudflareBindings` | Cloudflare Workers entry |
|
|
50
50
|
| `/tools` | `ToolDefinition`, `AnyToolDefinition`, `ToolAnnotations` | Tool definition types |
|
|
51
51
|
| `/resources` | `ResourceDefinition`, `AnyResourceDefinition` | Resource definition types |
|
|
52
52
|
| `/prompts` | `PromptDefinition` | Prompt definition type |
|
|
53
53
|
| `/errors` | `McpError`, `JsonRpcErrorCode`, `notFound`, `validationError`, `unauthorized`, ... | Error types, codes, and factory functions |
|
|
54
|
-
| `/config` | `AppConfig`, `config`, `parseConfig`, `parseEnvConfig`, `resetConfig`, `ConfigSchema`, `FRAMEWORK_NAME`, `FRAMEWORK_VERSION` | Zod-validated config, framework identity, env-var
|
|
54
|
+
| `/config` | `AppConfig`, `config`, `parseConfig`, `parseEnvConfig`, `resetConfig`, `normalizeEnv`, `ConfigSchema`, `FRAMEWORK_NAME`, `FRAMEWORK_VERSION` | Zod-validated config, framework identity, env-var helpers |
|
|
55
55
|
| `/auth` | `checkScopes` | Dynamic scope checking |
|
|
56
56
|
| `/storage` | `StorageService` | Storage abstraction |
|
|
57
57
|
| `/storage/types` | `IStorageProvider` | Provider interface |
|
|
@@ -106,9 +106,13 @@ await createApp({
|
|
|
106
106
|
extensions: { // SEP-2133 extensions advertised in capabilities
|
|
107
107
|
'vendor/my-extension': { /* extension config */ },
|
|
108
108
|
},
|
|
109
|
+
sessionMode: 'stateless', // session posture in code, not in a Dockerfile
|
|
109
110
|
setup(core) { // runs after core services init, before transport starts
|
|
110
111
|
initMyService(core.config, core.storage);
|
|
111
112
|
},
|
|
113
|
+
async teardown(core) { // the setup() counterpart — runs on every shutdown path
|
|
114
|
+
await closeMyService();
|
|
115
|
+
},
|
|
112
116
|
});
|
|
113
117
|
```
|
|
114
118
|
|
|
@@ -116,6 +120,10 @@ await createApp({
|
|
|
116
120
|
|
|
117
121
|
**Identity fields** — Optional `title`, `websiteUrl`, `description`, `icons` (SEP-973) pass through to the SDK's `initialize` serverInfo and to the server manifest, keeping the `/.well-known/mcp.json` server card and landing page consistent with what `initialize` reports. Explicit `description` wins over `MCP_SERVER_DESCRIPTION`/package.json.
|
|
118
122
|
|
|
123
|
+
**`sessionMode`** — `SessionMode | { default?: SessionMode; require?: 'stateful' }`; the bare string is shorthand for `{ default }`. Declares the HTTP session posture in `src/` instead of leaving it to a deployment's `MCP_SESSION_MODE`, which still wins whenever it carries a meaningful value — an empty string and a whole-value unsubstituted `${…}` placeholder read as unset, so both fall through to the option rather than to the schema default (`auto`). `require: 'stateful'` fails startup with a `ConfigurationError` when the resolved HTTP mode is `stateless`: declare it on a server whose tools ask the caller for input mid-handler, since under `stateless` a 2025-era client's round trip is refused unconditionally and the tool is unusable rather than merely guarded. Never refuses a stdio start. Workers are outside this contract (`MCP_SESSION_MODE` is not in `CORE_ENV_BINDINGS`).
|
|
124
|
+
|
|
125
|
+
**`teardown(core)`** — the `setup()` counterpart, awaited inside `shutdown()` after the transport stops accepting requests and before core services are disposed and the logger closes, so the hook can still log and still reach `core.storage`. Runs exactly once per shutdown, on the signal path, the stdin-EOF path, and a direct `ServerHandle.shutdown()` alike; an error it raises is logged and never blocks the exit, and on the signal and EOF paths a hook that never settles is cut by the shutdown ceiling (exit 1 on a signal, 0 on EOF). Register whatever the framework cannot see — a `fs.watch`, an open socket, a `setInterval` nobody `unref()`'d. Node/Bun only; `createWorkerHandler` does not accept it, because an isolate is evicted without notice.
|
|
126
|
+
|
|
119
127
|
**Also available** — `landing` (`LandingConfig`, HTTP transport only: landing-page config, all fields optional), `context: { exposeStatelessSessionId }` (populate `ctx.sessionId` from the SDK's per-request token in stateless HTTP mode; default `false`), `eventBus` (the `ServerEventBus` backing `subscriptions/listen`; defaults to an in-process bus — supply one for a multi-isolate or multi-process runtime, Workers most of all), and `cacheHints` (2026-07-28 `ttlMs`/`cacheScope` per cacheable operation — see Adding a Resource for the per-resource override).
|
|
120
128
|
|
|
121
129
|
### Cloudflare Workers — `createWorkerHandler(options)`
|
|
@@ -151,7 +159,7 @@ interface CoreServices {
|
|
|
151
159
|
canvas?: DataCanvas; // present when CANVAS_PROVIDER_TYPE=duckdb; never on Workers
|
|
152
160
|
llmProvider?: ILlmProvider;
|
|
153
161
|
speechService?: SpeechService;
|
|
154
|
-
supabase?:
|
|
162
|
+
supabase?: SupabaseClientHandle;
|
|
155
163
|
}
|
|
156
164
|
|
|
157
165
|
interface ServerHandle {
|
|
@@ -160,6 +168,8 @@ interface ServerHandle {
|
|
|
160
168
|
}
|
|
161
169
|
```
|
|
162
170
|
|
|
171
|
+
**Exit contract.** `shutdown()` is exit-free and unbounded: it also serves the startup-failure rollback and direct calls from embedders and tests, so ending the process belongs to the handlers. `SIGTERM`, `SIGINT`, and stdin EOF each run that same shutdown and then exit explicitly. A signal exits 0 once the shutdown settles and 1 when the 10 s ceiling fires, after a warning naming the step that never settled; stdin EOF exits 0 either way, unchanged. The ceiling bounds the shutdown as a whole rather than any single await, so a step that settles inside it is never truncated. A second signal mid-shutdown reaches no handler (shutdown detaches them as it starts) and terminates on the OS default, 143 / 130 — the operator's force-kill escape hatch. `uncaughtException` / `unhandledRejection` exit 1.
|
|
172
|
+
|
|
163
173
|
---
|
|
164
174
|
|
|
165
175
|
## Server Structure
|
|
@@ -404,7 +414,7 @@ Available factories: `invalidParams`, `invalidRequest`, `notFound`, `forbidden`,
|
|
|
404
414
|
|
|
405
415
|
For HTTP responses from upstream APIs, use `httpErrorFromResponse(response, { service, data })` from `/utils` — maps the full status table (401/403/408/422/429/5xx) and captures body + `Retry-After`.
|
|
406
416
|
|
|
407
|
-
**Auto-classification.** Plain `Error`, `ZodError`, and any other thrown value are caught and classified automatically. Resolution order: `McpError` code (preserved as-is) → JS constructor name (`TypeError` → `ValidationError`) → provider patterns (HTTP status codes, AWS errors, DB errors) → common message patterns → `AbortError` name → `InternalError` fallback.
|
|
417
|
+
**Auto-classification.** Plain `Error`, `ZodError`, and any other thrown value are caught and classified automatically. Resolution order: `McpError` code (preserved as-is) → SDK `ConnectionClosed` (→ `RequestCancelled`) → JS constructor name (`TypeError` → `ValidationError`) → provider patterns (HTTP status codes, AWS errors, DB errors) → common message patterns → `AbortError` name (→ `Timeout`) → `InternalError` fallback.
|
|
408
418
|
|
|
409
419
|
**Error-path parity.** Tool errors: `content[]` carries markdown with `data.recovery.hint`; `structuredContent.error` carries `{ code, message, data? }`. No `_meta.error`. Resources re-throw via JSON-RPC error envelope.
|
|
410
420
|
|
|
@@ -506,11 +516,11 @@ Detailed method signatures, options, and examples live in skill files. Read the
|
|
|
506
516
|
|
|
507
517
|
### Skill versioning
|
|
508
518
|
|
|
509
|
-
Each `skills/<name>/SKILL.md` carries `metadata.version` in frontmatter. The `maintenance` skill's Phase A uses this to sync consumer copies — replaces the **entire skill directory** as one unit. Without a version bump, Phase A skips the skill (content-hash backstop catches drift, but noisier).
|
|
519
|
+
Each `framework-skills/<name>/SKILL.md` carries `metadata.version` in frontmatter. The `maintenance` skill's Phase A uses this to sync consumer copies — replaces the **entire skill directory** as one unit. Without a version bump, Phase A skips the skill (content-hash backstop catches drift, but noisier).
|
|
510
520
|
|
|
511
|
-
**Policy:** Bump `metadata.version` when changing any file under `skills/<name>/` — SKILL.md is the single version knob for the directory. Typo/whitespace fixes exempt. One bump per release cycle suffices. Enforced by `bun run devcheck` (`scripts/check-skill-versions.ts`): a SKILL.md body change vs `HEAD` without a `metadata.version` bump surfaces as a warning; whitespace-only edits never trigger it, and a genuine typo fix opts out via `devcheck.config.json` `skillVersions.ignore`.
|
|
521
|
+
**Policy:** Bump `metadata.version` when changing any file under `framework-skills/<name>/` — SKILL.md is the single version knob for the directory. Typo/whitespace fixes exempt. One bump per release cycle suffices. Enforced by `bun run devcheck` (`scripts/check-skill-versions.ts`): a SKILL.md body change vs `HEAD` without a `metadata.version` bump surfaces as a warning; whitespace-only edits never trigger it, and a genuine typo fix opts out via `devcheck.config.json` `skillVersions.ignore`.
|
|
512
522
|
|
|
513
|
-
Skills live in `skills/<name>/SKILL.md`; the full list is discoverable via the agent's skill registry at session start.
|
|
523
|
+
Skills live in `framework-skills/<name>/SKILL.md`; the full list is discoverable via the agent's skill registry at session start. The directory is deliberately not `skills/`: Claude Code and Codex auto-load a plugin's root `skills/`, and these are development-time skills, not skills for the agents that use a server. `skills/` stays free for that second kind.
|
|
514
524
|
|
|
515
525
|
---
|
|
516
526
|
|
|
@@ -542,7 +552,8 @@ Skills live in `skills/<name>/SKILL.md`; the full list is discoverable via the a
|
|
|
542
552
|
| `bun run build` | Build library output (`scripts/build.ts`) |
|
|
543
553
|
| `bun run rebuild` | Clean and rebuild (`scripts/clean.ts` + `build`) |
|
|
544
554
|
| `bun run devcheck` | **Use often.** Biome lint/format, typecheck, MCP definition + packaging lint, docs/skills/changelog sync checks, secrets + antipattern scans, `bun audit`, `bun outdated` |
|
|
545
|
-
| `bun run audit:
|
|
555
|
+
| `bun run audit:fix` | `bun audit fix` — upgrade vulnerable packages to the lowest safe version within existing ranges (`--dry-run` previews, `--latest` rewrites ranges). First response to a transitive advisory; then `bun update <name>`, then `bun dedupe` |
|
|
556
|
+
| `bun run audit:refresh` | Delete `bun.lock` and reinstall. Last resort after `audit:fix`, `bun update <name>`, and `bun dedupe` — re-resolves every ranged dep and rewrites the lockfile as `lockfileVersion: 2` |
|
|
546
557
|
| `bun run lint:mcp` | Validate MCP definitions against spec |
|
|
547
558
|
| `bun run format` | Auto-fix Biome lint/format issues (safe fixes only) |
|
|
548
559
|
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior, not just formatting |
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
<div align="center">
|
|
8
8
|
|
|
9
|
-
[](./CHANGELOG.md) [](./LICENSE) [](https://modelcontextprotocol.io/specification/2026-07-28)
|
|
10
10
|
|
|
11
11
|
[](https://modelcontextprotocol.io/) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
12
12
|
|
package/biome.json
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "The framework's skill tree moves from skills/ to framework-skills/, so a server that ships a plugin manifest no longer hands its development skills to every installing agent; consumers migrate with a git mv plus path updates. Empty and unsubstituted ${…} env values now read as unset on both config paths."
|
|
3
|
+
breaking: true
|
|
4
|
+
security: false
|
|
5
|
+
agent-notes: |
|
|
6
|
+
Two one-time steps for a consumer upgrading from an earlier release.
|
|
7
|
+
|
|
8
|
+
1. Move the skill tree: `git mv skills framework-skills`, then update the path
|
|
9
|
+
in `CLAUDE.md`/`AGENTS.md`, `.mcpbignore` (`/framework-skills/`), and
|
|
10
|
+
`.github/CONTRIBUTING.md`, and regenerate `docs/tree.md`. `bun run devcheck`
|
|
11
|
+
reports an unmigrated tree until this is done. The agent mirrors
|
|
12
|
+
(`.claude/skills/`, `.agents/skills/`) keep their names — plugin hosts do
|
|
13
|
+
not scan them.
|
|
14
|
+
2. Drop any per-server `z.preprocess` guard that maps a `${…}` placeholder or
|
|
15
|
+
an empty string to `undefined`. Both config paths now do it.
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# 0.13.0 — 2026-09-13
|
|
19
|
+
|
|
20
|
+
## Added
|
|
21
|
+
|
|
22
|
+
- **`lint:packaging` check 11 — MCPB `user_config` wiring** — every declared option must be referenced from `mcp_config` as `${user_config.<key>}`, every reference must be declared, no other `${…}` may appear besides the host's path placeholders, and an optional string option needs `"default": ""`.
|
|
23
|
+
- **Check 10 rejects an empty plugin `env` value** — the client sets the entry on the child process, so `""` replaces a key the user exported. A `${user_config.<option>}` reference in `.claude-plugin/plugin.json` must name a declared `userConfig` option; Codex forwards a host variable only when `env_vars` names it.
|
|
24
|
+
|
|
25
|
+
## Changed
|
|
26
|
+
|
|
27
|
+
- **The framework's skill tree moved from `skills/` to `framework-skills/`** ([#428](https://github.com/cyanheads/mcp-ts-core/issues/428)) — Claude Code and Codex auto-load a plugin's root `skills/`, so a server shipping `.claude-plugin/` or `.codex-plugin/` handed its development skills to every agent that installed it. No runtime API changed; the break is the path, and `check-skills-sync` reports an unmigrated tree until a consumer moves it (see `agent-notes`).
|
|
28
|
+
- **`parseEnvConfig` and the framework's own config read an empty string and a whole-value `${…}` placeholder as unset** ([#427](https://github.com/cyanheads/mcp-ts-core/issues/427)) — an optional field stays `undefined`, a defaulted field takes its default, and a required field fails as missing rather than as a format error against the literal text. A value that merely contains `${…}` is kept; `parseEnvConfig` previously did not strip empty strings.
|
|
29
|
+
- **`audit:fix` runs `bun audit fix`** ([#363](https://github.com/cyanheads/mcp-ts-core/issues/363)) — `bun audit` has no `--fix` flag, so the script ran a read-only audit and exited `0` having changed nothing. The `maintenance` skill's transitive-advisory triage now leads with it, then `bun update <name>`, then `bun dedupe`, with `audit:refresh` last ([#366](https://github.com/cyanheads/mcp-ts-core/issues/366)).
|
|
30
|
+
- **The scaffolded app UI imports `@modelcontextprotocol/ext-apps@2`** ([#424](https://github.com/cyanheads/mcp-ts-core/issues/424)) — the package depends on `^2.0.0`.
|
|
31
|
+
- **Scaffolded issue forms accept blank issues, and this repo's forms link the private advisory form** — a vulnerability had no route in but a public issue.
|
|
32
|
+
- Shipped skills: `add-app-tool` 1.5, `add-resource` 1.6, `add-service` 1.10, `add-test` 1.7, `add-tool` 2.24, `api-config` 1.17, `api-context` 2.3, `api-linter` 1.14, `maintenance` 2.7, `orchestrations` 1.9, `polish-docs-meta` 2.15, `release-pr-review` 1.1, `report-issue-framework` 1.10, `report-issue-local` 1.8, `setup` 1.11.
|
|
33
|
+
|
|
34
|
+
## Fixed
|
|
35
|
+
|
|
36
|
+
- **`templates/.env.example` names all three `MCP_SESSION_MODE` values and `auto` as the default** ([#384](https://github.com/cyanheads/mcp-ts-core/issues/384)).
|
|
37
|
+
- **`changelog/template.md` no longer prescribes a tag format `git-wrapup` forbids** ([#348](https://github.com/cyanheads/mcp-ts-core/issues/348)) — the tag-annotation spec is owned by `release-and-publish`.
|
|
38
|
+
- **The `report-issue-framework` and `report-issue-local` feature templates carry the form's required `Use case` field** ([#353](https://github.com/cyanheads/mcp-ts-core/issues/353)) — both led with a `## Proposal` heading that is not a form field.
|
|
39
|
+
- **The scaffolded agent-protocol checklist names the unscoped repo name for plugin display fields** ([#382](https://github.com/cyanheads/mcp-ts-core/issues/382)) — it said "package name", which `lint:packaging` rejects for a scoped package.
|
|
40
|
+
|
|
41
|
+
## Dependencies
|
|
42
|
+
|
|
43
|
+
- `zod` ^4.6.0 → ^4.6.1
|
|
44
|
+
- `@biomejs/biome` 2.5.12 → 2.5.13
|
|
45
|
+
- `@cloudflare/workers-types` 5.20260905.1 → 5.20260910.1
|
|
46
|
+
- `@types/node` 26.4.0 → 26.5.1
|
|
47
|
+
- `vite` 8.2.2 → 8.3.0
|
|
48
|
+
- `vitest`, `@vitest/ui`, `@vitest/coverage-istanbul` held at 4.1.11 pending `@cloudflare/vitest-pool-workers` support ([#420](https://github.com/cyanheads/mcp-ts-core/issues/420))
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Five polynomial-time regexes become linear scans, two of them on client-supplied input. createApp gains a sessionMode option and a teardown hook, and SIGTERM/SIGINT now end the process explicitly instead of waiting for the event loop to drain."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: true
|
|
5
|
+
agent-notes: |
|
|
6
|
+
Three adoption steps for a consumer upgrading from 0.13.0.
|
|
7
|
+
|
|
8
|
+
1. `MCP_SESSION_MODE` is now set actively in the scaffold's
|
|
9
|
+
`templates/.env.example` (`stateless`) rather than commented out at `auto`.
|
|
10
|
+
An upgrade never rewrites an existing server's `.env.example` — set the
|
|
11
|
+
value there deliberately, and prefer `stateful` on a server whose tools
|
|
12
|
+
gate on `ctx.requestInput`. Declaring it in `src/` via
|
|
13
|
+
`createApp({ sessionMode })` is the durable form.
|
|
14
|
+
2. Move service cleanup into `createApp({ teardown })`. A service that
|
|
15
|
+
allocates a watcher, socket, or non-`unref()`'d timer in `setup()` and
|
|
16
|
+
exposes its own `shutdown()` had nothing calling it; the hook is that
|
|
17
|
+
call. Signal shutdown now exits explicitly, so an unreleased ref'd handle
|
|
18
|
+
no longer hangs the process either — but it is cut, not closed.
|
|
19
|
+
3. The tsconfig move only affects a project that extends a root config **by
|
|
20
|
+
path** (`…/mcp-ts-core/tsconfig.base.json`); retarget it to
|
|
21
|
+
`config/tsconfig.base.json`. A project extending the published
|
|
22
|
+
`@cyanheads/mcp-ts-core/tsconfig.base.json` subpath — the scaffold's own
|
|
23
|
+
form — needs no change.
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# 0.13.1 — 2026-09-13
|
|
27
|
+
|
|
28
|
+
## Added
|
|
29
|
+
|
|
30
|
+
- **`createApp({ sessionMode })` declares the HTTP session posture in `src/`** ([#376](https://github.com/cyanheads/mcp-ts-core/issues/376)) — `SessionMode | { default?: SessionMode; require?: 'stateful' }`, the bare string being shorthand for `{ default }`. `MCP_SESSION_MODE` still wins over `default` whenever it carries a meaningful value; an empty string and a whole-value unsubstituted `${…}` placeholder read as unset and fall through to the option rather than to the schema default.
|
|
31
|
+
- **`require: 'stateful'` fails startup instead of degrading** ([#376](https://github.com/cyanheads/mcp-ts-core/issues/376)) — a `ConfigurationError` naming the conflicting env value, raised before any service is constructed, when the resolved HTTP mode is `stateless`. Declare it on a server whose tools gate on `ctx.requestInput`, which a 2025-era HTTP client cannot answer under `stateless`. Never refuses a stdio start.
|
|
32
|
+
- **`createApp({ teardown })`, the `setup()` counterpart** ([#435](https://github.com/cyanheads/mcp-ts-core/issues/435)) — awaited inside `shutdown()` after the transport stops accepting requests and before core services are disposed and the logger closes, so the hook can still log and still reach `core.storage`. Runs once per shutdown on the signal, stdin-EOF, and direct `ServerHandle.shutdown()` paths alike; an error it raises is logged and never blocks the exit. Node/Bun only — `createWorkerHandler` omits it, because an isolate is evicted without notice.
|
|
33
|
+
- **`SessionMode` and `ResolvedSessionMode` on the root barrel; `normalizeEnv` on `/config`** — the seeding decision needs the same verdict `parseConfig` reaches on an empty or placeholder value, so the rule is exported rather than re-implemented at the call site.
|
|
34
|
+
|
|
35
|
+
## Changed
|
|
36
|
+
|
|
37
|
+
- **`SIGTERM` and `SIGINT` end the process explicitly** ([#435](https://github.com/cyanheads/mcp-ts-core/issues/435)) — exit `0` once shutdown settles, `1` when the 10 s ceiling fires, after a warning naming the cleanup step that never settled. The ceiling bounds the shutdown as a whole, so a step that settles inside it is never truncated. A second signal mid-shutdown still terminates on the OS default (`143` / `130`), and `ServerHandle.shutdown()` stays exit-free and unbounded.
|
|
38
|
+
- **The four project tsconfigs live in `config/`** ([#432](https://github.com/cyanheads/mcp-ts-core/issues/432)) — `tsconfig.json` stays at the root and extends `./config/tsconfig.base.json`. The published `./tsconfig.base.json` subpath is retargeted through the export map, not renamed, so a consumer extending it is untouched.
|
|
39
|
+
- **Both `.env.example` files set `MCP_SESSION_MODE=stateless`** ([#376](https://github.com/cyanheads/mcp-ts-core/issues/376)) — actively, where the root file carried `auto` and the template left the line commented. Stateless fits the data-API shape the scaffold targets; `ctx.state` is tenant-scoped storage, not the session store, so caching across calls is unaffected.
|
|
40
|
+
- **`code-simplifier` 1.5** ([#433](https://github.com/cyanheads/mcp-ts-core/issues/433)) — Phase 1 takes a named path, module, or whole codebase as its scope, not only the working-tree diff. Adds the *Pass-through layers* deletion test and *Test-only reach* dimensions, with the framework's init/accessor pair, definition→service split, and config-selected provider interfaces exempted.
|
|
41
|
+
- **`report-issue-local` 1.9 and `report-issue-framework` 1.11 document the blocked-by labels** ([#436](https://github.com/cyanheads/mcp-ts-core/issues/436)) — `blocked-by-framework` for a server fix waiting on a released `@cyanheads/mcp-ts-core` change, `blocked-by-sdk` for one waiting on `@modelcontextprotocol/sdk`.
|
|
42
|
+
- **Skill upkeep** — `release-and-publish` 2.17 writes the annotated tag from a file (`-F`) rather than `-m`, whose backticks were command substitution, and waits for npm to serve the new version before `publish-mcp`; `polish-docs-meta` 2.16's README reference pins the Overview opener, the Features shape, and which deployment subsections apply, and `maintenance` 2.8 re-audits `README.md` when that reference changes; `api-telemetry` 1.9, `api-config` 1.18, `api-context` 2.4, `add-tool` 2.25, and `design-mcp-server` 2.26 carry the two new options and the corrected `MCP_SESSION_MODE=stateless` behavior (the 2025-era shim runs and its capability gate refuses — it is not disabled).
|
|
43
|
+
|
|
44
|
+
## Fixed
|
|
45
|
+
|
|
46
|
+
- **`FrontmatterParser.parse`'s JSDoc examples** — `content` for a `---\n…\n---\n\n# Body` document is `'# Body'`; two examples documented a leading newline the parser never produced.
|
|
47
|
+
- **`MCP_HTTP_HOST` in the scaffold's env comment** — `127.0.0.1`, matching the schema default, where the comment said `localhost`.
|
|
48
|
+
|
|
49
|
+
## Security
|
|
50
|
+
|
|
51
|
+
- **Five polynomial-time regexes replaced with linear-time scans** ([#431](https://github.com/cyanheads/mcp-ts-core/issues/431)) — CodeQL `js/polynomial-redos`. Two run on input a client chooses and become index walks: `stripSqlComments`, over the SQL sent to a canvas query tool, and `frontmatterParser`'s fence match, over any markdown a server hands `parse()` — at the 1 MiB default body cap either pinned the event loop for minutes. `encodeCursor` and the two linter URI-template helpers stay regexes, narrowed so they cannot backtrack; neither has client-reachable input.
|
|
52
|
+
- **One deliberate divergence on a malformed URI template** — the linter helpers now read `{{{{a}` as a literal `{{{` followed by the expression `{a}`, where the old patterns took the whole run as one expression body. RFC 6570 has no nested expressions, so no well-formed template changes, and admitting `{` into the body is exactly what let a run of them backtrack.
|
|
53
|
+
|
|
54
|
+
## Dependencies
|
|
55
|
+
|
|
56
|
+
- `openai` `^7.10.0` → `^7.15.0`
|
package/changelog/template.md
CHANGED
|
@@ -117,30 +117,13 @@ security: false
|
|
|
117
117
|
in that unrelated item's metadata.
|
|
118
118
|
|
|
119
119
|
TAG ANNOTATIONS — the annotated tag body renders as the GitHub Release body
|
|
120
|
-
via `gh release create --notes-from-tag`.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
Dependency bumps: ← section header
|
|
128
|
-
← blank line
|
|
129
|
-
- `@cyanheads/mcp-ts-core` ^0.9.1 → ^0.9.6 ← bullet
|
|
130
|
-
← blank line
|
|
131
|
-
Changed: ← only sections with entries
|
|
132
|
-
← blank line
|
|
133
|
-
- `format()` output includes `query` in text mode
|
|
134
|
-
← blank line
|
|
135
|
-
Added:
|
|
136
|
-
← blank line
|
|
137
|
-
- `manifest.json` scaffolded for MCPB bundle support
|
|
138
|
-
- Install badges (Claude Desktop, Cursor, VS Code)
|
|
139
|
-
← blank line
|
|
140
|
-
<N> tests pass; `bun run devcheck` clean. ← footer
|
|
141
|
-
|
|
142
|
-
Never a flat comma-separated string. Always structured markdown with
|
|
143
|
-
sections. The tag must scan well as a rendered GitHub Release page.
|
|
120
|
+
via `gh release create --notes-from-tag`. It is a condensed digest of this
|
|
121
|
+
entry, never a copy, and its format is owned by the `release-and-publish`
|
|
122
|
+
skill (step 4, "Create the annotated tag"): the entry's `summary:` as the
|
|
123
|
+
theme line without the version, flat headline bullets — no Keep-a-Changelog
|
|
124
|
+
section headers, no gates line — at most one deps line, issue backlinks,
|
|
125
|
+
and the changelog link last. In release-PR mode the `git-wrapup` skill
|
|
126
|
+
authors that digest as the PR body's `## Changes` and the tag copies it.
|
|
144
127
|
-->
|
|
145
128
|
|
|
146
129
|
## Added
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
"esModuleInterop": true,
|
|
16
16
|
|
|
17
|
-
"outDir": "
|
|
17
|
+
"outDir": "../dist",
|
|
18
18
|
"declaration": true,
|
|
19
19
|
"declarationMap": true,
|
|
20
20
|
"sourceMap": true,
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"allowJs": false,
|
|
35
35
|
|
|
36
36
|
"paths": {
|
|
37
|
-
"@/*": ["
|
|
37
|
+
"@/*": ["../src/*"]
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
}
|
package/dist/cli/init.js
CHANGED
|
@@ -9,7 +9,7 @@ import { basename, dirname, extname, join, relative } from 'node:path';
|
|
|
9
9
|
import { fileURLToPath } from 'node:url';
|
|
10
10
|
const PACKAGE_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
11
11
|
const TEMPLATES_DIR = join(PACKAGE_ROOT, 'templates');
|
|
12
|
-
const SKILLS_DIR = join(PACKAGE_ROOT, 'skills');
|
|
12
|
+
const SKILLS_DIR = join(PACKAGE_ROOT, 'framework-skills');
|
|
13
13
|
const SCRIPTS_DIR = join(PACKAGE_ROOT, 'scripts');
|
|
14
14
|
const PACKAGE_JSON = JSON.parse(readFileSync(join(PACKAGE_ROOT, 'package.json'), 'utf-8'));
|
|
15
15
|
/**
|
|
@@ -152,7 +152,7 @@ function copyExternalSkills(dest, created, skipped) {
|
|
|
152
152
|
continue;
|
|
153
153
|
const skillFiles = walkDir(join(SKILLS_DIR, dir.name));
|
|
154
154
|
for (const srcPath of skillFiles) {
|
|
155
|
-
const relPath = join('skills', dir.name, relative(join(SKILLS_DIR, dir.name), srcPath));
|
|
155
|
+
const relPath = join('framework-skills', dir.name, relative(join(SKILLS_DIR, dir.name), srcPath));
|
|
156
156
|
const destPath = join(dest, relPath);
|
|
157
157
|
copyIfAbsent(srcPath, destPath, relPath, created, skipped);
|
|
158
158
|
}
|
package/dist/cli/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":";AACA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClG,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAC/E,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;AACtD,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":";AACA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClG,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAC/E,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;AACtD,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,EAAE,kBAAkB,CAAC,CAAC;AAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;AAClD,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAKxF,CAAC;AACF;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,CAAC,YAAY,CAAC,KAAK,IAAI,EAAE,CAAC;KAChD,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;KACxE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;KAC9C,IAAI,EAAE,CAAC;AACV,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IAC9B,KAAK;IACL,KAAK;IACL,KAAK;IACL,OAAO;IACP,OAAO;IACP,MAAM;IACN,OAAO;IACP,MAAM;IACN,EAAE;CACH,CAAC,CAAC;AAEH,yEAAyE;AAEzE,MAAM,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAE3C,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;IAC1B,IAAI,EAAE,CAAC;AACT,CAAC;KAAM,CAAC;IACN,UAAU,EAAE,CAAC;IACb,OAAO,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5E,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,CAAC,GAAG,CAAC;4BACc,YAAY,CAAC,OAAO;;;;;;;;;;CAU/C,CAAC,CAAC;AACH,CAAC;AAED,yEAAyE;AAEzE,SAAS,IAAI;IACX,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEnC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,UAAU,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;IACzE,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,6BAA6B,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACtE,OAAO,CAAC,KAAK,CAAC,4DAA4D,CAAC,CAAC;QAC5E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;IAC9D,MAAM,WAAW,GAAG,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;IAE3C,IAAI,IAAI,EAAE,CAAC;QACT,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/D,OAAO,CAAC,KAAK,CACX,kCAAkC,IAAI,kDAAkD,CACzF,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,IAAI,CAAC,CAAC;IAErE,MAAM,aAAa,GAA2B;QAC5C,YAAY,EAAE,WAAW;QACzB,iBAAiB,EAAE,YAAY,CAAC,OAAO;QACvC,eAAe,EAAE,YAAY,CAAC,YAAY,EAAE,CAAC,8BAA8B,CAAC,IAAI,EAAE;QAClF,WAAW,EAAE,YAAY,CAAC,gBAAgB,EAAE,GAAG,IAAI,EAAE;KACtD,CAAC;IAEF,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,yBAAyB;IACzB,aAAa,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAErD,uBAAuB;IACvB,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAEpC,+BAA+B;IAC/B,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAE3C,gBAAgB;IAChB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,yEAAyE;AAEzE,SAAS,aAAa,CACpB,IAAY,EACZ,aAAqC,EACrC,OAAiB,EACjB,OAAiB;IAEjB,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAEvC,KAAK,MAAM,OAAO,IAAI,OAAO,EAAE,CAAC;QAC9B,IAAI,OAAO,GAAG,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QAE/C,qEAAqE;QACrE,qCAAqC;QACrC,wCAAwC;QACxC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;QACxD,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;QAErD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAErC,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACxB,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACzB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACtB,SAAS;YACX,CAAC;YACD,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAClD,IAAI,OAAO,GAAG,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC7C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;gBACzD,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC;YACpD,CAAC;YACD,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACjC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;aAAM,CAAC;YACN,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;AACH,CAAC;AAED,yEAAyE;AAEzE,SAAS,WAAW,CAAC,IAAY,EAAE,OAAiB,EAAE,OAAiB;IACrE,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO;IAErC,KAAK,MAAM,UAAU,IAAI,gBAAgB,EAAE,CAAC;QAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QAC9C,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,SAAS;QAEnC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAErC,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC;AAED,yEAAyE;AAEzE,SAAS,kBAAkB,CAAC,IAAY,EAAE,OAAiB,EAAE,OAAiB;IAC5E,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO;IAEpC,MAAM,SAAS,GAAG,WAAW,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAElG,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QAC3D,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;YAAE,SAAS;QAEvC,MAAM,QAAQ,GAAG,eAAe,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;QACrE,IAAI,QAAQ,KAAK,UAAU;YAAE,SAAS;QAEtC,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QAEvD,KAAK,MAAM,OAAO,IAAI,UAAU,EAAE,CAAC;YACjC,MAAM,OAAO,GAAG,IAAI,CAClB,kBAAkB,EAClB,GAAG,CAAC,IAAI,EACR,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAC9C,CAAC;YACF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAErC,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,eAAe,CAAC,OAAe;IACtC,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC3D,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QAAE,OAAO;IAC9B,iFAAiF;IACjF,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;IACjG,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IAC7B,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,wFAAwF;AACxF,SAAS,YAAY,CACnB,OAAe,EACf,QAAgB,EAChB,OAAe,EACf,OAAiB,EACjB,OAAiB;IAEjB,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC1B,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,yEAAyE;AAEzE,+EAA+E;AAC/E,SAAS,OAAO,CAAC,GAAW;IAC1B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC;IAErC,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW;YAAE,SAAS;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,UAAU,CAAC,QAAgB;IAClC,OAAO,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,YAAY,CAAC,OAAiB,EAAE,OAAiB,EAAE,IAAwB;IAClF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC1B,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;QAC5C,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,OAAO,OAAO,CAAC,MAAM,aAAa,OAAO,CAAC,MAAM,UAAU,CAAC,CAAC;IAExE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;QAC/B,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,eAAe,CAAC,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,sDAAsD,CAAC,CAAC;QAC/E,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,oEAAoE,CAAC,CAAC;QAClF,OAAO,CAAC,GAAG,EAAE,CAAC;IAChB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Normalizes raw environment values before schema validation,
|
|
3
|
+
* so "nothing was provided" reads the same whether the variable is unset,
|
|
4
|
+
* empty, or an install-time placeholder nobody substituted.
|
|
5
|
+
* @module src/config/envValue
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Returns `undefined` for a value that carries no configuration: an empty or
|
|
9
|
+
* whitespace-only string, or an unsubstituted whole-value placeholder. An
|
|
10
|
+
* optional field then stays unset, a defaulted field takes its default, and a
|
|
11
|
+
* required field fails as missing rather than as a format error against the
|
|
12
|
+
* literal text. Anything else — including a longer string that merely
|
|
13
|
+
* contains `${…}` — passes through unchanged.
|
|
14
|
+
*/
|
|
15
|
+
export declare const emptyStringAsUndefined: (val: unknown) => unknown;
|
|
16
|
+
/** Applies {@link emptyStringAsUndefined} to every entry of an env map. */
|
|
17
|
+
export declare const normalizeEnv: (env: Record<string, string | undefined>) => Record<string, string | undefined>;
|
|
18
|
+
//# sourceMappingURL=envValue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envValue.d.ts","sourceRoot":"","sources":["../../src/config/envValue.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,QAAS,OAAO,KAAG,OAIrD,CAAC;AAEF,2EAA2E;AAC3E,eAAO,MAAM,YAAY,QAClB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,KACtC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAMnC,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Normalizes raw environment values before schema validation,
|
|
3
|
+
* so "nothing was provided" reads the same whether the variable is unset,
|
|
4
|
+
* empty, or an install-time placeholder nobody substituted.
|
|
5
|
+
* @module src/config/envValue
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* A whole-value `${…}` reference — `${user_config.api_key}`, `${API_KEY}` —
|
|
9
|
+
* that an install-time host (an MCPB manifest, a plugin manifest) forwarded
|
|
10
|
+
* verbatim because nothing substituted it.
|
|
11
|
+
*/
|
|
12
|
+
const UNSUBSTITUTED_PLACEHOLDER = /^\$\{[^}]+\}$/;
|
|
13
|
+
/**
|
|
14
|
+
* Returns `undefined` for a value that carries no configuration: an empty or
|
|
15
|
+
* whitespace-only string, or an unsubstituted whole-value placeholder. An
|
|
16
|
+
* optional field then stays unset, a defaulted field takes its default, and a
|
|
17
|
+
* required field fails as missing rather than as a format error against the
|
|
18
|
+
* literal text. Anything else — including a longer string that merely
|
|
19
|
+
* contains `${…}` — passes through unchanged.
|
|
20
|
+
*/
|
|
21
|
+
export const emptyStringAsUndefined = (val) => {
|
|
22
|
+
if (typeof val !== 'string')
|
|
23
|
+
return val;
|
|
24
|
+
const trimmed = val.trim();
|
|
25
|
+
return trimmed === '' || UNSUBSTITUTED_PLACEHOLDER.test(trimmed) ? undefined : val;
|
|
26
|
+
};
|
|
27
|
+
/** Applies {@link emptyStringAsUndefined} to every entry of an env map. */
|
|
28
|
+
export const normalizeEnv = (env) => {
|
|
29
|
+
const out = {};
|
|
30
|
+
for (const [key, value] of Object.entries(env)) {
|
|
31
|
+
out[key] = emptyStringAsUndefined(value);
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=envValue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envValue.js","sourceRoot":"","sources":["../../src/config/envValue.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;GAIG;AACH,MAAM,yBAAyB,GAAG,eAAe,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,GAAY,EAAW,EAAE;IAC9D,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IACxC,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,OAAO,OAAO,KAAK,EAAE,IAAI,yBAAyB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC;AACrF,CAAC,CAAC;AAEF,2EAA2E;AAC3E,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,GAAuC,EACH,EAAE;IACtC,MAAM,GAAG,GAAuC,EAAE,CAAC;IACnD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/C,GAAG,CAAC,GAAG,CAAC,GAAG,sBAAsB,CAAC,KAAK,CAAuB,CAAC;IACjE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC,CAAC"}
|
package/dist/config/index.d.ts
CHANGED
|
@@ -393,6 +393,14 @@ declare const config: {
|
|
|
393
393
|
* Export the runtime configuration, parser, and schema, plus a static AppConfig type.
|
|
394
394
|
*/
|
|
395
395
|
export type AppConfig = z.infer<typeof ConfigSchema>;
|
|
396
|
+
/**
|
|
397
|
+
* The normalization `parseConfig` applies to every environment value before
|
|
398
|
+
* schema validation. Exported so a caller deciding whether a variable carries
|
|
399
|
+
* configuration reaches the same verdict this module does — an empty string and
|
|
400
|
+
* an unsubstituted `${…}` placeholder both read as unset — instead of
|
|
401
|
+
* re-implementing those rules and disagreeing with the parser.
|
|
402
|
+
*/
|
|
403
|
+
export { normalizeEnv } from './envValue.js';
|
|
396
404
|
export { parseEnvConfig } from './parseEnvConfig.js';
|
|
397
405
|
export { ConfigSchema, config, parseConfig, resetConfig };
|
|
398
406
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB,wEAAwE;AACxE,eAAO,MAAM,cAAc,2BAA2B,CAAC;AACvD,eAAO,MAAM,iBAAiB,QAAkC,CAAC;AAyBjE,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoWd,CAAC;AAGL,QAAA,MAAM,WAAW,kBAAmB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmMrE,CAAC;AAIF;;;;;;;;GAQG;AACH,QAAA,MAAM,WAAW,kBAAmB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,KAAG,IAExE,CAAC;AAEF;;;;;;GAMG;AACH,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuBV,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAErD;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC"}
|