{
  "markdown": "<h1 align=\"center\">workflow-mcp</h1>\n\n<p align=\"center\">\n  Run Claude Code dynamic-workflow files through any agent provider — as a durable, resumable Model Context Protocol server.\n</p>\n\n<p align=\"center\">\n  <a href=\"https://github.com/Juliusolsson05/workflow-mcp/stargazers\"><img src=\"https://img.shields.io/github/stars/Juliusolsson05/workflow-mcp?style=flat\" alt=\"Stars\"></a>\n  <a href=\"https://github.com/Juliusolsson05/workflow-mcp/network/members\"><img src=\"https://img.shields.io/github/forks/Juliusolsson05/workflow-mcp?style=flat\" alt=\"Forks\"></a>\n  <a href=\"https://github.com/Juliusolsson05/workflow-mcp/issues\"><img src=\"https://img.shields.io/github/issues/Juliusolsson05/workflow-mcp?style=flat\" alt=\"Issues\"></a>\n  <a href=\"LICENSE\"><img src=\"https://img.shields.io/github/license/Juliusolsson05/workflow-mcp?style=flat\" alt=\"License\"></a>\n  <a href=\"https://github.com/Juliusolsson05/workflow-mcp/commits/main\"><img src=\"https://img.shields.io/github/last-commit/Juliusolsson05/workflow-mcp?style=flat\" alt=\"Last commit\"></a>\n</p>\n\n---\n\n`workflow-mcp` is a standalone runtime and MCP server that executes Claude Code\n**dynamic workflow files** — the JavaScript orchestration programs that fan out\ndozens or hundreds of agent calls and return a single result — without requiring\nClaude to run them.\n\nThe whole project hangs on one promise:\n\n> A workflow authored for `workflow-mcp` can be copied into\n> `.claude/workflows/<name>.js` and run by a compatible Claude Code release\n> without changing the file — and a real Claude workflow file runs through this\n> runtime without any importer or translation step.\n\nEverything that _isn't_ portable — which provider executes the agents, its\ncredentials, the durable run cache, MCP run IDs, and UI state — lives in the\nruntime, never inside the `.js` file.\n\n## Why it exists\n\nClaude Code workflows are a strong primitive: the JavaScript owns the loops,\nbranches, fan-out, and aggregation, while each `agent()` call owns the model\nreasoning and side effects — keeping hundreds of intermediate results out of the\nparent conversation. But out of the box they come with three constraints:\n\n- **Claude executes every agent node.** There is no seam for another provider.\n- **A run belongs to a Claude session.** Its state lives inside that session\n  directory, and it does not survive as an independently controllable object.\n- **There is no server surface.** Other tools cannot discover, launch, follow,\n  or resume a run.\n\n`workflow-mcp` keeps the exact same workflow file and lifts those constraints:\nthe same `.js` runs through a provider-neutral engine (Codex today), every run\nis a durable object that survives restarts, and any MCP-capable host can drive\nit over a stable set of tools.\n\n## How it works\n\nTwo kinds of portability are kept deliberately separate — MCP solves the first,\nthe provider interface solves the second:\n\n```text\nMCP-capable host\n      │  workflow_list · workflow_run · workflow_run_events · …\n      ▼\nWorkflowService  +  durable event store   ← the long-lived owner of runs\n      ▼\nClaude-compatible JavaScript runtime       ← agent/parallel/pipeline/phase/…\n      ▼\nprovider-neutral AgentProvider\n      ├─ Codex SDK  (@openai/codex-sdk)\n      ├─ fake       (deterministic tests)\n      └─ future providers\n```\n\n- **Claude-compatible runtime.** A restricted, killable Node/V8 context that\n  exposes exactly the globals Claude injects (`agent`, `parallel`, `pipeline`,\n  `phase`, `log`, `workflow`, `args`, `budget`, top-level `await`/`return`) with\n  the same discovery rules, metadata grammar, and cache identity. The behaviour\n  is pinned to an observed Claude Code profile so future Claude releases add a\n  _new_ profile instead of silently breaking old runs.\n- **Durable service.** The `WorkflowService` — not any single MCP connection —\n  owns runs. Every event is appended and fsynced **before** any subscriber sees\n  it, so a run can be reconstructed after a renderer reload, a provider\n  reconnect, or a process restart by replaying a strict event cursor.\n- **Provider-neutral execution.** The engine only knows an `AgentProvider`\n  interface. The first real adapter drives the official Codex SDK; a\n  deterministic fake provider runs the conformance suite. Model aliases\n  (`haiku`/`sonnet`/`opus`) are host policy, never guessed.\n- **Reliability.** One work-conserving scheduler across all runs, supervised\n  per-agent retries, one process-owned Codex host per attempt, a shared provider\n  circuit breaker, single-writer fencing, and interrupted-run recovery that\n  sparsely reuses already-completed siblings.\n\n## What you get\n\n- **Portable workflow files** — the same `.js` runs here and in Claude Code.\n- **A durable MCP server** — fourteen stable tools (`workflow_list`,\n  `workflow_author_guide`, `workflow_describe`, `workflow_validate`, `workflow_run`,\n  `workflow_run_status`, `workflow_run_events`, `workflow_result_read`, `workflow_run_cancel`,\n  `workflow_resume`, `workflow_agent_list`, `workflow_agent_result_read`,\n  `workflow_agent_results_read`, `workflow_agent_transcript_read`) over stdio or an authenticated\n  loopback HTTP transport.\n- **Per-agent inspection** — a finished run is not just its final value. List its logical agents\n  with attempt history, then read any single agent's complete untruncated output, or sweep them\n  all in one paginated walk.\n- **Immediate run handles** — `workflow_run` returns a run ID at once; clients\n  follow progress by polling a durable cursor, not a transport-specific push.\n- **Unattended best-effort completion** — retryable read-only work restarts in a\n  fresh provider thread. An exhausted or unsafe logical assignment becomes a\n  versioned `__workflowAgentFailure` coverage gap, while independent siblings\n  and final synthesis continue. Such runs finish as `completed_with_errors`;\n  only persistence or supervisor faults fail the complete run.\n- **Resume** — continue a managed run, or import-and-resume a real Claude run\n  after verifying its source and journal byte-identity. Exact source/arguments\n  reuse completed calls sparsely; automatic crash recovery also preserves\n  terminal coverage gaps, while an explicit manual resume retries those gaps.\n  Edited source retains the longest unchanged prefix. MCP callers may pass a\n  managed `run_*` ID or Claude's native `wf_*` ID; Claude's own files are never\n  rewritten. For exact-source Claude imports, bounded hashes of the original\n  subagent prompts preserve completed dynamic-pipeline siblings even when cached\n  parents settle in a different order; raw prompt text is not copied into the\n  workflow-mcp sidecar.\n- **An embeddable service** — the same `WorkflowService` and tool registrar that\n  the CLI uses can be mounted inside another host (this is how\n  [Agent Code](https://github.com/Juliusolsson05/agent-code) renders each run as\n  a live feed card) instead of starting a second server.\n- **A browser-safe state entry** — `workflow-mcp/state` exposes the event union\n  and pure reducer with no filesystem, MCP, or Codex code, so a renderer can\n  project run state without pulling server code into its bundle.\n\n## Getting started\n\nThe Docker-first standalone product needs no host Node or Codex installation. It ships one\nproject-scoped daemon, a Codex MCP proxy, terminal UI, optional local web UI, isolated credentials,\nand durable named-volume state. Start with the verified release and operator guide in\n[`standalone/README.md`](standalone/README.md); the full decisions and support boundaries are in\n[`standalone/docs/adr`](standalone/docs/adr/README.md).\n\nFor core-library development, Node ≥ 20.19 is required. A source checkout can build and test both\nthe provider-neutral runtime and isolated standalone package:\n\n```bash\nnpm install --include=dev\nnpm run build\nnpm run check\n```\n\nThen drive a workflow from the CLI:\n\n```bash\n# Validate one workflow file (direct paths do not need a .js extension).\nnode dist/cli.js validate ./path/to/workflow.js\n\n# List personal and project workflows visible from a directory.\nnode dist/cli.js list ./path/to/project\n\n# Run through the Codex SDK. Events are JSONL on stderr; the final result is\n# JSON on stdout. The optional second argument is one JSON value exposed as `args`.\nnode dist/cli.js run ./path/to/workflow.js '{\"files\":[\"src/index.ts\"]}'\n\n# Resume a persisted Claude run (imported runs are read-only).\nnode dist/cli.js resume /path/to/claude/session/workflows/wf_id.json\n\n# Serve over stdio, scoped to one project.\nnode dist/cli.js serve --stdio /path/to/project\n\n# Serve over an authenticated loopback Streamable HTTP endpoint (URL + bearer\n# token are printed once to stderr).\nnode dist/cli.js serve --http /path/to/project 0\n```\n\nOnce served, both `workflow_resume({ runId: \"wf_...\" })` and\n`workflow_run({ resumeFromRunId: \"wf_...\" })` discover that Claude run inside\nthe scoped project's Claude state. Use `claudeRunPath` only when duplicate\nhistorical metadata requires explicit selection.\n\n### Reading a complete result\n\nEvery newly completed service run stores one immutable UTF-8 result artifact. The compact\n`workflow_run_status.run.result` reference and the `run.completed` event both include its\n`artifactId`, media type, total UTF-8 byte count, line count, and SHA-256 checksum. When\n`truncated` is true, inline `content` is only a display prefix; it is not the complete result.\n\n`workflow_result_read` accepts only a scoped `runId` plus that opaque `artifactId`—never a\nfilesystem path. Pages default to 16 KiB and may request 4 through 65,536 bytes. Page ends are\nmoved backward when necessary so concatenating `content` never splits a UTF-8 code point:\n\n```ts\nimport { createHash } from 'node:crypto'\n\nconst statusCall = await client.callTool({\n  name: 'workflow_run_status',\n  arguments: { runId },\n})\nconst status = statusCall.structuredContent as {\n  run: {\n    result: {\n      artifactId: string\n      checksum: { algorithm: 'sha256'; value: string }\n    }\n  }\n}\n\nconst parts: string[] = []\nlet cursor: string | undefined\nfor (;;) {\n  const call = await client.callTool({\n    name: 'workflow_result_read',\n    arguments: {\n      runId,\n      artifactId: status.run.result.artifactId,\n      ...(cursor === undefined ? {} : { cursor }),\n      maxBytes: 16_384,\n    },\n  })\n  const { page } = call.structuredContent as {\n    page: { content: string; hasMore: boolean; nextCursor?: string }\n  }\n  parts.push(page.content)\n  if (!page.hasMore) break\n  if (page.nextCursor === undefined) throw new Error('missing continuation cursor')\n  cursor = page.nextCursor\n}\n\nconst completeResult = parts.join('')\nconst digest = createHash('sha256').update(completeResult, 'utf8').digest('hex')\nif (digest !== status.run.result.checksum.value) throw new Error('result integrity mismatch')\n```\n\nString results are raw `text/plain`; objects, arrays, numbers, booleans, and `null` are pretty\nprinted `application/json`; JavaScript `undefined` is the `text/plain` bytes `undefined`. An empty\nstring has zero bytes and zero lines. A top-level string containing a lone UTF-16 surrogate fails\nbefore completion because it has no lossless UTF-8 representation. Non-terminal runs return\n`result-not-ready`; failed, cancelled, or interrupted runs return `result-unavailable`; a\ncompleted legacy run without an artifact also returns `result-unavailable`; and missing retained\nbytes return `result-expired`.\nMalformed, stale, or non-UTF-8-boundary cursors return `invalid-cursor`.\n\n`FileWorkflowStore` retains result bytes with the run directory and defaults to a 64 MiB result\nceiling. Configure `maxResultBytes` when constructing the store if the host needs a different\nbounded policy (up to the hard 512 MiB safety ceiling). A result over that ceiling fails the run\nbefore `run.completed` rather than publishing another irreversible prefix. The direct\n`workflow-mcp run` CLI still writes its full result to stdout; the paginated contract applies to\ndurable service/MCP runs.\n\n## Embedding\n\nThe public API uses plain names and hands the host full control of the MCP\nserver, transport, and authentication lifecycle:\n\n```ts\nimport {\n  CodexAgentProvider,\n  FileWorkflowStore,\n  WorkflowService,\n  registerWorkflowMcpTools,\n} from 'workflow-mcp'\n\nconst service = new WorkflowService({\n  store: new FileWorkflowStore('/private/application/state/workflows'),\n  provider: () => new CodexAgentProvider({\n    codexPathOverride: '/approved/codex',\n    // Required before a host may attest that normal user/project MCP servers cannot leak into\n    // an automatically replayed read-only workflow attempt.\n    configurationIsolation: {\n      codexHome: '/private/application/state/workflow-codex',\n      authenticationFile: '/home/user/.codex/auth.json',\n      // This must come from inspection of the exact executable plus user/project/system/managed\n      // configuration layers. Omit it and use \"unknown\" below when the host cannot prove that.\n      effectiveConfigurationFingerprint: verifiedCodexPolicyDigest,\n    },\n    capabilities: { inheritedMcpServers: 'disabled' },\n  }),\n  sandbox: { mode: 'read-only', approvalPolicy: 'never', network: false },\n})\nawait service.initialize()\n\n// The host still owns McpServer and its transport/authentication.\nregisterWorkflowMcpTools(mcpServer, service, { cwd: projectDirectory, clientId: sessionId })\n```\n\n## Documentation\n\n- **[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)** — the full technical\n  reference: the pinned Claude-workflow compatibility profile, the exact runtime\n  realm, discovery/precedence, cache and resume mechanics, the MCP architecture,\n  the Codex SDK findings, and the conformance matrix.\n- **[docs/EXECUTION_PLAN.md](docs/EXECUTION_PLAN.md)** — the phased build decisions.\n- **[docs/RELIABILITY_IMPLEMENTATION_PLAN.md](docs/RELIABILITY_IMPLEMENTATION_PLAN.md)** —\n  the unattended-execution and failure-domain plan.\n\n## Agent Code and standalone use\n\n`workflow-mcp` began as a feature of\n[**Agent Code**](https://github.com/Juliusolsson05/agent-code) — an open-source\nElectron IDE for driving the real Claude Code and Codex CLIs across a\nmulti-agent workspace. Agent Code embeds this runtime through its existing MCP\nhost and renders each run as a live, first-class feed card: phases and agents as\nvertical lists, with prompt, activity, and outcome expandable inline.\n\nThat embedded path remains supported, but it is no longer required. The Docker-first product above\nadds the supervised owner, project-scoped Codex MCP proxy, terminal and optional browser clients,\ncredential and authoring controls, offline maintenance, and verified release machinery needed to\nrun without the desktop application. Generic OCI/MCP-registry mode is intentionally session-bound;\nuse the checksummed Compose bundle when runs must outlive an MCP client connection.\n\n## Status\n\nThe loader, execution runtime, durable service, MCP facade, Agent Code embedding, and Docker-first\nstandalone implementation are in place. The first stable container release still requires the\nexternal platform qualification, protected release controls, and registry publication documented\nin the standalone implementation ledger. Compatibility is pinned to an **observed** Claude Code\nprofile — a snapshot of a fast-moving upstream, not a promise about future versions.\n\n## License\n\n[MIT](LICENSE)\n",
  "bytes": 15289,
  "sha": "c6d781deea47b066104ee6f28b5fedf1270764c7ed790a4af1b6c157e2386cd4",
  "repo_slug": "juliusolsson05/workflow-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_juliusolsson05_workflow_mcp_d42dc788/readme"
}