Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save vmitchell85/f00c30d93ff397ce4f43c6411f384d1e to your computer and use it in GitHub Desktop.

Select an option

Save vmitchell85/f00c30d93ff397ce4f43c6411f384d1e to your computer and use it in GitHub Desktop.
Solo Orchestration

Solo MCP multi-agent orchestrator guide

Last investigated: 2026-04-30 Project: Desktop 2 (project_id: 3), path /Users/aaron/Code/soloterm/desktop-2Current actor: Solo agent process 2992 (Codex W), actor mcp-e0f2bae3b20314f1

Mental model

Solo is a project-scoped control plane for commands, terminals, and agent sessions. The MCP tools let an orchestrator agent discover processes, spawn workers, send prompts or shell input, read terminal output, coordinate shared state, and schedule wake-ups without running polling loops.

Use Solo as the durable outer loop:

  • Processes are the workers: command processes, terminals, and agent sessions.
  • Scratchpads are shared long-form memory: plans, findings, runbooks, integration notes, and reports.
  • Todos are the execution queue: ownership, blockers, comments, completion state, and task-level locks.
  • Timers wake the orchestrator later: periodic check-ins, worker-idle callbacks, and deadline guards.
  • Locks protect shared files or logical resources from concurrent edits.
  • Readiness tools discover and wait for local services without scraping logs.

Important ID rule: Solo process_id values are Solo database IDs from tools such as list_processes and spawn_process. They are not OS PIDs and they are not developer-runtime agent IDs from other orchestration systems. Tool responses may also include pid; use that only when the OS PID is explicitly needed.

Active Solo MCP tool surface

Discovery and scope:

  • help(topic?): read capability docs.
  • whoami(): identify the current Solo actor, process, and effective project.
  • list_projects(): list Solo projects.
  • select_project(project_id): set the default project for later MCP calls.
  • get_project_status(project_id?): snapshot project processes and status.
  • get_project_stats(project_id?): CPU, memory, and process status summary.

Process lifecycle:

  • list_processes(project_id?): list command, terminal, and agent processes.
  • get_process_status(process_id | process_name): detailed status, including agent idle/thinking state when available.
  • start_process, stop_process, restart_process: lifecycle control for an existing Solo process.
  • start_all_commands, stop_all_commands, restart_all_commands: bulk lifecycle for trusted command processes only.
  • close_process: remove a Solo terminal or Solo agent entry. Do not use it for command processes.
  • rename_process: change a process display name.

Input and output:

  • get_process_output(process_id | process_name, lines?): recent rendered terminal rows.
  • get_process_raw_output(process_id | process_name, lines?): raw output, useful for ANSI/full-screen/debug details.
  • search_output and search_raw_output: search rendered or raw output.
  • send_input(process_id | process_name, input, submit?): type text into a process; submits Enter by default.
  • send_input(bytes=[3]): Ctrl-C; bytes=[4]: Ctrl-D; bytes=[27]: Escape.
  • clear_output: clear Solo's saved output buffer without touching the PTY.
  • get_process_ports: inspect bound ports and URLs for one process and children.

Spawning:

  • list_agent_tools(): discover available agent runtimes.
  • spawn_process(kind="terminal", name?): create a new interactive shell.
  • spawn_process(kind="agent", agent_tool_id, name?, include_agent_instructions?): create a new Solo-managed agent session.

Timers:

  • timer_set(delay_ms, body, loop?, repeat_every_ms?, metadata?): one-shot or repeating wake-up for the bound Solo agent process.
  • timer_fire_when_idle_any(processes, max_wait_ms, body): wake when any watched non-idle terminal/agent becomes idle, or when the guard expires.
  • timer_fire_when_idle_all(processes, max_wait_ms, body): wake when all watched terminals/agents are idle, or when the guard expires.
  • timer_list, timer_cancel, timer_pause, timer_resume: manage pending timers owned by the current actor.

Scratchpads:

  • scratchpad_write: create or replace shared markdown/text content. Use expected_revision when replacing.
  • scratchpad_append: append safely; expected_revision is optional because append cannot clobber existing content.
  • scratchpad_read: read full content, line slices, heading outlines, or a single section.
  • scratchpad_list, scratchpad_tags_list: discover scratchpads.
  • scratchpad_rename, scratchpad_add_tags, scratchpad_remove_tags: metadata changes with revision guards.
  • scratchpad_save_to_file, scratchpad_load_from_file: bridge scratchpads to repository files.
  • scratchpad_clear, scratchpad_delete, scratchpad_archive, scratchpad_transfer: lifecycle and movement.

Todos:

  • todo_create, todo_list, todo_get, todo_update, todo_complete, todo_delete: core task management.
  • todo_add_tag, todo_remove_tag, todo_tags_list: tags.
  • todo_set_blockers, todo_add_blocker, todo_remove_blocker: dependencies.
  • todo_lock, todo_unlock: lease-based task editing coordination.
  • todo_comment_create, todo_comment_update, todo_comment_delete, todo_comment_list: discussion and status notes.
  • todo_transfer: move a todo to another project.

Locks:

  • lock_acquire(lock_key, lease_ttl_seconds): non-blocking mutual exclusion.
  • lock_status(lock_key): inspect owner and state.
  • lock_release(lock_key): release a lock owned by the current actor.

Readiness and services:

  • services_list: list detected project-local services, readiness, ports, and URLs.
  • wait_for_bound_port(process_id | process_name, timeout_ms?): wait until a tracked process exposes a listening port.
  • get_process_ports: point lookup for process listeners.

Support and integration:

  • bind_session_process: bind the MCP session to a Solo process ID.
  • register_agent: register an external actor identity; Solo-managed processes usually do not need this.
  • setup_agent_integration: add Solo MCP docs to CLAUDE.md or AGENTS.md.
  • submit_solo_feedback: open a drafted feedback report for Solo.

Help mentioned kv_* tools and list_prompts/get_prompt, but those are not exposed in this session's active callable Solo tool list. Treat KV as unavailable here; use scratchpads, todos, tags, and locks for shared state unless the callable surface changes.

Orchestration lifecycle

1. Bootstrap the run

Start by establishing scope and current state:

whoami()
get_project_status()
get_project_stats()
list_agent_tools()
timer_list()
scratchpad_list(tags=["orchestration"])
todo_list(completed=false)

Create a run scratchpad with the goal, constraints, worker assignments, known process IDs, and links to todos. Create todos for each durable work item. Use tags such as orchestration, investigation, frontend, backend, or state/active so other agents can discover the work.

A good scratchpad layout for a multi-agent run:

Title: Run: <goal>
Section: Goal
Section: Constraints
Section: Process Map
Section: Todo Map
Section: Decisions
Section: Worker Assignments
Section: Findings
Subsection: Worker A
Subsection: Worker B
Section: Integration Notes
Section: Final State

2. Split work before spawning

Delegate bounded, independent side work. Keep the immediate critical-path task local unless another worker can work independently without blocking the orchestrator's next action.

For code changes, give each worker an ownership boundary:

  • Files or modules they own.
  • Files or modules they must not edit.
  • The exact output expected in their final message.
  • A reminder that other agents may be editing the codebase and they must not revert unrelated changes.

Track the assignment with a todo and, for larger work, a scratchpad section.

3. Spawn workers

Use list_agent_tools() to choose the runtime, then spawn_process.

spawn_process(kind="agent", agent_tool_id=4, name="Explorer: terminal streams")

For spawned agents, the response can include agent_instructions. Prepend those instructions to the first prompt you send to the agent. Then send the task with send_input:

send_input(
  process_id=<worker_process_id>,
  input="<agent_instructions>\n\nTask: inspect terminal stream handling. Own read-only analysis. Report files and risks; do not edit."
)

For command-line experiments or manual shell work, spawn a terminal:

spawn_process(kind="terminal", name="Repro shell")
send_input(process_id=<terminal_id>, input="npm run test:e2e:auto")

4. Monitor without polling

Use timers instead of sleep loops. Timer bodies are injected into the owning agent as fresh user turns, so make them self-contained: include process IDs, scratchpad IDs, todo IDs, and the next action.

Wait for one worker to go quiet:

timer_fire_when_idle_any(
  processes=[<worker_a>, <worker_b>],
  max_wait_ms=600000,
  body="One worker went idle. Read get_process_output for worker A and B, update scratchpad <id>, then decide whether to continue waiting or integrate."
)

Wait for all workers:

timer_fire_when_idle_all(
  processes=[<worker_a>, <worker_b>],
  max_wait_ms=900000,
  body="All watched workers are idle or the deadline fired. Read outputs, update todos, append integration notes to scratchpad <id>, then continue orchestration."
)

Periodic check-in:

timer_set(
  delay_ms=120000,
  loop=true,
  body="Periodic orchestration check. Inspect active worker processes, summarize progress in scratchpad <id>, and cancel this timer when the run is done."
)

Cancel timers when the watched work is complete.

5. Read and write process IO

Use rendered output for normal agent and terminal transcripts:

get_process_output(process_id=<worker_id>, lines=80)
search_output(process_id=<worker_id>, pattern="error", max_results=20)

Use raw output when ANSI state, alternate screen output, or overwritten lines matter:

get_process_raw_output(process_id=<worker_id>, lines=120)
search_raw_output(process_id=<worker_id>, pattern="FAILED", max_results=20)

Send input deliberately:

send_input(process_id=<worker_id>, input="Please continue with option 2.")
send_input(process_id=<terminal_id>, bytes=[3])  # Ctrl-C

Prefer newline-oriented, coarse updates when driving agents. Avoid ESC-heavy repaint loops and frequent terminal redraws unless a full-screen TUI is actually required.

6. Coordinate shared state

Use scratchpads for durable narrative state:

  • The orchestrator owns the top-level run scratchpad.
  • Workers can append findings to assigned sections or produce separate scratchpads tagged with the run tag.
  • Use scratchpad_read(mode="headings") for large scratchpads before reading sections.
  • Use expected_revision when overwriting or renaming to avoid clobbering another agent's edits.
  • Prefer scratchpad_append for concurrent notes because it cannot overwrite existing content.

Use todos for actionable work:

  • One todo per worker assignment or durable task.
  • Tags make queues discoverable.
  • Blockers express dependencies.
  • Comments hold short progress notes.
  • todo_lock before editing a todo that another agent may edit.
  • todo_complete when the assignment is done.

Use locks for critical shared resources:

lock_acquire(lock_key="edit:src/features/terminal", lease_ttl_seconds=600)
lock_status(lock_key="edit:src/features/terminal")
lock_release(lock_key="edit:src/features/terminal")

Name locks by logical ownership, not just a filename, when several files form one consistency boundary.

7. Handle services and long-running commands

For YAML-backed command processes, use Solo lifecycle tools. Commands may need trust in the Solo UI before MCP can start or restart them.

start_process(process_name="Tauri - dev")
wait_for_bound_port(process_name="Tauri - dev", timeout_ms=120000)
services_list()
get_process_ports(process_name="Tauri - dev")

For tests or logs:

start_process(process_name="Test - local")
timer_fire_when_idle_any(
  processes=["Test - local"],
  max_wait_ms=1800000,
  body="The local test process is idle or the deadline fired. Read output, search for failures, and update the test todo."
)

Use stop_process or restart_process for command processes. Use close_process only for Solo terminals and Solo agents.

8. Integrate and close down

When workers finish:

  1. Read each worker's rendered output and, if needed, raw output.
  2. Update the run scratchpad with findings, changed files, test results, and open risks.
  3. Mark finished todos complete and leave comments for partial work.
  4. Cancel no-longer-needed timers.
  5. Stop command processes that should not remain running.
  6. Close temporary worker agents and terminals when their context is no longer needed.
  7. Release any locks owned by the orchestrator.

Practical worker prompt template

<agent_instructions from spawn_process>

You are part of a Solo-orchestrated multi-agent run.

Goal: <goal>
Your assignment: <bounded task>
Ownership: <files/modules/logical area>
Do not edit outside this ownership without reporting why first.
Other agents may be editing the same repository; do not revert unrelated changes.
Shared scratchpad: <scratchpad_id/name>
Todo: <todo_id>
Expected output: summarize findings, files changed or inspected, verification run, and remaining risks.

Recommended patterns

  • Use whoami and get_project_status at the start of a run.
  • Use explicit project_id when crossing project boundaries.
  • Use scratchpads for long-lived shared context; use todos for execution state.
  • Use timers for wake-ups and worker quiet periods; avoid manual polling.
  • Use services_list and wait_for_bound_port for server readiness.
  • Use rendered output first, raw output second.
  • Use locks for shared edit boundaries and todo locks for task metadata edits.
  • Keep timer bodies self-contained enough for a cold resume.
  • Prefer appending to scratchpads over rewriting while multiple agents are active.
  • Close temporary agents and terminals after integration so future process lists stay readable.

Anti-patterns

  • Do not confuse Solo process_id with OS pid.
  • Do not rely on KV in this session; the active callable tools do not expose it.
  • Do not start command processes that have not been trusted in the Solo UI.
  • Do not close command processes; stop or restart them.
  • Do not scrape logs to detect service readiness when wait_for_bound_port or services_list can answer directly.
  • Do not leave repeating timers running after the run ends.
  • Do not use high-frequency terminal output as a progress protocol for agents unless the task requires a full-screen TUI.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment