@cyanheads/workflows-mcp-server 0.3.0 → 0.3.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 +30 -47
- package/CLAUDE.md +30 -47
- package/Dockerfile +10 -0
- package/README.md +1 -1
- package/changelog/0.3.x/0.3.1.md +33 -0
- package/package.json +7 -7
- package/server.json +3 -3
package/AGENTS.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Agent Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** @cyanheads/workflows-mcp-server
|
|
4
|
-
**Version:** 0.3.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.12.
|
|
4
|
+
**Version:** 0.3.1
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.12.5`
|
|
6
6
|
**Engines:** Bun ≥1.3.0, Node ≥24.0.0
|
|
7
7
|
**MCP SDK:** `@modelcontextprotocol/server` ^2.0.0
|
|
8
|
-
**Zod:** ^4.4
|
|
8
|
+
**Zod:** ^4.5.4
|
|
9
9
|
|
|
10
10
|
> **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
|
|
11
11
|
|
|
@@ -112,6 +112,14 @@ export function getServerConfig() {
|
|
|
112
112
|
|
|
113
113
|
`parseEnvConfig` maps Zod schema paths → env var names so errors name the variable (`WORKFLOWS_DIR`) not the path (`workflowsDir`). Throws `ConfigurationError`, which the framework prints as a clean startup banner.
|
|
114
114
|
|
|
115
|
+
For env booleans use `z.stringbool()`, never `z.coerce.boolean()` — `Boolean("false")` is `true`, so a coerced flag can't be disabled through the environment. `z.stringbool()` parses `true/false/1/0/yes/no/on/off` and rejects anything else, so `=false` actually disables.
|
|
116
|
+
|
|
117
|
+
### Server identity
|
|
118
|
+
|
|
119
|
+
Framework identity (`name`, `version`, `description`, `keywords`) and a relative `LOGS_DIR` resolve against the **application root** — the nearest `package.json` at or above the process entry module — never the launching client's working directory. Server-specific paths like `WORKFLOWS_DIR` are this server's own config and still resolve against `process.cwd()`, so a relative default points at the caller's workflow library rather than the installed package.
|
|
120
|
+
|
|
121
|
+
`createApp()` declares `name` + `title` only. `description`, `version`, and `keywords` derive from `package.json` — restating them in the call is drift, not configuration. `instructions` is optional server-level orientation sent on every `initialize`; use it for deployment guidance instead of repeating the same context across tool descriptions.
|
|
122
|
+
|
|
115
123
|
---
|
|
116
124
|
|
|
117
125
|
## Context
|
|
@@ -120,12 +128,15 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
120
128
|
|
|
121
129
|
| Property | Description |
|
|
122
130
|
|:---------|:------------|
|
|
123
|
-
| `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. |
|
|
124
|
-
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. |
|
|
131
|
+
| `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. Dual-sink: Pino **and** `notifications/message` to the client, so treat it as client-visible. |
|
|
132
|
+
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
133
|
+
| `ctx.requestInput` | Suspend and ask the caller for more input — `return ctx.requestInput({ inputRequests: { key: inputRequired.elicit({ message, requestedSchema }) } })`. Never returns; the handler is re-entered with the answers. Always present, but a 2025-era HTTP client cannot answer under `MCP_SESSION_MODE=stateless` — treat an unanswered round as terminal, never as consent. |
|
|
134
|
+
| `ctx.inputs` | Reader over a retried request's responses — `.accepted(key, schema)`, `.view(key)`, `.state()`, `.dropped`. Empty on the first round. |
|
|
125
135
|
| `ctx.enrich` | Success-path agent context that reaches both `structuredContent` and `content[]` when the definition declares an `enrichment` block. |
|
|
136
|
+
| `ctx.content` | Non-text content blocks — `.image(data, mimeType)`, `.audio(data, mimeType)`, or `ctx.content(block)` for a raw block. Prepended to `content[]` after `format()`; never enters `structuredContent`. |
|
|
126
137
|
| `ctx.signal` | `AbortSignal` for cancellation. |
|
|
127
138
|
| `ctx.requestId` | Unique request ID. |
|
|
128
|
-
| `ctx.tenantId` | Tenant ID from JWT
|
|
139
|
+
| `ctx.tenantId` | Tenant ID from JWT; `'default'` for stdio or HTTP with auth off. |
|
|
129
140
|
| `ctx.fail` | Typed error factory for declared error contracts — `ctx.fail('reason', msg, ctx.recoveryFor('reason'))`. |
|
|
130
141
|
|
|
131
142
|
---
|
|
@@ -134,7 +145,7 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
134
145
|
|
|
135
146
|
Handlers throw — the framework catches, classifies, and formats.
|
|
136
147
|
|
|
137
|
-
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required (≥ 5 words, lint-validated) and is the single source of truth for the agent's next move. Pass `ctx.recoveryFor('reason')` as the throw's data to put it on the wire (`data.recovery.hint`, mirrored into `content[]` text); override it explicitly when dynamic runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring.
|
|
148
|
+
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required (≥ 5 words, lint-validated) and is the single source of truth for the agent's next move. Pass `ctx.recoveryFor('reason')` as the throw's data to put it on the wire (`data.recovery.hint`, mirrored into `content[]` text); override it explicitly when dynamic runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`, `RequestCancelled`) bubble freely and don't need declaring.
|
|
138
149
|
|
|
139
150
|
```ts
|
|
140
151
|
errors: [
|
|
@@ -234,7 +245,7 @@ Available skills:
|
|
|
234
245
|
| `tool-defs-analysis` | Read-only audit of MCP definition language across the surface — voice, leaks, defaults, recovery hints, output descriptions |
|
|
235
246
|
| `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
|
|
236
247
|
| `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
|
|
237
|
-
| `
|
|
248
|
+
| `techniques` | Catalog of response/data-shaping techniques — overflow handling, payload shaping, retrieval patterns |
|
|
238
249
|
| `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
239
250
|
| `git-wrapup` | Land working-tree changes as a versioned commit + annotated tag — version bump, changelog, verify, tag. Local only. |
|
|
240
251
|
| `release-and-publish` | Push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
@@ -268,9 +279,14 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
268
279
|
| `bun run clean` | Remove build artifacts |
|
|
269
280
|
| `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
|
|
270
281
|
| `bun run audit:refresh` | Delete `bun.lock`, reinstall, re-audit. Use when `devcheck` flags a transitive advisory — stale lockfile can mask already-patched deps. If advisory survives, it's real. |
|
|
282
|
+
| `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
|
|
283
|
+
| `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity (run by devcheck) |
|
|
284
|
+
| `bun run list-skills` | Print the skill registry |
|
|
271
285
|
| `bun run tree` | Generate directory structure doc |
|
|
272
|
-
| `bun run format` | Auto-fix formatting |
|
|
273
|
-
| `bun run
|
|
286
|
+
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
287
|
+
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
|
|
288
|
+
| `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
|
|
289
|
+
| `bun run test:coverage` | Run tests with Istanbul coverage |
|
|
274
290
|
| `bun run start:stdio` | Production mode (stdio) |
|
|
275
291
|
| `bun run start:http` | Production mode (HTTP) |
|
|
276
292
|
| `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
|
|
@@ -281,44 +297,11 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
281
297
|
|
|
282
298
|
## Bundling
|
|
283
299
|
|
|
284
|
-
`bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
|
|
300
|
+
`bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. The pack step is followed by `scripts/clean-mcpb.ts`, which prunes dev dependencies (`mcpb clean`) and strips two classes of `node_modules/**` content that root-anchored `.mcpbignore` patterns cannot reach: dependency-shipped agent docs (`skills/`, `.claude/`, `.agents/`, `SKILL.md`) and platform-specific native bindings, which would otherwise lock the bundle to the platform it was packed on. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
|
|
285
301
|
|
|
286
302
|
**Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match.
|
|
287
303
|
|
|
288
|
-
**README install badges
|
|
289
|
-
|
|
290
|
-
| Client | Mechanism |
|
|
291
|
-
|:-------|:----------|
|
|
292
|
-
| Claude Desktop | Browser downloads the `.mcpb` from the latest GitHub Release; OS file handler routes it to Claude Desktop, which opens the install dialog. No deep-link URL scheme yet — this is the canonical path. |
|
|
293
|
-
| Cursor | Official `https://cursor.com/en/install-mcp` endpoint with base64 JSON config. |
|
|
294
|
-
| VS Code / Insiders | Official `vscode:mcp/install?...` deep link, wrapped in `https://vscode.dev/redirect?url=` so GitHub-rendered markdown doesn't strip the non-HTTP scheme. |
|
|
295
|
-
| Claude Code / Codex | CLI only (`claude mcp add` / `codex mcp add`); no URL scheme. |
|
|
296
|
-
|
|
297
|
-
```markdown
|
|
298
|
-
[](https://github.com/<OWNER>/<REPO>/releases/latest/download/<PACKAGE_NAME>.mcpb)
|
|
299
|
-
[](https://cursor.com/en/install-mcp?name=<PACKAGE_NAME>&config=<BASE64_CONFIG>)
|
|
300
|
-
[](https://vscode.dev/redirect?url=vscode:mcp/install?<URLENCODED_JSON>)
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
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.
|
|
304
|
-
|
|
305
|
-
Generate the encoded configs (replace `<PACKAGE_NAME>` and add env vars for any required API keys):
|
|
306
|
-
|
|
307
|
-
```bash
|
|
308
|
-
# Cursor: base64-encoded JSON. Split command/args, add env when keys are needed.
|
|
309
|
-
echo -n '{"command":"npx","args":["-y","<PACKAGE_NAME>"],"env":{"API_KEY":"your-api-key"}}' | base64
|
|
310
|
-
# Without env (no required keys):
|
|
311
|
-
echo -n '{"command":"npx","args":["-y","<PACKAGE_NAME>"]}' | base64
|
|
312
|
-
|
|
313
|
-
# VS Code: URL-encoded JSON. Same shape plus a `name` field.
|
|
314
|
-
node -p 'encodeURIComponent(JSON.stringify({name:"<SHORT_NAME>",command:"npx",args:["-y","<PACKAGE_NAME>"],env:{API_KEY:"your-api-key"}}))'
|
|
315
|
-
# Without env:
|
|
316
|
-
node -p 'encodeURIComponent(JSON.stringify({name:"<SHORT_NAME>",command:"npx",args:["-y","<PACKAGE_NAME>"]}))'
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
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`.
|
|
320
|
-
|
|
321
|
-
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.
|
|
304
|
+
**README install badges** (Claude Desktop `.mcpb`, Cursor, VS Code) and the `base64` / `encodeURIComponent` config-generation commands are ship-time concerns — run the `polish-docs-meta` skill, which carries the badge format, layout, and generation snippets in `skills/polish-docs-meta/references/readme.md`.
|
|
322
305
|
|
|
323
306
|
---
|
|
324
307
|
|
|
@@ -332,14 +315,14 @@ Each per-version file opens with YAML frontmatter:
|
|
|
332
315
|
---
|
|
333
316
|
summary: "One-line headline, ≤350 chars" # required — powers the rollup index
|
|
334
317
|
breaking: false # optional — true flags breaking changes
|
|
335
|
-
security: false # optional — true
|
|
318
|
+
security: false # optional — true ONLY for a source-code security fix, never a dependency CVE bump
|
|
336
319
|
---
|
|
337
320
|
|
|
338
321
|
# 0.1.0 — YYYY-MM-DD
|
|
339
322
|
...
|
|
340
323
|
```
|
|
341
324
|
|
|
342
|
-
`breaking: true` renders a `· ⚠️ Breaking` badge — use it when consumers must update code on upgrade (signature changes, removed APIs, config renames). `security: true` renders a `· 🛡️ Security` badge and pairs with a `## Security` body section. When both are set, badges render `· ⚠️ Breaking · 🛡️ Security`.
|
|
325
|
+
`breaking: true` renders a `· ⚠️ Breaking` badge — use it when consumers must update code on upgrade (signature changes, removed APIs, config renames). `security: true` renders a `· 🛡️ Security` badge and pairs with a `## Security` body section — set it only for a security fix in this server's *own source code*, never for a routine dependency or transitive CVE bump (record those under `## Dependencies`). When both are set, badges render `· ⚠️ Breaking · 🛡️ Security`.
|
|
343
326
|
|
|
344
327
|
`agent-notes` is an optional free-form field for maintenance agents processing the release downstream. Content here won't appear in the rendered CHANGELOG — it's consumed by agents running the `maintenance` skill. Use it for adoption instructions that don't fit the human-facing sections: new files to create, fields to populate, one-time migration steps. Omit entirely when there's nothing to say.
|
|
345
328
|
|
package/CLAUDE.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Agent Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** @cyanheads/workflows-mcp-server
|
|
4
|
-
**Version:** 0.3.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.12.
|
|
4
|
+
**Version:** 0.3.1
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.12.5`
|
|
6
6
|
**Engines:** Bun ≥1.3.0, Node ≥24.0.0
|
|
7
7
|
**MCP SDK:** `@modelcontextprotocol/server` ^2.0.0
|
|
8
|
-
**Zod:** ^4.4
|
|
8
|
+
**Zod:** ^4.5.4
|
|
9
9
|
|
|
10
10
|
> **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
|
|
11
11
|
|
|
@@ -112,6 +112,14 @@ export function getServerConfig() {
|
|
|
112
112
|
|
|
113
113
|
`parseEnvConfig` maps Zod schema paths → env var names so errors name the variable (`WORKFLOWS_DIR`) not the path (`workflowsDir`). Throws `ConfigurationError`, which the framework prints as a clean startup banner.
|
|
114
114
|
|
|
115
|
+
For env booleans use `z.stringbool()`, never `z.coerce.boolean()` — `Boolean("false")` is `true`, so a coerced flag can't be disabled through the environment. `z.stringbool()` parses `true/false/1/0/yes/no/on/off` and rejects anything else, so `=false` actually disables.
|
|
116
|
+
|
|
117
|
+
### Server identity
|
|
118
|
+
|
|
119
|
+
Framework identity (`name`, `version`, `description`, `keywords`) and a relative `LOGS_DIR` resolve against the **application root** — the nearest `package.json` at or above the process entry module — never the launching client's working directory. Server-specific paths like `WORKFLOWS_DIR` are this server's own config and still resolve against `process.cwd()`, so a relative default points at the caller's workflow library rather than the installed package.
|
|
120
|
+
|
|
121
|
+
`createApp()` declares `name` + `title` only. `description`, `version`, and `keywords` derive from `package.json` — restating them in the call is drift, not configuration. `instructions` is optional server-level orientation sent on every `initialize`; use it for deployment guidance instead of repeating the same context across tool descriptions.
|
|
122
|
+
|
|
115
123
|
---
|
|
116
124
|
|
|
117
125
|
## Context
|
|
@@ -120,12 +128,15 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
120
128
|
|
|
121
129
|
| Property | Description |
|
|
122
130
|
|:---------|:------------|
|
|
123
|
-
| `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. |
|
|
124
|
-
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. |
|
|
131
|
+
| `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. Dual-sink: Pino **and** `notifications/message` to the client, so treat it as client-visible. |
|
|
132
|
+
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
133
|
+
| `ctx.requestInput` | Suspend and ask the caller for more input — `return ctx.requestInput({ inputRequests: { key: inputRequired.elicit({ message, requestedSchema }) } })`. Never returns; the handler is re-entered with the answers. Always present, but a 2025-era HTTP client cannot answer under `MCP_SESSION_MODE=stateless` — treat an unanswered round as terminal, never as consent. |
|
|
134
|
+
| `ctx.inputs` | Reader over a retried request's responses — `.accepted(key, schema)`, `.view(key)`, `.state()`, `.dropped`. Empty on the first round. |
|
|
125
135
|
| `ctx.enrich` | Success-path agent context that reaches both `structuredContent` and `content[]` when the definition declares an `enrichment` block. |
|
|
136
|
+
| `ctx.content` | Non-text content blocks — `.image(data, mimeType)`, `.audio(data, mimeType)`, or `ctx.content(block)` for a raw block. Prepended to `content[]` after `format()`; never enters `structuredContent`. |
|
|
126
137
|
| `ctx.signal` | `AbortSignal` for cancellation. |
|
|
127
138
|
| `ctx.requestId` | Unique request ID. |
|
|
128
|
-
| `ctx.tenantId` | Tenant ID from JWT
|
|
139
|
+
| `ctx.tenantId` | Tenant ID from JWT; `'default'` for stdio or HTTP with auth off. |
|
|
129
140
|
| `ctx.fail` | Typed error factory for declared error contracts — `ctx.fail('reason', msg, ctx.recoveryFor('reason'))`. |
|
|
130
141
|
|
|
131
142
|
---
|
|
@@ -134,7 +145,7 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
134
145
|
|
|
135
146
|
Handlers throw — the framework catches, classifies, and formats.
|
|
136
147
|
|
|
137
|
-
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required (≥ 5 words, lint-validated) and is the single source of truth for the agent's next move. Pass `ctx.recoveryFor('reason')` as the throw's data to put it on the wire (`data.recovery.hint`, mirrored into `content[]` text); override it explicitly when dynamic runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring.
|
|
148
|
+
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required (≥ 5 words, lint-validated) and is the single source of truth for the agent's next move. Pass `ctx.recoveryFor('reason')` as the throw's data to put it on the wire (`data.recovery.hint`, mirrored into `content[]` text); override it explicitly when dynamic runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`, `RequestCancelled`) bubble freely and don't need declaring.
|
|
138
149
|
|
|
139
150
|
```ts
|
|
140
151
|
errors: [
|
|
@@ -234,7 +245,7 @@ Available skills:
|
|
|
234
245
|
| `tool-defs-analysis` | Read-only audit of MCP definition language across the surface — voice, leaks, defaults, recovery hints, output descriptions |
|
|
235
246
|
| `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
|
|
236
247
|
| `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
|
|
237
|
-
| `
|
|
248
|
+
| `techniques` | Catalog of response/data-shaping techniques — overflow handling, payload shaping, retrieval patterns |
|
|
238
249
|
| `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
239
250
|
| `git-wrapup` | Land working-tree changes as a versioned commit + annotated tag — version bump, changelog, verify, tag. Local only. |
|
|
240
251
|
| `release-and-publish` | Push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
@@ -268,9 +279,14 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
268
279
|
| `bun run clean` | Remove build artifacts |
|
|
269
280
|
| `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
|
|
270
281
|
| `bun run audit:refresh` | Delete `bun.lock`, reinstall, re-audit. Use when `devcheck` flags a transitive advisory — stale lockfile can mask already-patched deps. If advisory survives, it's real. |
|
|
282
|
+
| `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
|
|
283
|
+
| `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity (run by devcheck) |
|
|
284
|
+
| `bun run list-skills` | Print the skill registry |
|
|
271
285
|
| `bun run tree` | Generate directory structure doc |
|
|
272
|
-
| `bun run format` | Auto-fix formatting |
|
|
273
|
-
| `bun run
|
|
286
|
+
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
287
|
+
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
|
|
288
|
+
| `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
|
|
289
|
+
| `bun run test:coverage` | Run tests with Istanbul coverage |
|
|
274
290
|
| `bun run start:stdio` | Production mode (stdio) |
|
|
275
291
|
| `bun run start:http` | Production mode (HTTP) |
|
|
276
292
|
| `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
|
|
@@ -281,44 +297,11 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
281
297
|
|
|
282
298
|
## Bundling
|
|
283
299
|
|
|
284
|
-
`bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
|
|
300
|
+
`bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. The pack step is followed by `scripts/clean-mcpb.ts`, which prunes dev dependencies (`mcpb clean`) and strips two classes of `node_modules/**` content that root-anchored `.mcpbignore` patterns cannot reach: dependency-shipped agent docs (`skills/`, `.claude/`, `.agents/`, `SKILL.md`) and platform-specific native bindings, which would otherwise lock the bundle to the platform it was packed on. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
|
|
285
301
|
|
|
286
302
|
**Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match.
|
|
287
303
|
|
|
288
|
-
**README install badges
|
|
289
|
-
|
|
290
|
-
| Client | Mechanism |
|
|
291
|
-
|:-------|:----------|
|
|
292
|
-
| Claude Desktop | Browser downloads the `.mcpb` from the latest GitHub Release; OS file handler routes it to Claude Desktop, which opens the install dialog. No deep-link URL scheme yet — this is the canonical path. |
|
|
293
|
-
| Cursor | Official `https://cursor.com/en/install-mcp` endpoint with base64 JSON config. |
|
|
294
|
-
| VS Code / Insiders | Official `vscode:mcp/install?...` deep link, wrapped in `https://vscode.dev/redirect?url=` so GitHub-rendered markdown doesn't strip the non-HTTP scheme. |
|
|
295
|
-
| Claude Code / Codex | CLI only (`claude mcp add` / `codex mcp add`); no URL scheme. |
|
|
296
|
-
|
|
297
|
-
```markdown
|
|
298
|
-
[](https://github.com/<OWNER>/<REPO>/releases/latest/download/<PACKAGE_NAME>.mcpb)
|
|
299
|
-
[](https://cursor.com/en/install-mcp?name=<PACKAGE_NAME>&config=<BASE64_CONFIG>)
|
|
300
|
-
[](https://vscode.dev/redirect?url=vscode:mcp/install?<URLENCODED_JSON>)
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
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.
|
|
304
|
-
|
|
305
|
-
Generate the encoded configs (replace `<PACKAGE_NAME>` and add env vars for any required API keys):
|
|
306
|
-
|
|
307
|
-
```bash
|
|
308
|
-
# Cursor: base64-encoded JSON. Split command/args, add env when keys are needed.
|
|
309
|
-
echo -n '{"command":"npx","args":["-y","<PACKAGE_NAME>"],"env":{"API_KEY":"your-api-key"}}' | base64
|
|
310
|
-
# Without env (no required keys):
|
|
311
|
-
echo -n '{"command":"npx","args":["-y","<PACKAGE_NAME>"]}' | base64
|
|
312
|
-
|
|
313
|
-
# VS Code: URL-encoded JSON. Same shape plus a `name` field.
|
|
314
|
-
node -p 'encodeURIComponent(JSON.stringify({name:"<SHORT_NAME>",command:"npx",args:["-y","<PACKAGE_NAME>"],env:{API_KEY:"your-api-key"}}))'
|
|
315
|
-
# Without env:
|
|
316
|
-
node -p 'encodeURIComponent(JSON.stringify({name:"<SHORT_NAME>",command:"npx",args:["-y","<PACKAGE_NAME>"]}))'
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
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`.
|
|
320
|
-
|
|
321
|
-
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.
|
|
304
|
+
**README install badges** (Claude Desktop `.mcpb`, Cursor, VS Code) and the `base64` / `encodeURIComponent` config-generation commands are ship-time concerns — run the `polish-docs-meta` skill, which carries the badge format, layout, and generation snippets in `skills/polish-docs-meta/references/readme.md`.
|
|
322
305
|
|
|
323
306
|
---
|
|
324
307
|
|
|
@@ -332,14 +315,14 @@ Each per-version file opens with YAML frontmatter:
|
|
|
332
315
|
---
|
|
333
316
|
summary: "One-line headline, ≤350 chars" # required — powers the rollup index
|
|
334
317
|
breaking: false # optional — true flags breaking changes
|
|
335
|
-
security: false # optional — true
|
|
318
|
+
security: false # optional — true ONLY for a source-code security fix, never a dependency CVE bump
|
|
336
319
|
---
|
|
337
320
|
|
|
338
321
|
# 0.1.0 — YYYY-MM-DD
|
|
339
322
|
...
|
|
340
323
|
```
|
|
341
324
|
|
|
342
|
-
`breaking: true` renders a `· ⚠️ Breaking` badge — use it when consumers must update code on upgrade (signature changes, removed APIs, config renames). `security: true` renders a `· 🛡️ Security` badge and pairs with a `## Security` body section. When both are set, badges render `· ⚠️ Breaking · 🛡️ Security`.
|
|
325
|
+
`breaking: true` renders a `· ⚠️ Breaking` badge — use it when consumers must update code on upgrade (signature changes, removed APIs, config renames). `security: true` renders a `· 🛡️ Security` badge and pairs with a `## Security` body section — set it only for a security fix in this server's *own source code*, never for a routine dependency or transitive CVE bump (record those under `## Dependencies`). When both are set, badges render `· ⚠️ Breaking · 🛡️ Security`.
|
|
343
326
|
|
|
344
327
|
`agent-notes` is an optional free-form field for maintenance agents processing the release downstream. Content here won't appear in the rendered CHANGELOG — it's consumed by agents running the `maintenance` skill. Use it for adoption instructions that don't fit the human-facing sections: new files to create, fields to populate, one-time migration steps. Omit entirely when there's nothing to say.
|
|
345
328
|
|
package/Dockerfile
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
#
|
|
4
4
|
# This stage installs all dependencies (including dev), builds the TypeScript
|
|
5
5
|
# source code into JavaScript, and prepares the production assets.
|
|
6
|
+
#
|
|
7
|
+
# Pinned to $BUILDPLATFORM rather than the target platform: `bun run build` emits
|
|
8
|
+
# JavaScript, and only `dist/` crosses into the production stage, which runs its
|
|
9
|
+
# own target-arch install. Built for the target instead, the non-native leg of a
|
|
10
|
+
# `--platform linux/amd64,linux/arm64` build runs under QEMU, where bun >= 1.4
|
|
11
|
+
# aborts with a JavaScriptCore allocator assertion and fails the multi-arch push.
|
|
12
|
+
#
|
|
13
|
+
# The constraint this assumes: the build stage produces platform-independent
|
|
14
|
+
# output. A stage that compiles a native addon needs the target-arch toolchain
|
|
15
|
+
# and cannot cross-compile this way — drop the flag there.
|
|
6
16
|
# ==============================================================================
|
|
7
17
|
FROM --platform=$BUILDPLATFORM oven/bun:1.4.0 AS build
|
|
8
18
|
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
<div align="center">
|
|
9
9
|
|
|
10
|
-
[](./CHANGELOG.md) [](./LICENSE) [](https://github.com/users/cyanheads/packages/container/package/workflows-mcp-server) [](https://modelcontextprotocol.io/) [](https://www.npmjs.com/package/@cyanheads/workflows-mcp-server) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
11
11
|
|
|
12
12
|
</div>
|
|
13
13
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Server identity now resolves from the served package instead of the caller's working directory, pre-init logs are no longer dropped, and MCP_SESSION_MODE settles to stateless. mcp-ts-core bumps to 0.12.5, zod to 4.5.4."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.3.1 — 2026-09-04
|
|
8
|
+
|
|
9
|
+
## Changed
|
|
10
|
+
|
|
11
|
+
- **`.env.example` `MCP_SESSION_MODE`: `auto` → `stateless`**, matching the Docker image — this server declares no `ctx.requestInput` call sites and so needs no session-backed 2025-era elicitation shim. Verified: `GET /mcp` now advertises `sessionMode: "stateless"`.
|
|
12
|
+
- **Server identity resolves from the served package, not the caller's working directory** (mcp-ts-core 0.12.5) — `resolveAppRoot()` reads the nearest `package.json` above the process entry module. Verified: launched from a scratch directory whose own `package.json` declared an unrelated project, `GET /mcp` still returned this server's own name, version, and description.
|
|
13
|
+
- **Pre-init log records are replayed instead of dropped** (mcp-ts-core 0.12.5) — verified in the startup log: `createStorageProvider` and `OpenRouterProvider.constructor` records now appear despite predating `loggerInit`.
|
|
14
|
+
- A caller disconnect mid-call now classifies as `RequestCancelled` (-32011) rather than `InternalError`, and the HTTP transport answers 499 (mcp-ts-core 0.12.4).
|
|
15
|
+
- `manifest.json` `author.name`: `Casey Hand` → `cyanheads`, matching fleet convention.
|
|
16
|
+
- Dockerfile build stage carries a new comment explaining why it pins `$BUILDPLATFORM`.
|
|
17
|
+
- Skills and agent docs synced with mcp-ts-core 0.12.5 — 11 `skills/` files plus the `CLAUDE.md`/`AGENTS.md` templates.
|
|
18
|
+
|
|
19
|
+
## Dependencies
|
|
20
|
+
|
|
21
|
+
Runtime:
|
|
22
|
+
|
|
23
|
+
- `@cyanheads/mcp-ts-core` `^0.12.3` → `^0.12.5`
|
|
24
|
+
- `zod` `^4.4.3` → `^4.5.4`
|
|
25
|
+
|
|
26
|
+
Development:
|
|
27
|
+
|
|
28
|
+
- `@biomejs/biome` `2.5.9` → `2.5.11`
|
|
29
|
+
- `@types/node` `26.2.0` → `26.4.0`
|
|
30
|
+
- `ignore` `^7.0.6` → `^7.0.8`
|
|
31
|
+
- `tsc-alias` `^1.9.2` → `^1.9.3`
|
|
32
|
+
|
|
33
|
+
The lockfile re-resolve also cleared 8 transitive advisories (`fast-uri` `3.1.5` → `3.1.6`, `browserslist` `4.28.4` → `4.28.8`, `qs` `6.15.3` → `6.16.0`); `bun audit` now reports no vulnerabilities across 375 packages.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyanheads/workflows-mcp-server",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"mcpName": "io.github.cyanheads/workflows-mcp-server",
|
|
5
5
|
"description": "Store, query, and create YAML workflow playbooks for LLM agents via MCP. STDIO or Streamable HTTP.",
|
|
6
6
|
"type": "module",
|
|
@@ -85,22 +85,22 @@
|
|
|
85
85
|
"access": "public"
|
|
86
86
|
},
|
|
87
87
|
"dependencies": {
|
|
88
|
-
"@cyanheads/mcp-ts-core": "^0.12.
|
|
88
|
+
"@cyanheads/mcp-ts-core": "^0.12.5",
|
|
89
89
|
"pino-pretty": "^13.1.3",
|
|
90
90
|
"semver": "^7.8.5",
|
|
91
91
|
"yaml": "^2.9.0",
|
|
92
|
-
"zod": "^4.4
|
|
92
|
+
"zod": "^4.5.4"
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
|
-
"@biomejs/biome": "2.5.
|
|
95
|
+
"@biomejs/biome": "2.5.11",
|
|
96
96
|
"@socketsecurity/bun-security-scanner": "^1.1.2",
|
|
97
|
-
"@types/node": "26.
|
|
97
|
+
"@types/node": "26.4.0",
|
|
98
98
|
"@types/semver": "^7.8.0",
|
|
99
99
|
"@vitest/coverage-istanbul": "4.1.11",
|
|
100
100
|
"depcheck": "^1.4.7",
|
|
101
101
|
"fast-check": "^4.9.0",
|
|
102
|
-
"ignore": "^7.0.
|
|
103
|
-
"tsc-alias": "^1.9.
|
|
102
|
+
"ignore": "^7.0.8",
|
|
103
|
+
"tsc-alias": "^1.9.3",
|
|
104
104
|
"typescript": "^7.0.2",
|
|
105
105
|
"vitest": "^4.1.11"
|
|
106
106
|
}
|
package/server.json
CHANGED
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
"url": "https://github.com/cyanheads/workflows-mcp-server",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.1",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
14
14
|
"identifier": "@cyanheads/workflows-mcp-server",
|
|
15
15
|
"runtimeHint": "bun",
|
|
16
|
-
"version": "0.3.
|
|
16
|
+
"version": "0.3.1",
|
|
17
17
|
"packageArguments": [
|
|
18
18
|
{ "type": "positional", "value": "run" },
|
|
19
19
|
{ "type": "positional", "value": "start:stdio" }
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
58
58
|
"identifier": "@cyanheads/workflows-mcp-server",
|
|
59
59
|
"runtimeHint": "bun",
|
|
60
|
-
"version": "0.3.
|
|
60
|
+
"version": "0.3.1",
|
|
61
61
|
"packageArguments": [
|
|
62
62
|
{ "type": "positional", "value": "run" },
|
|
63
63
|
{ "type": "positional", "value": "start:http" }
|