AI Agents (MCP)
Turn the workflows you built into tools an AI assistant can call. Each flow you expose becomes its own named tool — the assistant runs it and reads the result.
The flow you already built
One tool per exposed flow
Calls it, reads the result
What It Is
An AI assistant decides what to do; Watchflows decides what it's allowed to do. It can see, run, and read results from the flows you share, and nothing else. This works over the Model Context Protocol (MCP) — a small watchflows-mcp helper in the app bundle talks to a local-only server inside the app (it listens on 127.0.0.1 / loopback, so nothing off this Mac can reach it).
It is off by default: no server listens and no live token is in effect until you flip the master toggle. When on, it stays local to this Mac:
| Property | Detail |
|---|---|
| Binds | 127.0.0.1 only — no network exposure |
| Auth | Private bearer token required on every call |
| Token | 32 random bytes (43 base64url chars), regenerated every time the server starts |
| Stored in | A 0600 file only your account can read |
| Never in | The Keychain or your shell history |
Setup at a Glance
Four steps, once. The next three sections walk through 1–3.
AI Agents master toggle
Wire in your assistant
Per-flow toggle
Assistant runs the tool
1. Turn It On
Open Settings → AI Agents and flip Enable AI Agents. That starts the loopback server, generates a fresh access token, and writes the port to disk so your assistant's helper can find it. The token is regenerated every time the server starts — a restart or off/on cycle invalidates the old one. Turning the toggle off stops the server.
2. Connect Your Assistant
The AI Agents pane has a connector for each major client. You never paste the token — every snippet carries only the path to the bundled helper, and the helper reads the token off its 0600 file itself.
| Client | How to connect |
|---|---|
| Claude Desktop | One-click Connect: merges a watchflows entry into your config, leaves other servers untouched. You restart Claude. (If it isn't installed, you get a copyable config instead.) |
| Claude Code | Copy one command, paste into a terminal (below) |
| Cursor / Windsurf / VS Code | Copy the same mcpServers JSON; only the target file differs (~/.cursor/mcp.json, ~/.codeium/windsurf/mcp_config.json, etc.) |
| Codex | Copy a TOML table into ~/.codex/config.toml |
The Claude Code command looks like this (the helper path comes from wherever the app is actually installed):
claude mcp add --transport stdio watchflows -- /Applications/Watchflows.app/Contents/MacOS/watchflows-mcp
An Advanced section shows the active port and a masked copy of the token, with Copy and Rotate. Rotating replaces the token file and restarts the server so the new token takes effect immediately — any client still using the old token will need to reconnect.
3. Expose a Flow
Enabling AI Agents does not hand over all your flows. Each workflow has its own Expose to AI agents toggle, off by default, in the “Exposed workflows” list or the flow's inspector. A flow is agent-runnable only when both are true:
Enabled
The flow is on
Exposed
Toggle flipped on
Agent-runnable
Becomes a named tool
-
Everything else stays visible, just not runnable.
list_workflowsreports all flows with anagentRunnableflag, so the assistant sees what exists — but a non-exposed flow has no run tool and the run gate refuses it. - Destructive flows use the same toggle. A flow that can delete files or run scripts is gated by the very same Expose to AI agents switch — there's no separate one. The only difference: the refusal message adds a heads-up that the flow contains such a node, so you opt in with eyes open.
How Input Works
Exposing a flow turns it into a tool the assistant can call — and the first node of your flow is the slot where its input goes in. Whatever the assistant passes replaces the placeholder you saved while building (it replaces, never blends). Your saved value is shown to the assistant only as an example of what belongs there — not a fallback it quietly runs.
You get a clean, labeled slot when your flow starts with an Input node: it shows the assistant exactly one box, named after the Input's type.
| Entry trigger | Argument key | Type |
|---|---|---|
| Input · Text (default) | text | string |
| Input · JSON | data | object |
| Input · File | filePath | string |
| Any other trigger | payload | object (free-form) |
An Input node isn't strictly required — the assistant's data is fed into whichever trigger your flow starts with. But only Input flows get that tidy labeled box. Every other trigger — File Changed, Schedule, Device Event, and so on — hands the assistant a single blank payload box to fill in, with no labels to guide it.
podcast_info_fetcher(text: "Acquired")
Fills contentType + data for you
Curated output + output_source inline
The two run entry points are not interchangeable. Only the flow's own named tool does the typed key→payload expansion shown above (your text argument, then contentType and data filled in). run_workflow passes its payload object straight through with no remapping, so there you supply the raw trigger keys yourself.
To see a flow's exact input contract, call describe_workflow — it returns a runPayload block: a note plus a keys list giving each key's type, description, and example.
What You Get Back
A completed run returns the answer inline — a top-level output plus an output_source telling you where it came from. The rest of the body is lean: per-node status, timings, and errors, but not full payloads. For every node's full payload, the assistant calls get_run.
Watchflows picks the output automatically, and output_source self-advertises the pick:
output_source |
Meaning |
|---|---|
explicit:<node> | You set “Return this to the agent” on that node, and it ran |
auto:<node> | Single end node — its result was returned |
none:side-effect | The leaf ran but produced no result |
none:dead-end | The run ended without reaching an end node |
ambiguous:multi-leaf:<names> | Two or more branches ended at once — pick unclear |
-
“Return this to the agent” override. When the auto-pick is wrong, turn this on in any node's inspector — its output becomes the answer (
explicit:<node>), as long as it ran and produced a value. If it was skipped or empty, projection quietly falls back to the auto-pick. Never a hard error. - Verbose agent-trace fields are stripped from the output.
- The inline value is capped at ~16 KB (16,384 bytes); anything larger is truncated inline to a marker.
-
A value already spilled to disk surfaces as a
…[spilled blob — use get_run]marker, never the full bytes.
Long Runs & Retries
A run that hasn't finished after about a minute doesn't fail — the flow keeps executing in the background and is saved under a run_id from the start. Two non-error statuses tell the assistant to wait, not re-call:
| Status | Means | Do |
|---|---|---|
running | Still executing past ~60s | Poll get_run with the run_id |
already_running | You tried to start it while in flight | Poll the in-flight run_id — don't start a duplicate |
The rule: on running or already_running, poll the run_id — don't re-call.
The Tools
Alongside each exposed flow's own named tool, the assistant always has these five core tools. They are read/run only — no creating, editing, or enabling/disabling flows in this version (planned for a later phase).
| Tool | What it does |
|---|---|
list_workflows | Lists every workflow, each with an agentRunnable flag |
describe_workflow | A flow's node-by-node shape plus its runPayload input contract |
run_workflow | Runs a flow by id with an optional payload, returning curated output inline (a flow's named tool is the typed shortcut) |
get_run | The full, verbose record of one run, including every node's payload |
list_runs | Pages through a flow's run history |
Read tools see every flow; only exposed-and-enabled flows can be run.