@cyanheads/workflows-mcp-server 0.3.0 → 0.3.2
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 +46 -61
- package/CLAUDE.md +46 -61
- package/Dockerfile +10 -0
- package/README.md +4 -3
- package/changelog/0.3.x/0.3.1.md +33 -0
- package/changelog/0.3.x/0.3.2.md +49 -0
- package/changelog/template.md +7 -24
- package/package.json +11 -10
- 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.
|
|
6
|
-
**Engines:** Bun ≥1.
|
|
4
|
+
**Version:** 0.3.2
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.0`
|
|
6
|
+
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
7
|
**MCP SDK:** `@modelcontextprotocol/server` ^2.0.0
|
|
8
|
-
**Zod:** ^4.
|
|
8
|
+
**Zod:** ^4.6.1
|
|
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
|
|
|
@@ -37,6 +37,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
|
|
|
37
37
|
- **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
|
|
38
38
|
- **Need caller input?** Return `ctx.requestInput(...)`; the handler is re-entered with answers in `ctx.inputs`. Never await input mid-handler.
|
|
39
39
|
- **Secrets in env vars only** — never hardcoded.
|
|
40
|
+
- **Cut noise.** Add only what earns its place: no speculative generality, no guards for states the framework already prevents (Zod-validated params, classified errors), no abstraction until a third caller proves it, no option nothing sets.
|
|
40
41
|
- **Close the loop on issues.** When implementing work tracked by a GitHub issue, comment on the issue with what landed and close it. Do both — a comment without a close leaves stale issues open; a close without a comment leaves no record of what shipped. The comment is for future readers — state the concrete changes, not the conversation that produced them.
|
|
41
42
|
|
|
42
43
|
---
|
|
@@ -112,6 +113,14 @@ export function getServerConfig() {
|
|
|
112
113
|
|
|
113
114
|
`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
115
|
|
|
116
|
+
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.
|
|
117
|
+
|
|
118
|
+
### Server identity
|
|
119
|
+
|
|
120
|
+
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.
|
|
121
|
+
|
|
122
|
+
`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.
|
|
123
|
+
|
|
115
124
|
---
|
|
116
125
|
|
|
117
126
|
## Context
|
|
@@ -120,12 +129,15 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
120
129
|
|
|
121
130
|
| Property | Description |
|
|
122
131
|
|:---------|:------------|
|
|
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 })`. |
|
|
132
|
+
| `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. |
|
|
133
|
+
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
134
|
+
| `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. |
|
|
135
|
+
| `ctx.inputs` | Reader over a retried request's responses — `.accepted(key, schema)`, `.view(key)`, `.state()`, `.dropped`. Empty on the first round. |
|
|
125
136
|
| `ctx.enrich` | Success-path agent context that reaches both `structuredContent` and `content[]` when the definition declares an `enrichment` block. |
|
|
137
|
+
| `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
138
|
| `ctx.signal` | `AbortSignal` for cancellation. |
|
|
127
139
|
| `ctx.requestId` | Unique request ID. |
|
|
128
|
-
| `ctx.tenantId` | Tenant ID from JWT
|
|
140
|
+
| `ctx.tenantId` | Tenant ID from JWT; `'default'` for stdio or HTTP with auth off. |
|
|
129
141
|
| `ctx.fail` | Typed error factory for declared error contracts — `ctx.fail('reason', msg, ctx.recoveryFor('reason'))`. |
|
|
130
142
|
|
|
131
143
|
---
|
|
@@ -134,7 +146,7 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
134
146
|
|
|
135
147
|
Handlers throw — the framework catches, classifies, and formats.
|
|
136
148
|
|
|
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.
|
|
149
|
+
**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
150
|
|
|
139
151
|
```ts
|
|
140
152
|
errors: [
|
|
@@ -165,7 +177,7 @@ throw new Error('Invalid query format'); // → ValidationError
|
|
|
165
177
|
|
|
166
178
|
// McpError — when no factory exists for the code
|
|
167
179
|
import { McpError, JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
168
|
-
throw new McpError(JsonRpcErrorCode.
|
|
180
|
+
throw new McpError(JsonRpcErrorCode.InitializationFailed, 'Connection failed', { pool: 'primary' });
|
|
169
181
|
```
|
|
170
182
|
|
|
171
183
|
See framework CLAUDE.md and the `api-errors` skill for the full auto-classification table, all available factories, and the contract reference.
|
|
@@ -189,6 +201,7 @@ src/
|
|
|
189
201
|
workflow-get.tool.ts # workflow_get — retrieve full workflow + global instructions
|
|
190
202
|
workflow-create.tool.ts # workflow_create — write permanent workflow YAML
|
|
191
203
|
workflow-create-temp.tool.ts # workflow_create_temp — write temporary workflow
|
|
204
|
+
workflow-delete.tool.ts # workflow_delete — remove permanent workflow
|
|
192
205
|
index.ts # Barrel export
|
|
193
206
|
workflows-yaml/ # Workflow library root (configurable via WORKFLOWS_DIR)
|
|
194
207
|
categories/ # Permanent workflows organized by category
|
|
@@ -212,9 +225,9 @@ workflows-yaml/ # Workflow library root (configurable vi
|
|
|
212
225
|
|
|
213
226
|
## Skills
|
|
214
227
|
|
|
215
|
-
Skills are modular instructions in `skills/` at the project root. Read them directly when a task matches — e.g., `skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry.
|
|
228
|
+
Skills are modular instructions in `framework-skills/` at the project root. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry. Keep development skills out of root `skills/`, which plugin hosts load for installing agents.
|
|
216
229
|
|
|
217
|
-
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
230
|
+
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `framework-skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
218
231
|
|
|
219
232
|
Available skills:
|
|
220
233
|
|
|
@@ -228,16 +241,15 @@ Available skills:
|
|
|
228
241
|
| `add-prompt` | Scaffold a new prompt definition |
|
|
229
242
|
| `add-service` | Scaffold a new service integration |
|
|
230
243
|
| `add-test` | Scaffold test file for a tool, resource, or service |
|
|
231
|
-
| `add-export` | Add or evolve a public framework export without leaking internal module paths |
|
|
232
|
-
| `add-provider` | Add a provider integration with config, lifecycle, errors, and tests |
|
|
233
244
|
| `field-test` | Exercise tools/resources/prompts with real inputs, verify behavior, report issues |
|
|
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
|
-
| `git-wrapup` | Land working-tree changes as a
|
|
240
|
-
| `release-
|
|
250
|
+
| `git-wrapup` | Land working-tree changes as a commit stack — version bump, changelog, verify, commit by concern. Opens a release PR only when the project declares release PR mode |
|
|
251
|
+
| `release-pr-review` | Review an open release PR, autosquash fixes into the stack, and keep the PR body in sync. Release PR mode only |
|
|
252
|
+
| `release-and-publish` | Tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
241
253
|
| `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
|
|
242
254
|
| `orchestrations` | Chain task skills into a gated multi-phase pipeline when sub-agents are available |
|
|
243
255
|
| `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
|
|
@@ -267,10 +279,16 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
267
279
|
| `bun run rebuild` | Clean + build |
|
|
268
280
|
| `bun run clean` | Remove build artifacts |
|
|
269
281
|
| `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
|
|
270
|
-
| `bun run audit:
|
|
282
|
+
| `bun run audit:fix` | Upgrade vulnerable packages to the lowest safe version within existing ranges; `--dry-run` previews |
|
|
283
|
+
| `bun run audit:refresh` | Delete `bun.lock` and reinstall. Last resort after `audit:fix`, `bun update <name>`, and `bun dedupe`; re-resolves every ranged dependency |
|
|
284
|
+
| `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
|
|
285
|
+
| `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity (run by devcheck) |
|
|
286
|
+
| `bun run list-skills` | Print the skill registry |
|
|
271
287
|
| `bun run tree` | Generate directory structure doc |
|
|
272
|
-
| `bun run format` | Auto-fix formatting |
|
|
273
|
-
| `bun run
|
|
288
|
+
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
289
|
+
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
|
|
290
|
+
| `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
|
|
291
|
+
| `bun run test:coverage` | Run tests with Istanbul coverage |
|
|
274
292
|
| `bun run start:stdio` | Production mode (stdio) |
|
|
275
293
|
| `bun run start:http` | Production mode (HTTP) |
|
|
276
294
|
| `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
|
|
@@ -281,44 +299,11 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
281
299
|
|
|
282
300
|
## Bundling
|
|
283
301
|
|
|
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.
|
|
285
|
-
|
|
286
|
-
**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
|
-
|
|
288
|
-
**README install badges.** Drop these into the project README to give users one-click install paths. Fill in `<OWNER>` / `<REPO>` / `<PACKAGE_NAME>` and encode the per-server config. Cursor + VS Code badges assume the server is published to npm; Claude Desktop downloads the `.mcpb` directly so npm publishing isn't required.
|
|
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
|
-
```
|
|
302
|
+
`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 (`framework-skills/`, `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.
|
|
318
303
|
|
|
319
|
-
|
|
304
|
+
**Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). Wire each option through `${user_config.<key>}` and give optional strings `default: ""`. `lint:packaging` checks wiring and env-name parity.
|
|
320
305
|
|
|
321
|
-
|
|
306
|
+
**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 `framework-skills/polish-docs-meta/references/readme.md`.
|
|
322
307
|
|
|
323
308
|
---
|
|
324
309
|
|
|
@@ -332,18 +317,18 @@ Each per-version file opens with YAML frontmatter:
|
|
|
332
317
|
---
|
|
333
318
|
summary: "One-line headline, ≤350 chars" # required — powers the rollup index
|
|
334
319
|
breaking: false # optional — true flags breaking changes
|
|
335
|
-
security: false # optional — true
|
|
320
|
+
security: false # optional — true ONLY for a source-code security fix, never a dependency CVE bump
|
|
336
321
|
---
|
|
337
322
|
|
|
338
323
|
# 0.1.0 — YYYY-MM-DD
|
|
339
324
|
...
|
|
340
325
|
```
|
|
341
326
|
|
|
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`.
|
|
327
|
+
`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
328
|
|
|
344
329
|
`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
330
|
|
|
346
|
-
**Section order** (Keep a Changelog): Added, Changed, Deprecated, Removed, Fixed, Security. Include only sections with entries — don't ship empty headers.
|
|
331
|
+
**Section order** (Keep a Changelog): Added, Changed, Deprecated, Removed, Fixed, Security, then Dependencies. Include only sections with entries — don't ship empty headers.
|
|
347
332
|
|
|
348
333
|
**Tag annotations** render as GitHub Release bodies via `--notes-from-tag`. They must be structured markdown — never a flat comma-separated string. Subject omits the version number (GitHub prepends it). See `changelog/template.md` for the full format reference.
|
|
349
334
|
|
|
@@ -375,7 +360,7 @@ import { getMyService } from '@/services/my-domain/my-service.js';
|
|
|
375
360
|
- [ ] If wrapping external API: tests include at least one sparse payload case with omitted upstream fields
|
|
376
361
|
- [ ] Registered in `createApp()` arrays (directly or via barrel exports)
|
|
377
362
|
- [ ] Tests use `createMockContext()` from `@cyanheads/mcp-ts-core/testing`
|
|
378
|
-
- [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` =
|
|
379
|
-
- [ ] `.codex-plugin/mcp.json` updated — server
|
|
380
|
-
- [ ] `.claude-plugin/plugin.json` populated —
|
|
363
|
+
- [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` = unscoped repo name; `interface.shortDescription` from `package.json` description
|
|
364
|
+
- [ ] `.codex-plugin/mcp.json` updated — server key is the unscoped repo name; user-supplied variables appear in `env_vars` so Codex forwards the host environment
|
|
365
|
+
- [ ] `.claude-plugin/plugin.json` populated — metadata from `package.json`; inline `mcpServers` keyed by the unscoped repo name. User-supplied variables are declared in `userConfig` and referenced through `${user_config.<option>}`, mirroring `manifest.json`. Optional strings have `default: ""`
|
|
381
366
|
- [ ] `bun run devcheck` passes
|
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.
|
|
6
|
-
**Engines:** Bun ≥1.
|
|
4
|
+
**Version:** 0.3.2
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.0`
|
|
6
|
+
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
7
|
**MCP SDK:** `@modelcontextprotocol/server` ^2.0.0
|
|
8
|
-
**Zod:** ^4.
|
|
8
|
+
**Zod:** ^4.6.1
|
|
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
|
|
|
@@ -37,6 +37,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
|
|
|
37
37
|
- **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
|
|
38
38
|
- **Need caller input?** Return `ctx.requestInput(...)`; the handler is re-entered with answers in `ctx.inputs`. Never await input mid-handler.
|
|
39
39
|
- **Secrets in env vars only** — never hardcoded.
|
|
40
|
+
- **Cut noise.** Add only what earns its place: no speculative generality, no guards for states the framework already prevents (Zod-validated params, classified errors), no abstraction until a third caller proves it, no option nothing sets.
|
|
40
41
|
- **Close the loop on issues.** When implementing work tracked by a GitHub issue, comment on the issue with what landed and close it. Do both — a comment without a close leaves stale issues open; a close without a comment leaves no record of what shipped. The comment is for future readers — state the concrete changes, not the conversation that produced them.
|
|
41
42
|
|
|
42
43
|
---
|
|
@@ -112,6 +113,14 @@ export function getServerConfig() {
|
|
|
112
113
|
|
|
113
114
|
`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
115
|
|
|
116
|
+
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.
|
|
117
|
+
|
|
118
|
+
### Server identity
|
|
119
|
+
|
|
120
|
+
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.
|
|
121
|
+
|
|
122
|
+
`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.
|
|
123
|
+
|
|
115
124
|
---
|
|
116
125
|
|
|
117
126
|
## Context
|
|
@@ -120,12 +129,15 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
120
129
|
|
|
121
130
|
| Property | Description |
|
|
122
131
|
|:---------|:------------|
|
|
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 })`. |
|
|
132
|
+
| `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. |
|
|
133
|
+
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
134
|
+
| `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. |
|
|
135
|
+
| `ctx.inputs` | Reader over a retried request's responses — `.accepted(key, schema)`, `.view(key)`, `.state()`, `.dropped`. Empty on the first round. |
|
|
125
136
|
| `ctx.enrich` | Success-path agent context that reaches both `structuredContent` and `content[]` when the definition declares an `enrichment` block. |
|
|
137
|
+
| `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
138
|
| `ctx.signal` | `AbortSignal` for cancellation. |
|
|
127
139
|
| `ctx.requestId` | Unique request ID. |
|
|
128
|
-
| `ctx.tenantId` | Tenant ID from JWT
|
|
140
|
+
| `ctx.tenantId` | Tenant ID from JWT; `'default'` for stdio or HTTP with auth off. |
|
|
129
141
|
| `ctx.fail` | Typed error factory for declared error contracts — `ctx.fail('reason', msg, ctx.recoveryFor('reason'))`. |
|
|
130
142
|
|
|
131
143
|
---
|
|
@@ -134,7 +146,7 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
134
146
|
|
|
135
147
|
Handlers throw — the framework catches, classifies, and formats.
|
|
136
148
|
|
|
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.
|
|
149
|
+
**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
150
|
|
|
139
151
|
```ts
|
|
140
152
|
errors: [
|
|
@@ -165,7 +177,7 @@ throw new Error('Invalid query format'); // → ValidationError
|
|
|
165
177
|
|
|
166
178
|
// McpError — when no factory exists for the code
|
|
167
179
|
import { McpError, JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
168
|
-
throw new McpError(JsonRpcErrorCode.
|
|
180
|
+
throw new McpError(JsonRpcErrorCode.InitializationFailed, 'Connection failed', { pool: 'primary' });
|
|
169
181
|
```
|
|
170
182
|
|
|
171
183
|
See framework CLAUDE.md and the `api-errors` skill for the full auto-classification table, all available factories, and the contract reference.
|
|
@@ -189,6 +201,7 @@ src/
|
|
|
189
201
|
workflow-get.tool.ts # workflow_get — retrieve full workflow + global instructions
|
|
190
202
|
workflow-create.tool.ts # workflow_create — write permanent workflow YAML
|
|
191
203
|
workflow-create-temp.tool.ts # workflow_create_temp — write temporary workflow
|
|
204
|
+
workflow-delete.tool.ts # workflow_delete — remove permanent workflow
|
|
192
205
|
index.ts # Barrel export
|
|
193
206
|
workflows-yaml/ # Workflow library root (configurable via WORKFLOWS_DIR)
|
|
194
207
|
categories/ # Permanent workflows organized by category
|
|
@@ -212,9 +225,9 @@ workflows-yaml/ # Workflow library root (configurable vi
|
|
|
212
225
|
|
|
213
226
|
## Skills
|
|
214
227
|
|
|
215
|
-
Skills are modular instructions in `skills/` at the project root. Read them directly when a task matches — e.g., `skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry.
|
|
228
|
+
Skills are modular instructions in `framework-skills/` at the project root. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry. Keep development skills out of root `skills/`, which plugin hosts load for installing agents.
|
|
216
229
|
|
|
217
|
-
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
230
|
+
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `framework-skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
218
231
|
|
|
219
232
|
Available skills:
|
|
220
233
|
|
|
@@ -228,16 +241,15 @@ Available skills:
|
|
|
228
241
|
| `add-prompt` | Scaffold a new prompt definition |
|
|
229
242
|
| `add-service` | Scaffold a new service integration |
|
|
230
243
|
| `add-test` | Scaffold test file for a tool, resource, or service |
|
|
231
|
-
| `add-export` | Add or evolve a public framework export without leaking internal module paths |
|
|
232
|
-
| `add-provider` | Add a provider integration with config, lifecycle, errors, and tests |
|
|
233
244
|
| `field-test` | Exercise tools/resources/prompts with real inputs, verify behavior, report issues |
|
|
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
|
-
| `git-wrapup` | Land working-tree changes as a
|
|
240
|
-
| `release-
|
|
250
|
+
| `git-wrapup` | Land working-tree changes as a commit stack — version bump, changelog, verify, commit by concern. Opens a release PR only when the project declares release PR mode |
|
|
251
|
+
| `release-pr-review` | Review an open release PR, autosquash fixes into the stack, and keep the PR body in sync. Release PR mode only |
|
|
252
|
+
| `release-and-publish` | Tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
241
253
|
| `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
|
|
242
254
|
| `orchestrations` | Chain task skills into a gated multi-phase pipeline when sub-agents are available |
|
|
243
255
|
| `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
|
|
@@ -267,10 +279,16 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
267
279
|
| `bun run rebuild` | Clean + build |
|
|
268
280
|
| `bun run clean` | Remove build artifacts |
|
|
269
281
|
| `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
|
|
270
|
-
| `bun run audit:
|
|
282
|
+
| `bun run audit:fix` | Upgrade vulnerable packages to the lowest safe version within existing ranges; `--dry-run` previews |
|
|
283
|
+
| `bun run audit:refresh` | Delete `bun.lock` and reinstall. Last resort after `audit:fix`, `bun update <name>`, and `bun dedupe`; re-resolves every ranged dependency |
|
|
284
|
+
| `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
|
|
285
|
+
| `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity (run by devcheck) |
|
|
286
|
+
| `bun run list-skills` | Print the skill registry |
|
|
271
287
|
| `bun run tree` | Generate directory structure doc |
|
|
272
|
-
| `bun run format` | Auto-fix formatting |
|
|
273
|
-
| `bun run
|
|
288
|
+
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
289
|
+
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
|
|
290
|
+
| `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
|
|
291
|
+
| `bun run test:coverage` | Run tests with Istanbul coverage |
|
|
274
292
|
| `bun run start:stdio` | Production mode (stdio) |
|
|
275
293
|
| `bun run start:http` | Production mode (HTTP) |
|
|
276
294
|
| `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
|
|
@@ -281,44 +299,11 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
281
299
|
|
|
282
300
|
## Bundling
|
|
283
301
|
|
|
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.
|
|
285
|
-
|
|
286
|
-
**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
|
-
|
|
288
|
-
**README install badges.** Drop these into the project README to give users one-click install paths. Fill in `<OWNER>` / `<REPO>` / `<PACKAGE_NAME>` and encode the per-server config. Cursor + VS Code badges assume the server is published to npm; Claude Desktop downloads the `.mcpb` directly so npm publishing isn't required.
|
|
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
|
-
```
|
|
302
|
+
`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 (`framework-skills/`, `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.
|
|
318
303
|
|
|
319
|
-
|
|
304
|
+
**Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). Wire each option through `${user_config.<key>}` and give optional strings `default: ""`. `lint:packaging` checks wiring and env-name parity.
|
|
320
305
|
|
|
321
|
-
|
|
306
|
+
**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 `framework-skills/polish-docs-meta/references/readme.md`.
|
|
322
307
|
|
|
323
308
|
---
|
|
324
309
|
|
|
@@ -332,18 +317,18 @@ Each per-version file opens with YAML frontmatter:
|
|
|
332
317
|
---
|
|
333
318
|
summary: "One-line headline, ≤350 chars" # required — powers the rollup index
|
|
334
319
|
breaking: false # optional — true flags breaking changes
|
|
335
|
-
security: false # optional — true
|
|
320
|
+
security: false # optional — true ONLY for a source-code security fix, never a dependency CVE bump
|
|
336
321
|
---
|
|
337
322
|
|
|
338
323
|
# 0.1.0 — YYYY-MM-DD
|
|
339
324
|
...
|
|
340
325
|
```
|
|
341
326
|
|
|
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`.
|
|
327
|
+
`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
328
|
|
|
344
329
|
`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
330
|
|
|
346
|
-
**Section order** (Keep a Changelog): Added, Changed, Deprecated, Removed, Fixed, Security. Include only sections with entries — don't ship empty headers.
|
|
331
|
+
**Section order** (Keep a Changelog): Added, Changed, Deprecated, Removed, Fixed, Security, then Dependencies. Include only sections with entries — don't ship empty headers.
|
|
347
332
|
|
|
348
333
|
**Tag annotations** render as GitHub Release bodies via `--notes-from-tag`. They must be structured markdown — never a flat comma-separated string. Subject omits the version number (GitHub prepends it). See `changelog/template.md` for the full format reference.
|
|
349
334
|
|
|
@@ -375,7 +360,7 @@ import { getMyService } from '@/services/my-domain/my-service.js';
|
|
|
375
360
|
- [ ] If wrapping external API: tests include at least one sparse payload case with omitted upstream fields
|
|
376
361
|
- [ ] Registered in `createApp()` arrays (directly or via barrel exports)
|
|
377
362
|
- [ ] Tests use `createMockContext()` from `@cyanheads/mcp-ts-core/testing`
|
|
378
|
-
- [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` =
|
|
379
|
-
- [ ] `.codex-plugin/mcp.json` updated — server
|
|
380
|
-
- [ ] `.claude-plugin/plugin.json` populated —
|
|
363
|
+
- [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` = unscoped repo name; `interface.shortDescription` from `package.json` description
|
|
364
|
+
- [ ] `.codex-plugin/mcp.json` updated — server key is the unscoped repo name; user-supplied variables appear in `env_vars` so Codex forwards the host environment
|
|
365
|
+
- [ ] `.claude-plugin/plugin.json` populated — metadata from `package.json`; inline `mcpServers` keyed by the unscoped repo name. User-supplied variables are declared in `userConfig` and referenced through `${user_config.<option>}`, mirroring `manifest.json`. Optional strings have `default: ""`
|
|
381
366
|
- [ ] `bun run devcheck` passes
|
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
|
|
|
@@ -191,7 +191,7 @@ The repository ships a `workflows-yaml/` directory with example workflows organi
|
|
|
191
191
|
|
|
192
192
|
### Prerequisites
|
|
193
193
|
|
|
194
|
-
- [Bun v1.
|
|
194
|
+
- [Bun v1.4.0](https://bun.sh/) or higher (or Node.js v24+).
|
|
195
195
|
- A local directory containing YAML workflow files (or use the bundled `workflows-yaml/` seed).
|
|
196
196
|
|
|
197
197
|
### Installation
|
|
@@ -232,6 +232,7 @@ cp .env.example .env
|
|
|
232
232
|
| `WATCHER_DEBOUNCE_MS` | Milliseconds to debounce filesystem change events before rebuilding the index. | `500` |
|
|
233
233
|
| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
|
|
234
234
|
| `MCP_HTTP_PORT` | Port for HTTP server. | `3010` |
|
|
235
|
+
| `MCP_SESSION_MODE` | HTTP sessions: `auto`, `stateful`, or `stateless`. This server needs no caller-input round trips. | `stateless` in Docker and `.env.example`; framework `auto` resolves to `stateful` |
|
|
235
236
|
| `MCP_AUTH_MODE` | Auth mode: `none`, `jwt`, or `oauth`. | `none` |
|
|
236
237
|
| `MCP_LOG_LEVEL` | Log level (RFC 5424). | `info` |
|
|
237
238
|
| `OTEL_ENABLED` | Enable [OpenTelemetry instrumentation](https://github.com/cyanheads/mcp-ts-core/tree/main/docs/telemetry) (spans, metrics, completion logs). | `false` |
|
|
@@ -305,7 +306,7 @@ See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rule
|
|
|
305
306
|
|
|
306
307
|
## Contributing
|
|
307
308
|
|
|
308
|
-
Issues
|
|
309
|
+
Issues are welcome. Run checks and tests before submitting:
|
|
309
310
|
|
|
310
311
|
```sh
|
|
311
312
|
bun run devcheck
|
|
@@ -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.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "The development skill tree moves out of the plugin-loaded skills/ path, and MCPB and plugin installers now collect the workflow directory, instructions path, debounce, and log level. Adopts mcp-ts-core 0.13.0; the Bun engines floor rises to 1.4.0."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.3.2 — 2026-09-13
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **Installers collect this server's options** — `manifest.json` `user_config` and `.claude-plugin/plugin.json` `userConfig` declare `WORKFLOWS_DIR`, `GLOBAL_INSTRUCTIONS_PATH`, `WATCHER_DEBOUNCE_MS`, and `MCP_LOG_LEVEL`, each wired into the server environment as `${user_config.<key>}`; `.codex-plugin/mcp.json` names the same four in `env_vars` so Codex forwards host values. A blank answer arrives empty and takes the schema default.
|
|
12
|
+
- **`bun run audit:fix`** (mcp-ts-core 0.13.0) — upgrades vulnerable packages to the lowest safe version within the existing ranges.
|
|
13
|
+
|
|
14
|
+
## Changed
|
|
15
|
+
|
|
16
|
+
- **The development skill tree moved from `skills/` to `framework-skills/`** (mcp-ts-core 0.13.0) — plugin hosts auto-load a root `skills/`, so installing this server's plugin no longer hands its 33 development skills to the agent.
|
|
17
|
+
- **Bun engines floor `>=1.3.0` → `>=1.4.0`** (mcp-ts-core 0.12.9), required by the framework's mirror driver fix. The README prerequisite follows.
|
|
18
|
+
- **A blank or whole-value `${…}` environment value reads as unset** (mcp-ts-core 0.13.0) — `WORKFLOWS_DIR`, `GLOBAL_INSTRUCTIONS_PATH`, and `WATCHER_DEBOUNCE_MS` take their schema defaults rather than the unsubstituted placeholder text an installer can leave behind.
|
|
19
|
+
- **Schema-invalid tool arguments return the structured `InvalidParams` (-32602) envelope** (mcp-ts-core 0.12.7), before the handler runs.
|
|
20
|
+
- **`lint:packaging` checks MCPB `user_config` wiring and plugin-manifest version parity** (mcp-ts-core 0.13.0, 0.12.7) — an undeclared `${user_config.<key>}` reference, a declared-but-unreferenced option, an optional string without `"default": ""`, or a plugin `version` out of step with `package.json` now fails `devcheck`.
|
|
21
|
+
- **`docs/design.md` reconciled with as-built** — `workflow_delete`, the `workflow_list` `query` filter, the current `WorkflowSchema`, and the per-tool error contracts replace the pre-build sketch.
|
|
22
|
+
- **Issue forms accept blank issues and link the private advisory form** (mcp-ts-core 0.13.0), and both plugin manifests carry full author metadata.
|
|
23
|
+
|
|
24
|
+
## Removed
|
|
25
|
+
|
|
26
|
+
- **Framework configuration no longer recognizes `MCP_RESPONSE_VERBOSITY` or the `OAUTH_PROXY_*` block** (mcp-ts-core 0.12.8). Neither appeared in this server's `.env.example`; drop them from any deployment that set them.
|
|
27
|
+
|
|
28
|
+
## Fixed
|
|
29
|
+
|
|
30
|
+
- **A trailing slash on `MCP_PUBLIC_URL` no longer doubles** in landing-page links or the `/.well-known/mcp.json` server card (mcp-ts-core 0.12.8).
|
|
31
|
+
- **A cancelled request's SSE stream closes immediately** instead of holding open to keep-alive or session expiry (mcp-ts-core 0.12.9).
|
|
32
|
+
- **The landing page's curl snippet sends a handshake `initialize` accepts** (mcp-ts-core 0.12.9).
|
|
33
|
+
|
|
34
|
+
## Dependencies
|
|
35
|
+
|
|
36
|
+
Runtime:
|
|
37
|
+
|
|
38
|
+
- `@cyanheads/mcp-ts-core` `^0.12.5` → `^0.13.0`
|
|
39
|
+
- `zod` `^4.5.4` → `^4.6.1`
|
|
40
|
+
|
|
41
|
+
Development:
|
|
42
|
+
|
|
43
|
+
- `@biomejs/biome` `2.5.11` → `2.5.13`
|
|
44
|
+
- `@types/node` `26.4.0` → `26.5.1`
|
|
45
|
+
- `ignore` `^7.0.8` → `^7.0.9`
|
|
46
|
+
- `tsc-alias` `^1.9.3` → `^1.9.4`
|
|
47
|
+
- `vitest` `^4.1.11` → `^5.0.0`, `@vitest/coverage-istanbul` `4.1.11` → `5.0.0`
|
|
48
|
+
|
|
49
|
+
`bun audit` reports no vulnerabilities across 295 packages.
|
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
|
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.2",
|
|
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",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"rebuild": "bun run scripts/clean.ts && bun run scripts/build.ts",
|
|
25
25
|
"clean": "bun run scripts/clean.ts",
|
|
26
26
|
"devcheck": "bun run scripts/devcheck.ts",
|
|
27
|
+
"audit:fix": "bun audit fix",
|
|
27
28
|
"audit:refresh": "rm -f bun.lock && bun install && bun audit",
|
|
28
29
|
"tree": "bun run scripts/tree.ts",
|
|
29
30
|
"list-skills": "bun run scripts/list-skills.ts",
|
|
@@ -78,30 +79,30 @@
|
|
|
78
79
|
"license": "Apache-2.0",
|
|
79
80
|
"packageManager": "bun@1.4.0",
|
|
80
81
|
"engines": {
|
|
81
|
-
"bun": ">=1.
|
|
82
|
+
"bun": ">=1.4.0",
|
|
82
83
|
"node": ">=24.0.0"
|
|
83
84
|
},
|
|
84
85
|
"publishConfig": {
|
|
85
86
|
"access": "public"
|
|
86
87
|
},
|
|
87
88
|
"dependencies": {
|
|
88
|
-
"@cyanheads/mcp-ts-core": "^0.
|
|
89
|
+
"@cyanheads/mcp-ts-core": "^0.13.0",
|
|
89
90
|
"pino-pretty": "^13.1.3",
|
|
90
91
|
"semver": "^7.8.5",
|
|
91
92
|
"yaml": "^2.9.0",
|
|
92
|
-
"zod": "^4.
|
|
93
|
+
"zod": "^4.6.1"
|
|
93
94
|
},
|
|
94
95
|
"devDependencies": {
|
|
95
|
-
"@biomejs/biome": "2.5.
|
|
96
|
+
"@biomejs/biome": "2.5.13",
|
|
96
97
|
"@socketsecurity/bun-security-scanner": "^1.1.2",
|
|
97
|
-
"@types/node": "26.
|
|
98
|
+
"@types/node": "26.5.1",
|
|
98
99
|
"@types/semver": "^7.8.0",
|
|
99
|
-
"@vitest/coverage-istanbul": "
|
|
100
|
+
"@vitest/coverage-istanbul": "5.0.0",
|
|
100
101
|
"depcheck": "^1.4.7",
|
|
101
102
|
"fast-check": "^4.9.0",
|
|
102
|
-
"ignore": "^7.0.
|
|
103
|
-
"tsc-alias": "^1.9.
|
|
103
|
+
"ignore": "^7.0.9",
|
|
104
|
+
"tsc-alias": "^1.9.4",
|
|
104
105
|
"typescript": "^7.0.2",
|
|
105
|
-
"vitest": "^
|
|
106
|
+
"vitest": "^5.0.0"
|
|
106
107
|
}
|
|
107
108
|
}
|
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.2",
|
|
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.2",
|
|
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.2",
|
|
61
61
|
"packageArguments": [
|
|
62
62
|
{ "type": "positional", "value": "run" },
|
|
63
63
|
{ "type": "positional", "value": "start:http" }
|