fancy-flow-mcp-js
An MCP server for authoring, validating, and smoke-testing fancy-flow workflows headlessly on a TypeScript host, with the same 15 tools as the PHP twin.
Manage workflow drafts: create (with optional id), list, get full graph, delete.
Build graphs: add/remove nodes, configure nodes (accepts partially-valid configs with warnings), connect nodes with explicit or default ports, remove edges.
Discover vocabulary: list node kinds from the live registry (filterable by category), describe a kind's config schema/defaults/ports.
Validate and port: validate_workflow tags issues as schema vs host; export/import portable WorkflowSchema documents (lenient import).
Run smoke tests: run_workflow executes graphs with seeded initial inputs, refuses invalid graphs, and returns per-node output; it is not a production executor and lacks executors for many kinds.
Persistence is pluggable: use MemoryDraftStore or implement DraftStore; the server itself never persists.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@fancy-flow-mcp-jsCreate a workflow with a trigger and a log node, then connect them."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
@particle-academy/fancy-flow-mcp-js
MCP server that lets an agent author fancy-flow workflows headlessly on a TypeScript host — the Node twin of fancy-flow-mcp (Laravel).
Same 15 tools, same names, same arguments. An agent that learned to build graphs against the PHP server drives this one without relearning anything — and that claim is asserted by a test that reads the PHP source, not maintained by hand.
npx @particle-academy/fancy-flow-mcp-jsThe split it is built around
We own whether a graph is well-formed; the host owns whether it is allowed to run.
Every issue crosses the wire tagged with which of those refused it:
{ "source": "schema", "level": "error", "message": "message: Message is required", "nodeId": "log-1" }
{ "source": "host", "level": "error", "message": "This host cannot resume a paused run.", "nodeId": "ask-1" }Those have different remedies — one is fixed by editing the graph, the other by granting a capability or picking another kind. Collapsing them costs an author real time chasing the wrong fix, so they are never merged.
Related MCP server: Orchestration MCP
Two entry points
Import | What it costs | Use it when |
| nothing beyond | You want graph authoring and validation, no server |
| + | You want an MCP server |
The core was written first and deliberately depends on nothing, so a host that only wants to build and validate graphs never pays for a server it will not use.
No third-party runtime code, and no React. The server is agent-integrations'
first-party MicroMcpServer, imported from its headless /mcp subpath, which
imports no package at all. (Until 0.2.0 it was @modelcontextprotocol/sdk —
see the changelog for what changed and what did not.)
Use it from a host
import { createFlowServer, MemoryDraftStore } from "@particle-academy/fancy-flow-mcp-js";
import { attachStdio } from "@particle-academy/agent-integrations/mcp/stdio";
const server = createFlowServer({
store: new MemoryDraftStore(),
// Your answer to "may I run this kind here?". Return a string to REFUSE —
// and name the missing capability, because a refusal an author can act on is
// worth more than a correct one they cannot.
admits: (kind) =>
kind.name.includes("terminal") ? "This host has no terminal sessions." : null,
});
attachStdio(server); // stdin/stdout; log to stderr, never stdoutImporting @particle-academy/agent-integrations yourself? Declare it in your own
dependencies (>=0.45 <2.0) rather than relying on this package to bring it.
There is no need to call registerBuiltinKinds(): the kind registry fills
itself on first read. It is also the wrong import for a headless host —
@particle-academy/fancy-flow/registry pulls in React.
createFlowServer returns a transport-agnostic server. For anything other than
stdio, attach any agent-integrations transport, or implement its two-member
Transport (send, optional close) and hand each incoming frame to
server.receive(transport, frame).
MemoryDraftStore is a convenience. Implement DraftStore (list / get /
save / remove, sync or async) against your own storage and drafts survive a
restart — this package never persists anything itself.
The tools
| Drafts |
| Nodes |
| Edges |
| The vocabulary |
| Checking and portability |
| A smoke test of wiring — see below |
list_node_kinds reads the live registry, so kinds a host registered itself
and kinds vendored from the marketplace are included. It is not a fixed list.
configure_node writes a config that fails validation and reports warnings
rather than refusing. A half-configured node is a normal intermediate state when
an agent builds a graph one step at a time; validate_workflow is the place
that says "not finished".
run_workflow — read this before relying on it
It is a smoke test of wiring and routing, not a production run.
It takes no executor argument, and the executor registry is bound as a literal at the call site — the same shape the PHP twin uses. An agent-reachable run is structurally incapable of being pointed at your real infrastructure. Unrepresentable beats forbidden: a policy can be relaxed by a later edit; an absent parameter cannot be passed.
Two honest limits:
9 of 31 builtin kinds have a TypeScript executor. The PHP twin's
Builtin::executors()covers every kind; the TS runtime has no equivalent yet. The other 22 returnok: falsewith "No executor registered" — a true answer about this runtime, delivered as a result rather than an error. A test pins the count so the day it improves, this paragraph is forced to change.Kind-level executors still apply. A host process that has called
registerTerminalHostorregisterLlmClientmakes those reachable from here. That is your doing, not this package's, and it is said plainly rather than papered over.
Protocol revision
Speaks 2025-11-25, negotiating back to 2024-11-05 — the same family
laravel/mcp speaks, which is what the PHP twin is built on, and what Claude
Code and Codex speak. initialize echoes any of those a client asks for and
answers 2025-11-25 otherwise.
It does not speak 2026-07-28, the revision that removed initialize and
made the protocol stateless. Neither does the PHP twin. If you need to be
reached by a 2026-07-28-only client, that gap is open and belongs to both
twins together — a Node server that jumped ahead alone would stop being a twin.
Licence
MIT
Available Tools
15 toolsadd_nodeAdd NodeA
Add a node of a given kind to a workflow. The kind is checked against the live registry (call list_node_kinds first — a host may have registered its own). Omitting config applies the kind's schema defaults. Pass parent_id to place the node inside a lane. Returns the created node.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | Yes | Node kind, e.g. "manual_trigger", "llm_call", "branch". | |
| config | No | Optional config. See describe_node_kind for the fields. | |
| node_id | No | Optional explicit node id. Omit to auto-generate. | |
| parent_id | No | Optional lane node id — puts this node inside that lane. | |
| workflow_id | Yes | The workflow id from create_workflow. |
configure_nodeConfigure NodeA
Merge config into a node, keeping the kind's other defaults. A config that does not satisfy the kind's schema is WRITTEN ANYWAY and reported as warnings — a half-configured node is a normal intermediate state when building a graph one step at a time, and refusing the write makes it impossible to get there. validate_workflow is where 'not finished' is said.
| Name | Required | Description | Default |
|---|---|---|---|
| config | Yes | Fields to merge into the node's config. | |
| node_id | Yes | The node to configure. | |
| workflow_id | Yes | The workflow id. |
connect_nodesConnect NodesA
Draw an edge from one node's output port to another's input port. Omit the port names to use each kind's defaults. Both nodes must already exist — connecting to a node that is not there is an error rather than a promise to create it.
| Name | Required | Description | Default |
|---|---|---|---|
| source | Yes | Source node id. | |
| target | Yes | Target node id. | |
| source_port | No | Optional output port on the source, e.g. "true" on a branch. | |
| target_port | No | Optional input port on the target. | |
| workflow_id | Yes | The workflow id. |
create_workflowCreate WorkflowA
Start a new, empty workflow draft and return its workflow_id. Every other tool takes that id. Nothing is persisted beyond the host's store, and creating a workflow never runs anything.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Human-readable name for the workflow, e.g. "Nightly digest". | |
| workflow_id | No | Optional explicit id. Omit to have one minted. |
delete_workflowDelete WorkflowA
Remove a workflow draft from this host's store. Irreversible on a store that does not version, and it deletes only the draft — nothing that was already exported or run.
| Name | Required | Description | Default |
|---|---|---|---|
| workflow_id | Yes | The workflow id to delete. |
describe_node_kindDescribe Node KindA
One kind in full — its config schema, its default config, and its input and output ports. Call this before configure_node rather than guessing field names; the fields are deliberately not on list_node_kinds, which would turn a vocabulary query into a payload nobody reads.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | Yes | The kind name, e.g. "llm_call" or the fully-qualified form. |
export_workflowExport WorkflowA
Emit the portable WorkflowSchema document — the same JSON the TypeScript, PHP and Python runtimes all read. This is what you hand to another runtime, commit to a repo, or pass to import_workflow.
| Name | Required | Description | Default |
|---|---|---|---|
| workflow_id | Yes | The workflow id. |
get_workflowGet WorkflowA
Return one workflow's full graph — every node with its kind, config and parentId, and every edge. This is the authoring view; use export_workflow for the portable WorkflowSchema document.
| Name | Required | Description | Default |
|---|---|---|---|
| workflow_id | Yes | The workflow id from create_workflow. |
import_workflowImport WorkflowA
Read a WorkflowSchema document into a NEW draft and return its workflow_id, plus any issues found. Import is lenient on purpose: a document referencing a kind this host has not registered still imports, with the problem reported — refusing would lose the other forty nodes to fix one.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Optional name for the imported draft. | |
| workflow | Yes | A WorkflowSchema document, as export_workflow emits. | |
| workflow_id | No | Optional explicit id for the new draft. |
list_node_kindsList Node KindsA
Every node kind this host has registered, read from the LIVE registry rather than a fixed list — so kinds the host added itself, and kinds vendored in from the marketplace, are included. Each says whether this host ADMITS it, and a refusal names the missing capability.
| Name | Required | Description | Default |
|---|---|---|---|
| category | No | Optional filter, e.g. "logic", "ai", "io", "trigger". |
list_workflowsList WorkflowsA
List every workflow draft this host is holding, with node and edge counts. Use it to recover a workflow_id you did not keep, before creating a duplicate.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
remove_edgeRemove EdgeA
Remove one edge, named either by its edge id or by the source/target pair it connects. Removing an edge never removes the nodes it joined.
| Name | Required | Description | Default |
|---|---|---|---|
| source | No | Source node id, if naming the edge by its endpoints. | |
| target | No | Target node id, if naming the edge by its endpoints. | |
| edge_id | No | The edge id, if you have it. | |
| workflow_id | Yes | The workflow id. |
remove_nodeRemove NodeA
Remove a node and every edge touching it. The edges go WITH the node deliberately — leaving them would produce edges pointing at nothing, reported as a second error the author did not cause.
| Name | Required | Description | Default |
|---|---|---|---|
| node_id | Yes | The node to remove. | |
| workflow_id | Yes | The workflow id. |
run_workflowRun WorkflowA
Execute the graph and return per-node outputs plus ok/error. This is a SMOKE TEST of wiring and routing, not a production run: it takes no executors, so nodes fall back to their kind's own behaviour and anything needing a host capability (a terminal session, an LLM client) refuses rather than acting. Refuses outright to run a graph with validation errors, because a malformed graph produces failures that look like engine bugs and are not. Seed entry nodes with initial_inputs, keyed by node id then port.
| Name | Required | Description | Default |
|---|---|---|---|
| workflow_id | Yes | The workflow id. | |
| initial_inputs | No | Inputs seeded to entry nodes: { "<node_id>": { "<port>": <value> } }. |
validate_workflowValidate WorkflowA
Check a workflow and return every issue TAGGED by who refused it: source "schema" means the graph is malformed and editing it is the fix; source "host" means this host will not run that kind and the fix is a capability or a different kind. Collapsing the two costs you the wrong fix.
| Name | Required | Description | Default |
|---|---|---|---|
| workflow_id | Yes | The workflow id. |
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
15 tool updates
v0.1.0- First observed
add_node - First observed
configure_node - First observed
connect_nodes - First observed
create_workflow - First observed
delete_workflow - First observed
describe_node_kind - First observed
export_workflow - First observed
get_workflow - First observed
import_workflow - First observed
list_node_kinds - First observed
list_workflows - First observed
remove_edge - First observed
remove_node - First observed
run_workflow - First observed
validate_workflow
TDQS
Scored across 15 tools
Every tool maps to one distinct operation: workflow-level CRUD, node editing, edge editing, node-kind introspection, validation, import/export, and smoke-test running. The close pairs like get_workflow/export_workflow and list_node_kinds/describe_node_kind are explicitly differentiated. No two tools appear to do the same job.
All tools follow a consistent snake_case verb_noun pattern: list_, create_, get_, delete_, add_, remove_, configure_, connect_, describe_, validate_, export_, import_, and run_. The object nouns are predictable and consistent. There is no mixed casing or vague verb usage.
Fifteen tools sits at the upper boundary of ideal, but each tool earns its place in the workflow-authoring lifecycle. There are no duplicate or filler tools, and the count matches the apparent domain richness. It feels intentionally scoped rather than bloated.
The server covers workflow CRUD, node editing, edge editing, node-kind introspection, validation, portable import/export, and a run smoke test. Every editing operation has a corresponding read or delete path, and get_workflow/export_workflow provide both authoring and portable views. The declared out-of-scope production execution is a deliberate boundary, not a gap.
Maintenance
Related MCP Connectors
Open-source Zapier/n8n alternative as an MCP server: agents build, run and debug your workflows.
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
Hosted runtime for persistent agent teams, durable workflows, memory, schedules, and goals.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA TypeScript implementation of a Model Context Protocol (MCP) server that exposes Dify workflows as tools for AI systems to interact with.16ISC
- FlicenseAqualityDmaintenanceA TypeScript MCP server for launching, tracking, and managing external coding-agent runs across local and remote backends like Codex and Claude Code. It allows top-level agents to orchestrate subagents through tools for spawning tasks, polling events, and handling interactive sessions.72-
- AlicenseDqualityDmaintenanceA TypeScript MCP server demo supporting local Stdio and remote Streamable HTTP, demonstrating tool invocation for AI agents.21MIT
- AlicenseAqualityDmaintenanceA TypeScript MCP server that installs, starts, stops, and manages n8n workflows and executions via the n8n REST API.115 npmMIT