Overview

RPC mode enables headless operation of the Autohand CLI via a JSON-RPC protocol. This allows external applications to:

  • Send prompts and receive streaming responses
  • Monitor tool execution in real-time
  • Handle permission requests programmatically
  • Control auto-mode, plan mode, and YOLO mode
  • Manage skills and MCP servers
  • Build custom UIs and IDE extensions
  • Create automated pipelines with full agent control

Looking for session-managed connections? If you need persistent sessions, model switching, and editor-style integration, see ACP Mode instead.

Getting Started

Start RPC Mode

Launch Autohand in RPC mode:

# Basic RPC mode
autohand --mode rpc

# With options
autohand --mode rpc --model nvidia/nemotron-3-super-120b-a12b:free --ephemeral

# All options
autohand --mode rpc \
  --model <model-id> \
  --ephemeral \
  --thinking <level> \
  --config <path>

Protocol Basics

The protocol uses newline-delimited JSON (JSON Lines):

  • Commands: JSON objects sent to stdin, one per line
  • Responses: JSON objects with type: "response"
  • Events: JSON objects streamed during agent operation

Correlation IDs: Include an optional id field in commands. The corresponding response will include the same id for matching.

Core Commands

prompt

Send an instruction to the agent:

{"type": "prompt", "message": "Create a hello.ts file", "id": "1"}

// With file context
{"type": "prompt", "message": "Review this file", "mentions": ["src/index.ts"]}

abort

Cancel the current operation:

{"type": "abort"}

reset

Clear conversation context:

{"type": "reset"}

get_state

Get current agent state:

{"type": "get_state"}

// Response
{
  "type": "response",
  "command": "get_state",
  "success": true,
  "data": {
    "model": "nvidia/nemotron-3-super-120b-a12b:free",
    "provider": "openrouter",
    "workspace": "/path/to/project",
    "isProcessing": false,
    "contextPercent": 85,
    "messageCount": 5
  }
}

get_messages

Get conversation history:

{"type": "get_messages"}

get_history

Get the full session history including tool calls and results:

{"type": "get_history"}

// Response
{
  "type": "response",
  "command": "get_history",
  "success": true,
  "data": {
    "messages": [...],
    "toolCalls": [...],
    "totalTokens": 12500
  }
}

permission_response

Respond to a permission request:

{"type": "permission_response", "requestId": "perm-123", "approve": true}

Auto-Mode Commands

Control the autonomous execution mode programmatically. Auto-mode lets the agent work through tasks without requiring manual prompt-by-prompt interaction.

automode_start

Start auto-mode with an initial instruction:

{"type": "automode_start", "message": "Refactor the auth module", "id": "am-1"}

// Response
{
  "type": "response",
  "command": "automode_start",
  "success": true,
  "data": {"status": "running"}
}

automode_status

Check the current auto-mode status:

{"type": "automode_status"}

// Response
{
  "type": "response",
  "command": "automode_status",
  "success": true,
  "data": {
    "active": true,
    "paused": false,
    "turnsCompleted": 3,
    "turnsRemaining": 7
  }
}

automode_pause

Pause a running auto-mode session:

{"type": "automode_pause"}

automode_resume

Resume a paused auto-mode session:

{"type": "automode_resume"}

automode_cancel

Cancel auto-mode entirely:

{"type": "automode_cancel"}

automode_get_log

Get the auto-mode execution log:

{"type": "automode_get_log"}

// Response
{
  "type": "response",
  "command": "automode_get_log",
  "success": true,
  "data": {
    "entries": [
      {"turn": 1, "action": "Read file src/auth.ts", "timestamp": "..."},
      {"turn": 2, "action": "Edited src/auth.ts", "timestamp": "..."}
    ]
  }
}

Skill Commands

Manage the agent's skill registry programmatically.

get_skills_registry

List all available skills:

{"type": "get_skills_registry"}

// Response
{
  "type": "response",
  "command": "get_skills_registry",
  "success": true,
  "data": {
    "skills": [
      {
        "name": "commit",
        "description": "Create a git commit",
        "source": "builtin"
      },
      {
        "name": "review-pr",
        "description": "Review a pull request",
        "source": "plugin:code-review"
      }
    ]
  }
}

install_skill

Install a skill from a registry or path:

{
  "type": "install_skill",
  "source": "npm:@autohand/skill-tdd",
  "id": "sk-1"
}

// Response
{
  "type": "response",
  "command": "install_skill",
  "success": true,
  "data": {"name": "tdd", "installed": true}
}

MCP Commands

Manage Model Context Protocol servers and tools.

mcp_list_servers

List configured MCP servers:

{"type": "mcp_list_servers"}

// Response
{
  "type": "response",
  "command": "mcp_list_servers",
  "success": true,
  "data": {
    "servers": [
      {"name": "context7", "status": "connected", "tools": 2},
      {"name": "brave-search", "status": "connected", "tools": 1}
    ]
  }
}

mcp_list_tools

List all available MCP tools across servers:

{"type": "mcp_list_tools"}

// Response
{
  "type": "response",
  "command": "mcp_list_tools",
  "success": true,
  "data": {
    "tools": [
      {"name": "query-docs", "server": "context7"},
      {"name": "brave_web_search", "server": "brave-search"}
    ]
  }
}

mcp_get_server_configs

Get the full configuration for all MCP servers:

{"type": "mcp_get_server_configs"}

// Response
{
  "type": "response",
  "command": "mcp_get_server_configs",
  "success": true,
  "data": {
    "configs": {
      "context7": {
        "command": "npx",
        "args": ["-y", "@upstash/context7-mcp@latest"]
      }
    }
  }
}

mcp_set_vscode_tools

Configure MCP tools for VS Code integration. This syncs tool availability between the CLI and VS Code extension:

{
  "type": "mcp_set_vscode_tools",
  "tools": ["query-docs", "brave_web_search"]
}

mcp_invoke_response

Send the result of an MCP tool invocation back to the agent. Used when the host application handles tool execution:

{
  "type": "mcp_invoke_response",
  "toolCallId": "call_abc123",
  "result": {"content": "Search results..."}
}

Mode Commands

Control agent operation modes.

plan_mode_set

Toggle plan mode on or off. In plan mode, the agent designs an approach before making changes:

// Enable plan mode
{"type": "plan_mode_set", "enabled": true}

// Disable plan mode
{"type": "plan_mode_set", "enabled": false}

// Response
{
  "type": "response",
  "command": "plan_mode_set",
  "success": true,
  "data": {"planMode": true}
}

yolo_set

Toggle YOLO mode. When enabled, the agent auto-approves all tool executions without permission prompts:

// Enable YOLO mode
{"type": "yolo_set", "enabled": true}

// With specific patterns
{"type": "yolo_set", "enabled": true, "patterns": ["npm *", "git *"]}

// Response
{
  "type": "response",
  "command": "yolo_set",
  "success": true,
  "data": {"yolo": true}
}

Use YOLO mode with care: Auto-approving all operations can lead to unwanted changes. Consider using patterns to limit which commands are auto-approved.

File Change Commands

changes_decision

Accept or reject file changes proposed by the agent. When the agent writes or edits files, you can review and decide on each change:

// Accept changes
{
  "type": "changes_decision",
  "changeId": "chg-456",
  "decision": "accept"
}

// Reject changes
{
  "type": "changes_decision",
  "changeId": "chg-456",
  "decision": "reject"
}

// Response
{
  "type": "response",
  "command": "changes_decision",
  "success": true,
  "data": {"applied": true}
}

Events

Events are streamed to stdout during agent operation:

Event Description
agent_start Agent ready to accept commands
agent_end Agent shutting down
turn_start Started processing a prompt
turn_end Finished processing a prompt
message_start LLM response started
message_update Streaming content delta
message_end LLM response completed
tool_execution_start Tool execution started
tool_execution_update Tool output chunk
tool_execution_end Tool execution completed
permission_request Awaiting approval for action
automode_turn Auto-mode completed a turn
automode_complete Auto-mode finished all turns
file_change File was created, modified, or deleted
error Error occurred

Event Examples

// Turn lifecycle
{"type": "turn_start", "timestamp": "...", "data": {"instruction": "Create a file"}}
{"type": "turn_end", "timestamp": "...", "data": {"success": true}}

// Message streaming
{"type": "message_update", "timestamp": "...", "data": {"delta": "I'll create "}}

// Tool execution
{"type": "tool_execution_start", "timestamp": "...", "data": {
  "toolName": "write_file",
  "toolCallId": "call_abc",
  "args": {"path": "hello.ts"}
}}
{"type": "tool_execution_end", "timestamp": "...", "data": {
  "toolName": "write_file",
  "toolCallId": "call_abc",
  "success": true,
  "duration": 150
}}

// Permission request
{"type": "permission_request", "timestamp": "...", "data": {
  "requestId": "perm-123",
  "tool": "run_command",
  "message": "Execute: npm install?"
}}

// Auto-mode events
{"type": "automode_turn", "timestamp": "...", "data": {
  "turn": 3,
  "total": 10,
  "action": "Edited src/auth.ts"
}}
{"type": "automode_complete", "timestamp": "...", "data": {
  "turnsCompleted": 5,
  "success": true
}}

// File change
{"type": "file_change", "timestamp": "...", "data": {
  "path": "src/hello.ts",
  "action": "created",
  "changeId": "chg-456"
}}

Examples

Python Client

import subprocess
import json

proc = subprocess.Popen(
    ["autohand", "--mode", "rpc", "--ephemeral"],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    text=True
)

def send(cmd):
    proc.stdin.write(json.dumps(cmd) + "\n")
    proc.stdin.flush()

def read_events():
    for line in proc.stdout:
        yield json.loads(line)

# Wait for ready
for event in read_events():
    if event.get("type") == "agent_start":
        break

# Send prompt
send({"type": "prompt", "message": "Hello!", "id": "1"})

# Process events
for event in read_events():
    if event.get("type") == "message_update":
        print(event["data"]["delta"], end="", flush=True)

    if event.get("type") == "permission_request":
        # Handle permission
        send({
            "type": "permission_response",
            "requestId": event["data"]["requestId"],
            "approve": True
        })

    if event.get("type") == "turn_end":
        print()
        break

proc.terminate()

Node.js Client

const { spawn } = require("child_process");
const readline = require("readline");

const agent = spawn("autohand", ["--mode", "rpc", "--ephemeral"]);

const rl = readline.createInterface({ input: agent.stdout });

rl.on("line", (line) => {
    const event = JSON.parse(line);

    if (event.type === "message_update") {
        process.stdout.write(event.data.delta);
    }

    if (event.type === "permission_request") {
        agent.stdin.write(JSON.stringify({
            type: "permission_response",
            requestId: event.data.requestId,
            approve: true
        }) + "\n");
    }

    if (event.type === "turn_end") {
        console.log("\n--- Done ---");
    }
});

// Send prompt after ready
setTimeout(() => {
    agent.stdin.write(JSON.stringify({
        type: "prompt",
        message: "List files in current directory"
    }) + "\n");
}, 1000);

process.on("SIGINT", () => {
    agent.stdin.write(JSON.stringify({ type: "abort" }) + "\n");
    agent.kill();
});

Auto-Mode Example

import subprocess
import json

proc = subprocess.Popen(
    ["autohand", "--mode", "rpc"],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    text=True
)

def send(cmd):
    proc.stdin.write(json.dumps(cmd) + "\n")
    proc.stdin.flush()

def read_events():
    for line in proc.stdout:
        yield json.loads(line)

# Wait for ready
for event in read_events():
    if event.get("type") == "agent_start":
        break

# Enable YOLO mode for auto-approval
send({"type": "yolo_set", "enabled": True, "patterns": ["git *"]})

# Start auto-mode
send({
    "type": "automode_start",
    "message": "Add unit tests for the auth module"
})

# Monitor progress
for event in read_events():
    if event.get("type") == "automode_turn":
        d = event["data"]
        print(f"Turn {d['turn']}/{d['total']}: {d['action']}")

    if event.get("type") == "automode_complete":
        print("Auto-mode finished!")
        break

proc.terminate()

Use Cases

IDE Extensions

Build VS Code, JetBrains, or Zed extensions that embed Autohand:

  • Spawn RPC process on extension activation
  • Send file context with mentions
  • Display streaming responses in editor panels
  • Handle permission requests via UI dialogs

Custom Web UIs

Create browser-based interfaces:

  • Backend spawns RPC process
  • WebSocket bridges JSON events to browser
  • React/Vue components render streaming output
  • Users approve/deny via web interface

CI/CD Integration

Programmatic control in pipelines:

  • Auto-approve safe operations with YOLO patterns
  • Use auto-mode for multi-step tasks
  • Collect structured output for reports
  • Chain multiple prompts in sequence

Error Handling

Failed commands return responses with success: false:

{
  "type": "response",
  "command": "prompt",
  "success": false,
  "error": "Agent is already processing a request"
}

// Parse errors
{
  "type": "response",
  "command": "parse",
  "success": false,
  "error": "Invalid JSON: Unexpected token..."
}

Always handle errors: Check success field in responses and listen for error events to handle failures gracefully.

Future Commands

These commands are planned for future releases:

  • Model: set_model, cycle_model, get_available_models
  • Thinking: set_thinking_level, cycle_thinking_level
  • Compaction: compact, set_auto_compaction