Agents and MCP
The run loop
An agent drives a Workbench run the same way over mcp.workbenches.dev, over local wb mcp, and with the CLI: start it, wait for it to need you, reply, and wait again.
run -> wait -> send or answer -> wait -> ... -> completed, failed, or cancelled1. Find and inspect#
{ "tool": "search", "arguments": { "query": "code review" } }
{ "tool": "view", "arguments": { "workbench": "acme/code-review" } }Check required_env in the view result: a cloud run needs an organization secret for each name, plus OPENROUTER_API_KEY.
2. Track it in a project#
Call projects and reuse a close match. Call create_project only when none exists, then pass its name or slug to run.
3. Start the run#
{
"tool": "run",
"arguments": {
"workbench": "acme/code-review",
"task": "Review the open migration and open a draft pull request with fixes.",
"repository": "acme/api",
"ref": "main",
"branch": "migration-review",
"project": "api",
"max_minutes": 90
}
}run returns at once with a run_id and session_id. The run keeps going whether or not you are calling tools. Name the branch with branch, not in the task: the agent can push only its own branch, workbenches/<branch>.
4. Wait#
{ "tool": "wait", "arguments": { "run_id": "wb_...", "after": 0 } }wait blocks until the run asks a question or requests a permission, ends, or timeout_s passes (900 seconds by default, 3600 at most). Progress alone never returns it, so make one call per thing that needs you. A timeout with nothing new is normal: call wait again.
Every result carries next, the cursor of the last event you saw. Pass it back as after so nothing is repeated or missed.
5. Reply#
When wait reports a pending request, answer it with its request ID:
{
"tool": "answer",
"arguments": {
"run_id": "wb_...",
"request_id": "...",
"response": "allow"
}
}To add direction, use send:
mode |
Use when |
|---|---|
send |
The runner is idle and you want a new turn. |
steer |
A turn is in progress and you want to change it. |
queue |
You want the message to run as the next turn. It runs at once when idle. |
send, answer, and cancel return after. Pass it to the next wait.
6. Finish#
Repeat until the status is completed, failed, or cancelled. The final wait result carries the final response, the pull request the agent opened or updated, if any, a summary of changed files, and outcome_url when the run returned files. Call changes for the diff. Use cancel to stop a run early.
Supervising many runs#
Wait on a whole project, or up to 50 runs, at once:
{ "tool": "wait", "arguments": { "project": "api" } }The result's attention lists every run waiting on an answer and every run that ended since the previous call. Answer what is listed, then call wait again with the returned since. It returns at once when attention is not empty or nothing is running.
Run states#
A cloud run moves through queued, provisioning, starting, running, needs_input, completing, and one of completed, failed, or cancelled. A failed run carries the reason as its terminal_reason.
With the CLI#
The same loop on your own machine:
wb run project-core --task-file ./brief.md --detach --json
wb wait wb_... --timeout 120 --json
wb answer wb_... <request-id> allow --json
wb send wb_... "Check the rollback too" --json
wb wait wb_... --run --after 42 --timeout 120 --jsonwb wait exits 2 when the run needs input and 124 on timeout. See Sessions.