@itsaplan/runner runs an external agent
on your own machine. It has a preset for each of seven coding agent CLIs. A preset builds
the command itself: the flags for an unattended run, and the session resume.
Each preset needs two files in the working directory:
- the CLI's own config, which points it at the MCP server of your instance,
itsaplan-runner.json, which points the runner at your instance.
This page holds both files for each preset, and the notes that apply to that CLI. The runner's own settings are in its README.
Enable MCP for the project first, in Settings, MCP Server. It is off by default.
Claude Code reads .mcp.json from the working directory:
{
"mcpServers": {
"itsaplan": {
"type": "http",
"url": "http://localhost:3000/mcp",
"headers": { "Authorization": "Bearer ${ITSAPLAN_API_KEY}" }
}
}
}itsaplan-runner.json:
{
"url": "http://localhost:3000",
"apiKey": "the key you copied on creation",
"agent": "claude",
"cwd": "/Users/me/work/my-repo"
}- Claude Code uses this server in addition to the servers that the machine already has.
Your own MCP servers, skills and memory stay available in the run. For a file in a
different location, set
"args": ["--mcp-config", "/path/to/mcp.json"]. - The preset passes
--permission-mode auto. No person is available to approve an action. A classifier thus examines each action, MCP tool calls included. Repeat the flag inargsto select a different mode. - The preset also passes
--output-format stream-json --include-partial-messages. This stream gives the chat answer word by word, and includes the tool calls.--append-system-promptgives Claude Code the context of the run.
Codex reads its MCP servers from ~/.codex/config.toml. bearer_token_env_var takes the
key from the environment that the runner sets:
[mcp_servers.itsaplan]
url = "http://localhost:3000/mcp"
bearer_token_env_var = "ITSAPLAN_API_KEY"itsaplan-runner.json:
{
"url": "http://localhost:3000",
"apiKey": "the key you copied on creation",
"agent": "codex",
"cwd": "/path/to/working-dir",
"args": ["--skip-git-repo-check"]
}- Codex has no system-prompt flag. The preset puts the context of the run before the task. The preset sends both on stdin.
- The preset passes
-c sandbox_mode="workspace-write". Codex then writes only in the working directory. The preset uses a config override and not--sandbox, becausecodex exec resumedoes not accept that flag.codex exec resumeruns each message after the first one. Each argument that you add must thus be applicable tocodex execand tocodex exec resume. - Add
--skip-git-repo-checkonly if the working directory is not a git repository.
Antigravity CLI reads its MCP servers from ~/.gemini/config/mcp_config.json, one file
per machine. Its headers take no variables, so you write the key in the file. A remote
server is named by serverUrl, not by url or httpUrl:
{
"mcpServers": {
"itsaplan": {
"serverUrl": "http://localhost:3000/mcp",
"headers": { "Authorization": "Bearer the-agent-key" }
}
}
}itsaplan-runner.json:
{
"url": "http://localhost:3000",
"apiKey": "the key you copied on creation",
"agent": "antigravity",
"cwd": "/path/to/working-dir"
}- The binary is
agy. Sign in once by running it interactively. - Antigravity CLI has no system-prompt flag, and it does not read stdin. The preset thus
sends the context of the run and the task in one
-pargument. - The preset passes
--dangerously-skip-permissions. Antigravity CLI then does not wait for an approval. Without the flag a denied tool call still exits0. - The preset passes
--output-format stream-jsonand--print-timeout 24h. The stream gives the chat answer as it is written, includes the tool calls, and names the conversation that the session resume uses. The raised timeout leavestimeoutMsto end a long task. - One MCP config per machine means one key.
apiKeysandagentsstill run several agents, but they all reach the instance as the agent whose key is in that file.
GitHub Copilot CLI reads .mcp.json from the working directory, but only once you have
trusted that directory interactively. --additional-mcp-config loads the same file
without that step, which is what the runner config below passes. Its headers accept no
variables. You thus write the key in the file:
{
"mcpServers": {
"itsaplan": {
"type": "http",
"url": "http://localhost:3000/mcp",
"headers": { "Authorization": "Bearer the-agent-key" },
"tools": ["*"]
}
}
}itsaplan-runner.json:
{
"url": "http://localhost:3000",
"apiKey": "the key you copied on creation",
"agent": "copilot",
"cwd": "/path/to/working-dir",
"args": ["--additional-mcp-config", "@.mcp.json"]
}- Copilot CLI has no system-prompt flag, and it does not read stdin. The preset thus sends
the context of the run and the task in one
-pargument. - The preset passes
--allow-all-toolsand--no-ask-user. Copilot CLI then does not wait for an approval. It still denies a path outside the working directory, and the run continues and exits0. Add--add-diror--allow-all-pathstoargsfor a task that needs one. - The preset passes
--output-format json. This stream gives the chat answer as it is written, and includes the tool calls. Its last line names the session, which--session-idresumes.
opencode reads opencode.json from the working directory. It expands {env:VAR}. The key
thus stays out of the file:
{
"$schema": "https://opencode.ai/config.json",
"model": "anthropic/claude-sonnet-5",
"mcp": {
"itsaplan": {
"type": "remote",
"url": "http://localhost:3000/mcp",
"enabled": true,
"headers": { "Authorization": "Bearer {env:ITSAPLAN_API_KEY}" }
}
}
}itsaplan-runner.json:
{
"url": "http://localhost:3000",
"apiKey": "the key you copied on creation",
"agent": "opencode",
"cwd": "/path/to/working-dir"
}opencode runtakes the prompt as an argument and does not read stdin. The preset thus sends the context of the run and the task in one argument.- Set
modelasprovider/model. opencode supports many providers. Without this key, a run uses the model of the last interactive session. Runopencode auth loginto make a provider available.
Pi reads MCP servers through the pi-mcp-adapter extension. Install it once:
pi install npm:pi-mcp-adapterThe extension reads .mcp.json from the working directory:
{
"mcpServers": {
"itsaplan": {
"type": "http",
"url": "http://localhost:3000/mcp",
"headers": { "Authorization": "Bearer ${ITSAPLAN_API_KEY}" }
}
}
}itsaplan-runner.json:
{
"url": "http://localhost:3000",
"apiKey": "the key you copied on creation",
"agent": "pi",
"cwd": "/path/to/working-dir"
}- The preset passes
-p --mode json. The stream gives the chat answer word by word, the tool calls, and the session id on the first line.--session-idresumes that session. --append-system-promptgives Pi the context of the run.- Print mode does not wait for tool approval.
--approveis only project-trust for.pi/settings.json, not tool permission. - Sign in once with
piinteractively so a provider is available. - Headers in
.mcp.jsonexpand${ITSAPLAN_API_KEY}only if yourpi-mcp-adapterversion does. If they stay literal, write the key in the file the way Copilot's config does.
Oh My Pi reads .mcp.json from the working directory:
{
"mcpServers": {
"itsaplan": {
"type": "http",
"url": "http://localhost:3000/mcp",
"headers": { "Authorization": "Bearer ${ITSAPLAN_API_KEY}" }
}
}
}itsaplan-runner.json:
{
"url": "http://localhost:3000",
"apiKey": "the key you copied on creation",
"agent": "omp",
"cwd": "/path/to/working-dir"
}- The binary is
omp. Sign in once by running it interactively. - The preset passes
-p --mode json --auto-approve. No person is available to approve a tool. Repeat--approval-modeinargsto pickwriteinstead of skipping every prompt. --mode jsonis the same stream Pi writes.--resumetakes the session id from the opening line.--append-system-promptgives Oh My Pi the context of the run.- Headers in
.mcp.jsonexpand${ITSAPLAN_API_KEY}only if your Oh My Pi build does. If they stay literal, write the key in the file the way Copilot's config does.