21  RPC Mode

21.1 Overview

RPC (Remote Procedure Call) mode turns Pi into a stdin/stdout JSON-RPC server. It is designed for editor integrations, IDE plugins, and other process-control scenarios where a parent process needs bidirectional communication with Pi.

21.2 Starting RPC Mode

pi --mode rpc

Or programmatically:

echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | pi --mode rpc

RPC mode reads JSON-RPC messages from stdin and writes responses and events to stdout. Each message is a single line of JSON.

21.3 Protocol Overview

sequenceDiagram
    participant Client
    participant Pi
    Client->>Pi: initialize
    Pi-->>Client: initialized event
    Client->>Pi: setModel
    Pi-->>Client: modelChanged event
    Client->>Pi: prompt
    Pi-->>Client: assistantMessage (streaming)
    Pi-->>Client: toolCall
    Client->>Pi: toolResult (if needed)
    Pi-->>Client: assistantMessage
    Pi-->>Client: turnEnd

The protocol follows JSON-RPC 2.0 conventions:

  • Requests: { "jsonrpc": "2.0", "id": 1, "method": "...", "params": {...} }
  • Responses: { "jsonrpc": "2.0", "id": 1, "result": {...} }
  • Notifications (events): { "jsonrpc": "2.0", "method": "...", "params": {...} } (no id)

21.4 Commands

21.4.1 initialize

Initialize the RPC session.

// Request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "cwd": "/path/to/project",
    "model": "claude-sonnet-4-20250514",
    "provider": "anthropic",
    "apiKey": "sk-...",
    "thinkingLevel": "medium",
    "tools": ["read", "write", "edit", "bash"]
  }
}

// Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "sessionId": "550e8400-e29b-41d4-a716-446655440000",
    "sessionPath": "~/.pi/agent/sessions/.../550e8400.jsonl",
    "model": "claude-sonnet-4-20250514",
    "provider": "anthropic"
  }
}

21.4.2 prompt

Send a prompt to the model.

// Request
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "prompt",
  "params": {
    "content": "Summarize this codebase"
  }
}

// Response (immediate)
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": { "accepted": true }
}

21.4.3 sendUserMessage

Send a user message programmatically.

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "sendUserMessage",
  "params": {
    "content": "What files did you modify?"
  }
}

21.4.4 abort

Abort the current operation.

{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "abort",
  "params": {}
}

21.4.5 setModel

Change the active model.

{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "setModel",
  "params": {
    "model": "gpt-5"
  }
}

21.4.6 setThinkingLevel

Change the thinking level.

{
  "jsonrpc": "2.0",
  "id": 6,
  "method": "setThinkingLevel",
  "params": {
    "level": "high"
  }
}

21.4.7 setActiveTools

Set the active tool set.

{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "setActiveTools",
  "params": {
    "tools": ["read", "bash"]
  }
}

21.4.8 compact

Trigger context compaction.

{
  "jsonrpc": "2.0",
  "id": 8,
  "method": "compact",
  "params": {
    "prompt": "Focus on database changes"
  }
}

21.4.9 newSession

Start a new session.

{
  "jsonrpc": "2.0",
  "id": 9,
  "method": "newSession",
  "params": {}
}

21.4.10 resumeSession

Resume a previous session.

{
  "jsonrpc": "2.0",
  "id": 10,
  "method": "resumeSession",
  "params": {
    "sessionId": "550e8400-e29b-41d4-a716-446655440000"
  }
}

21.4.11 shutdown

Shut down the RPC session.

{
  "jsonrpc": "2.0",
  "id": 11,
  "method": "shutdown",
  "params": {}
}

21.4.12 getStatus

Get current session status.

// Request
{
  "jsonrpc": "2.0",
  "id": 12,
  "method": "getStatus",
  "params": {}
}

// Response
{
  "jsonrpc": "2.0",
  "id": 12,
  "result": {
    "idle": false,
    "model": "claude-sonnet-4-20250514",
    "thinkingLevel": "medium",
    "contextUsage": 0.45,
    "activeTools": ["read", "write", "edit", "bash"],
    "sessionId": "550e8400-..."
  }
}

21.5 Event Types

Events are notifications sent from Pi to the client without a request ID:

21.5.1 initialized

{
  "jsonrpc": "2.0",
  "method": "initialized",
  "params": {
    "sessionId": "...",
    "model": "...",
    "provider": "..."
  }
}

21.5.2 assistantMessage

Streamed assistant response:

{
  "jsonrpc": "2.0",
  "method": "assistantMessage",
  "params": {
    "type": "delta",
    "delta": "Let me look at "
  }
}
{
  "jsonrpc": "2.0",
  "method": "assistantMessage",
  "params": {
    "type": "complete",
    "content": "Let me look at the files...",
    "usage": { "inputTokens": 1500, "outputTokens": 45 },
    "cost": 0.0023
  }
}

21.5.3 toolCall

{
  "jsonrpc": "2.0",
  "method": "toolCall",
  "params": {
    "toolName": "read",
    "input": { "path": "src/app.ts" },
    "toolCallId": "tc_001"
  }
}

21.5.4 toolResult

{
  "jsonrpc": "2.0",
  "method": "toolResult",
  "params": {
    "toolCallId": "tc_001",
    "output": { "content": [{ "type": "text", "text": "..." }] },
    "details": {}
  }
}

21.5.5 turnEnd

{
  "jsonrpc": "2.0",
  "method": "turnEnd",
  "params": {
    "totalTokens": 3200,
    "totalCost": 0.015
  }
}

21.5.6 error

{
  "jsonrpc": "2.0",
  "method": "error",
  "params": {
    "message": "Rate limit exceeded",
    "code": "rate_limit",
    "retryable": true
  }
}

21.5.7 contextUsage

{
  "jsonrpc": "2.0",
  "method": "contextUsage",
  "params": {
    "used": 45000,
    "total": 200000,
    "percentage": 0.225
  }
}

21.6 Extension UI Protocol

Extensions can send custom UI events through RPC:

{
  "jsonrpc": "2.0",
  "method": "extension:ui",
  "params": {
    "extension": "my-extension",
    "type": "dialog",
    "data": {
      "title": "Confirmation",
      "message": "Allow rm -rf?"
    }
  }
}

The client should respond with:

{
  "jsonrpc": "2.0",
  "method": "extension:response",
  "params": {
    "extension": "my-extension",
    "result": true
  }
}

21.7 Error Handling

Errors follow JSON-RPC 2.0 error format:

{
  "jsonrpc": "2.0",
  "id": 2,
  "error": {
    "code": -32602,
    "message": "Invalid params: 'content' is required",
    "data": { "field": "content" }
  }
}

21.7.1 Standard Error Codes

Code Description
-32700 Parse error (invalid JSON)
-32600 Invalid request
-32601 Method not found
-32602 Invalid params
-32603 Internal error
-32000 Model error
-32001 Tool error
-32002 Rate limit
-32003 Context limit

21.8 Client Examples

21.8.1 Node.js Client

import { spawn } from "node:child_process";

const pi = spawn("pi", ["--mode", "rpc"], { stdio: ["pipe", "pipe", "inherit"] });

let buffer = "";

pi.stdout.on("data", (data) => {
  buffer += data.toString();
  const lines = buffer.split("\n");
  buffer = lines.pop() || "";
  
  for (const line of lines) {
    if (line.trim()) {
      const message = JSON.parse(line);
      handleMessage(message);
    }
  }
});

function send(method: string, params: object, id?: number) {
  const msg = { jsonrpc: "2.0", method, params, ...(id ? { id } : {}) };
  pi.stdin.write(JSON.stringify(msg) + "\n");
}

function handleMessage(message: any) {
  if (message.method === "assistantMessage") {
    process.stdout.write(message.params.delta || "");
  } else if (message.method === "turnEnd") {
    console.log("\n--- Turn complete ---");
  }
}

// Initialize
send("initialize", {
  cwd: process.cwd(),
  model: "claude-sonnet-4",
}, 1);

// Send prompt after initialization
setTimeout(() => {
  send("prompt", { content: "List files in the current directory" }, 2);
}, 1000);

21.8.2 Python Client

import subprocess
import json

def send(proc, method, params, msg_id=None):
    msg = {"jsonrpc": "2.0", "method": method, "params": params}
    if msg_id is not None:
        msg["id"] = msg_id
    proc.stdin.write(json.dumps(msg) + "\n")
    proc.stdin.flush()

def read_message(proc):
    line = proc.stdout.readline()
    return json.loads(line)

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

# Initialize
send(pi, "initialize", {"cwd": ".", "model": "claude-sonnet-4"}, 1)
resp = read_message(pi)
print("Initialized:", resp)

# Send prompt
send(pi, "prompt", {"content": "Hello!"}, 2)

# Read events
while True:
    msg = read_message(pi)
    if msg.get("method") == "assistantMessage":
        params = msg["params"]
        if params.get("type") == "delta":
            print(params["delta"], end="", flush=True)
        elif params.get("type") == "complete":
            print("\n[Complete]")
    elif msg.get("method") == "turnEnd":
        break
Tip

RPC mode is ideal for building IDE integrations, web interfaces, or any application that needs fine-grained control over Pi’s behavior.