MCP server
@klera/mcp ships a stdio MCP server (klera-mcp) that exposes the engine
plus the bridge as tool calls. The server embeds its own BridgeServer,
the Expo app connects to that bridge just like it would for the CLI, and
your editor agent drives flows by calling tools — tap, type_text,
assert_visible, run_flow, plan_flow.
The MCP path is the right pick when your editor of choice already has LLM credits — Claude Desktop, Claude Code, Cursor. The server defers planning to whichever LLM the host already has wired up.
Tool surface
The server advertises ten tools:
| Tool | Purpose |
|---|---|
tap | Press an element matching a target spec |
type_text | Type a value into the matched input |
scroll | Scroll the first visible scrollable container |
assert_visible | { visible, elementId? } — does the matcher resolve the target? |
query_ui | Resolve a target and return its key attributes without acting on it |
snapshot | Return a small summary of the current element graph |
run_flow | Run a parsed Flow IR against the connected runtime |
plan_and_run_prose | Plan a .flow.md body against the live snapshot and execute |
plan_flow | Compile prose to a SemanticPlan without executing |
apply_plan_response | Validate a host-LLM JSON response and return the cached SemanticPlan |
Each takes a TargetSpec (an object with at least one of testID, text,
or accessibilityLabel) and returns plain JSON. The server resolves
targets through the engine’s matcher with self-healing on by default —
drift is tolerated and reported as drift: true on the response.
Configure your editor
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"klera": {
"command": "node",
"args": ["<REPO>/packages/mcp/dist/stdio.js"],
"cwd": "<REPO>",
"env": {
"KLERA_HOST": "127.0.0.1",
"KLERA_PORT": "7345"
}
}
}
}Restart Claude Desktop. The tools appear under a klera server in
the tool panel.
Environment
KLERA_HOST— bind address for the embedded bridge. Default127.0.0.1.KLERA_PORT— bridge port. Default7345. Must match the port the app dials (EXPO_PUBLIC_E2E_PORT).KLERA_WAIT_MS— how long each tool call waits for the runtime to attach before failing. Default30000.KLERA_SELF_HEALING— set to"0"or"false"to disable fuzzy matching; any drift becomes a hard failure.ANTHROPIC_API_KEY— optional. When set,plan_and_run_proseandplan_flowuse the Anthropic API directly. Without it,plan_flowreturns the prompt body for the host’s LLM to handle (see below).
Launch the app
The MCP setup reuses the standard app launch — only the bridge moves inside the MCP process instead of a CLI pane.
cd my-expo-app
pnpm ios # or pnpm androidThe first tool call from the agent triggers the bridge to wait for the
runtime handshake. Metro’s console prints
[e2e-runtime] bridge bound on ws://127.0.0.1:7345 once the app
connects.
Smoke test
Prompt your editor agent with:
Call the
snapshottool and tell me what elements are visible.
Expected: the agent invokes snapshot, the MCP process asks the bridge
for a live snapshot, the runtime responds with the current element graph,
and the agent summarises it.
If the call times out, the app is not connected — reopen the simulator or check the port settings.
Driving flows from prose
plan_and_run_prose is the one-shot tool: prose in, FlowResult out.
Use
plan_and_run_proseto: “Sign in with the seeded test user, dismiss the onboarding modal, and assert the welcome greeting is visible.”
The server parses the prose, takes a snapshot, calls the planner, and
runs the resulting SemanticPlan. The response carries the same
status / driftCount / per-step result shape as the CLI.
Plan without ANTHROPIC_API_KEY
plan_flow is the BYO-LLM split. When ANTHROPIC_API_KEY is set on the
server, plan_flow returns the compiled SemanticPlan directly. Without
it, the tool returns the prompt body — the same Markdown blob klera plan --manual writes to disk:
{
"kind": "prompt",
"prompt": "# klera planner — manual mode\n\n…",
"reason": "no_llm_configured",
"hint": "Route the prompt through the host's LLM, then call apply_plan_response with the JSON."
}The agent runs the prompt through its host LLM, then calls
apply_plan_response with the JSON response, the same prose, and the
same snapshot. The server validates against the SemanticPlan Zod schema
and returns the cached plan. ANTHROPIC_API_KEY is not required
for any of this.
This is why MCP is a good fit for editor agents that already have LLM credits: planning runs on whatever model your editor talks to. The klera server only validates and routes.
apply_plan_response strips a leading ```json fence automatically
if the host LLM includes one, so prompt drift between host LLMs is
forgiving. Pasted responses are validated against the same Zod schema
the API mode uses.
Troubleshooting
kleraserver missing from the tool panel. The stdio process crashed on startup. Check the editor’s MCP log (Claude Desktop:~/Library/Logs/Claude/mcp-server-klera.log). Common cause: thedist/stdio.jspath inargsdoes not exist — rebuild or fix the path.- Every tool call times out after 30s. The app is not connected to
the bridge. Verify Metro printed
bridge bound …and thatEXPO_PUBLIC_E2E_PORTmatchesKLERA_PORT. - “target not matched” from
tap/type_text. Ask the agent to callsnapshotfirst and check the element graph for the right testID / text. The matcher’striedStrategieslist comes back in the error to narrow the diagnosis. - Stale tool surface after rebuilding the MCP package. The editor caches tool descriptions. Restart the editor after a fresh build.
See also
- Planner transports — the four ways prose becomes IR.
- Editor support — JSON Schema autocomplete for hand-authored YAML flows in the same editor.