Connect a harness
agent-local connect # Claude Code, Codex, Cursor, Gemini CLI, …
agent-local connect --list # what is registered
agent-local connect --remove codex
agent-local mcp --config # the config block, for any other clientRegistration is one JSON edit in the client's config: the absolute path to this binary, run with mcp. Long-running work (create, import, db import) returns a job id; jobs and job ID follow progress. Tool calls run concurrently, so a slow import never blocks the get_job polls that watch it; quick reads time out in 20 seconds rather than hanging when the daemon is wedged.
Tool groups
| Area | Tools |
|---|---|
| discovery | status, list_sites, get_site, localwp_sites, ddev_projects, resolve_path, list_runtimes |
| lifecycle | create_site, attach_site, import_site, start_site, stop_site, restart_site, move_site, delete_site |
| diagnose | probe_site, http_request, get_requests, clear_requests, get_errors, wp_info, get_logs, doctor, doctor_fix |
| fix & undo | checkpoint, list_checkpoints, rollback, delete_checkpoint, db_search, search_replace, magic_login |
| runtime | switch_php, install_runtime, get_http_front, set_http_front |
| domains | set_domain, get_domain_suffix, set_domain_suffix, add_hosts_entries, remove_hosts_entries, cert_status, cert_trust |
| database | db_creds, db_query, db_tables, db_import, db_export, db_reset, db_snapshot, db_snapshots, db_restore, open_adminer |
| files & media | get_media_fallback, set_media_fallback, get_sites_dir, set_sites_dir, yield_ports |
| wordpress | wp_cli, worktree_wp_cli, get_wp_debug, set_wp_debug, get_wp_constants, set_wp_constant — answer 409 for a site that is not WordPress, naming its kind |
list_mail, get_mail, clear_mail | |
| previews | list_branches, add_worktree, list_worktrees, start_worktree, stop_worktree, remove_worktree |
| jobs & sharing | list_jobs, get_job, share_local_site, unshare_local_site |
HTTP API
Every tool is also an endpoint on 127.0.0.1:10809, bearer-token authenticated (agent-local api-token). The shape mirrors the CLI: GET /status, GET|POST /sites, POST /import, POST /sites/{slug}/probe, POST /sites/{slug}/db/query, GET /sites/{slug}/mail and so on. Long-running calls return a job with ?async=1; GET /jobs/{id} follows it. A missing required argument comes back as a protocol error naming it, never a confusing 404.
Root steps (hosts, certificates, ports) go through a scoped passwordless sudo allowlist after the one-time agent-local sudo — exact commands, and cert trust on one fixed path. Not exposed as tools: sudo, alias, connect, update and the daemon controls — one-time installs and binary lifecycle that only make sense run by hand.