external-agent-mcp
Local stdio MCP runtime that lets Codex delegate work to installed external CLI coding agents. Codex starts asynchronous jobs through one MCP call, then polls for status, free-form results, logs, and sandbox patch artifacts.
The server is dependency-free Node.js and speaks MCP over line-delimited JSON-RPC on stdio.
Providers
Initial provider adapters:
- Cursor Agent via
/usr/local/bin/cursor agent --print - Gemini CLI via
/opt/homebrew/bin/gemini --prompt ... - Claude Code via
/opt/homebrew/bin/claude --print
Model selection is caller-controlled with the model argument. The MCP server does not hardcode routing logic such as "simple task uses X, complex task uses Y".
Tools
delegate_tasks
Creates one or more async jobs.
Required arguments:
repo_path: absolute repository/workspace pathprovider:cursor,gemini, orclaudetasks: array of task strings or task objects
Common optional arguments:
mode:analysisorsandbox_patch; defaults toanalysismodel: provider-specific model overridefiles: focus files underrepo_pathextra_context: additional backgroundtimeout_sec: defaults to600, capped at1800max_output_chars: defaults to30000, capped at100000base_ref: git ref forsandbox_patch; defaults toHEAD
Task objects may override provider, model, mode, files, extra_context, timeout_sec, max_output_chars, and base_ref.
job_status
Returns job lifecycle state and stdout/stderr tails. Status values are:
queuedrunningsucceededfailedtimed_outcancelledorphaned
If no job_id or job_ids are supplied, recent jobs are returned.
job_result
Returns the external agent's free-form result text plus MCP-managed metadata:
- command metadata and exit status
- stdout/stderr log paths
result.mddiff.patch,diff.stat, and changed files forsandbox_patch
search_jobs
Searches historical jobs by lightweight metadata and previews. This is the stable history lookup interface; full text and patch artifacts should still be read with job_result.
Supported filters:
repo_path: exact repository pathprovider:cursor,gemini, orclaudemodel: exact model stringmode:analysisorsandbox_patchstatus: any ofqueued,running,succeeded,failed,timed_out,
cancelled, or orphaned
created_after/created_before: ISO timestampsquery: case-insensitive substring search over title, task, result preview,
paths, provider/model/mode, and focused files
limit: defaults to20, capped at100cursor: opaque pagination cursor from a previous response
The response returns job summaries, previews, hashes, and artifact paths. It does not return large stdout, stderr, result, or patch bodies.
cancel_jobs
Cancels queued or running jobs.
cleanup_jobs
Removes terminal job logs/artifacts and associated sandbox worktrees. Use force: true only when cleaning non-terminal jobs intentionally.
agent_status
Checks provider binaries and reports capability metadata:
supports_analysissupports_sandbox_patchsupports_modelssafe_write_mode
quality_fix
Runs allow-listed deterministic quality commands over a bounded file set. This path is for mechanical fixes and does not spend LLM agent tokens.
Default commands:
ruff_format:ruff format <files>ruff_safe_fix:ruff check --fix <files>
Additional commands:
ruff_check:ruff check <files>ruff_unsafe_fix:ruff check --fix --unsafe-fixes <files>; requires
allow_unsafe_fixes: true
Job Storage
Jobs are persisted under:
~/.cache/external-agent-mcp/jobs
Override with:
EXTERNAL_AGENT_JOB_ROOT=/path/to/jobs
Each job directory contains:
job.jsonevents.jsonlstdout.logstderr.logresult.mddiff.patchdiff.stat
If the MCP server restarts, non-terminal jobs from the previous process are marked orphaned. Completed job artifacts remain readable.
The current storage implementation is FileJobStore: metadata is read from job.json, events are appended to events.jsonl, and large artifacts remain as plain files. search_jobs is intentionally defined above this storage layer so a future SQLite-backed index can replace the file scan without changing the MCP tool contract.
New jobs include stable metadata for history and future caching:
schema_versionrepo_headtask_hashprompt_hashduration_msresult_preview
Sandbox Patch Mode
mode: "sandbox_patch" requires a git repository. The server creates an isolated git worktree under the job directory, runs the external agent there, and then captures git diff --binary and git diff --stat.
The original repository is not modified by agent writes.
Provider write policy is conservative:
- Cursor sandbox patch is marked experimental and is launched without
--force or --yolo.
- Gemini uses
--approval-mode auto_edit. - Claude uses
--permission-mode acceptEditswith a restricted tool list. - The server rejects adapter commands that include
--force,--yolo, or
bypass-permission flags.
Codex Config
Add this to ~/.codex/config.toml or a trusted project .codex/config.toml:
[mcp_servers.external_agent]
command = "node"
args = ["/Users/luchong/OpenSourceProject/external-agent-mcp/src/server.mjs"]
startup_timeout_sec = 10
tool_timeout_sec = 900
[mcp_servers.external_agent.env]
CURSOR_AGENT_BIN = "/usr/local/bin/cursor"
GEMINI_BIN = "/opt/homebrew/bin/gemini"
CLAUDE_BIN = "/opt/homebrew/bin/claude"
RUFF_BIN = "ruff"
EXTERNAL_AGENT_ALLOWED_ROOTS = "/Users/luchong/Desktop:/Users/luchong/OpenSourceProject"
EXTERNAL_AGENT_MAX_CONCURRENCY = "2"
After changing MCP config, refresh/restart Codex or start a new thread so the new tool surface is loaded.
Examples
Parallel Analysis
{
"provider": "cursor",
"model": "composer-2.5",
"repo_path": "/Users/luchong/Desktop/Agnes_Core",
"mode": "analysis",
"tasks": [
"Map the agent stream cancellation call path. Return concise findings with file paths.",
"Audit prompt/skill dispatch boundaries. Return risks and missing tests."
],
"timeout_sec": 900
}
Sandbox Patch
{
"provider": "claude",
"repo_path": "/Users/luchong/Desktop/Agnes_Core",
"mode": "sandbox_patch",
"tasks": [
{
"task": "Fix the focused Ruff SIM102 issue and summarize the patch.",
"files": ["services/example/path.py"]
}
]
}
Then call job_status until terminal and job_result to inspect the free-form result, logs, and patch path.
Search History
{
"repo_path": "/Users/luchong/Desktop/Agnes_Core",
"provider": "cursor",
"model": "composer-2.5",
"mode": "analysis",
"status": ["succeeded", "failed"],
"query": "stream cancellation",
"limit": 20
}
Deterministic Ruff Fix
{
"repo_path": "/Users/luchong/Desktop/Agnes_Core",
"files": ["services/example/path.py"],
"commands": ["ruff_format", "ruff_safe_fix"],
"timeout_sec": 120,
"max_changed_files": 10
}
Manual Test
npm test





