Skip to main content
The Agent Client Protocol (ACP) is a standardized JSON-RPC protocol for AI agent communication. Goose implements ACP for rich, bidirectional integration with IDEs, desktop applications, and other tools.

Overview

ACP provides:
  • Bidirectional communication: Agents can request permissions and receive cancellations
  • Rich tool call handling: Detailed status updates, locations, and content
  • Session management: Create, load, and resume sessions with full history
  • MCP server integration: Dynamically add MCP servers to sessions
  • Streaming responses: Real-time updates as the agent works

ACP vs REST API

FeatureACP (stdio/WebSocket)REST API (goose-server)
Use caseIDE plugins, desktop apps, embedded agentsWeb apps, simple clients
Transportstdio, WebSocket, HTTPHTTP only
PermissionsInteractive promptsNot supported
Tool locationsFile paths with line numbersNot provided
StreamingJSON-RPC notificationsServer-Sent Events
Session resumeFull conversation historySession ID only
Use ACP for desktop applications and IDE integrations where you need rich tool feedback and permission prompts. Use the REST API for simpler web-based integrations.

Quick Start

Running ACP Server

# Start Goose as an ACP server on stdio
goose acp --with-builtin developer,memory

# Or programmatically
cargo run -p goose-cli -- acp --with-builtin developer
The server communicates via stdin/stdout using JSON-RPC.

Python Client Example

See test_acp_client.py in the source repository for a complete example:
import subprocess
import json

class AcpClient:
    def __init__(self):
        self.process = subprocess.Popen(
            ['goose', 'acp', '--with-builtin', 'developer'],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            text=True
        )
        self.request_id = 0
    
    def send_request(self, method, params=None):
        self.request_id += 1
        request = {
            "jsonrpc": "2.0",
            "method": method,
            "id": self.request_id
        }
        if params:
            request["params"] = params
        
        self.process.stdin.write(json.dumps(request) + "\n")
        self.process.stdin.flush()
        
        response = json.loads(self.process.stdout.readline())
        return response

# Initialize connection
client = AcpClient()
client.send_request("initialize", {
    "protocolVersion": "v1",
    "clientInfo": {"name": "my-client", "version": "1.0.0"}
})

# Create session
session = client.send_request("session/new", {
    "cwd": "/path/to/project"
})

# Send prompt
client.send_request("session/prompt", {
    "sessionId": session["result"]["sessionId"],
    "prompt": [{"type": "text", "text": "List files"}]
})

Protocol Methods

Connection Lifecycle

initialize

Establish connection and exchange capabilities. Request:
{
  "jsonrpc": "2.0",
  "method": "initialize",
  "id": 1,
  "params": {
    "protocolVersion": "v1",
    "clientCapabilities": {},
    "clientInfo": {
      "name": "vscode-goose",
      "version": "1.0.0"
    }
  }
}
Response:
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "v1",
    "agentCapabilities": {
      "loadSession": true,
      "promptCapabilities": {
        "image": true,
        "audio": false,
        "embeddedContext": true
      },
      "mcpCapabilities": {
        "http": true
      }
    },
    "authMethods": [
      {
        "id": "goose-provider",
        "name": "Configure Provider",
        "description": "Run `goose configure` to set up your AI provider"
      }
    ]
  }
}

Session Management

session/new

Create a new session with optional MCP servers. Request:
{
  "jsonrpc": "2.0",
  "method": "session/new",
  "id": 2,
  "params": {
    "cwd": "/home/user/project",
    "mcpServers": [
      {
        "type": "stdio",
        "name": "github",
        "command": "uvx",
        "args": ["github-mcp-server"],
        "env": [{"name": "GITHUB_TOKEN", "value": "ghp_..."}]
      }
    ]
  }
}
Response:
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "sessionId": "550e8400-e29b-41d4-a716-446655440000",
    "models": {
      "current": "claude-sonnet-4-20250514",
      "available": [
        {"id": "claude-sonnet-4-20250514", "name": "Claude Sonnet 4"},
        {"id": "gpt-4o", "name": "GPT-4o"}
      ]
    }
  }
}

session/load

Load an existing session by ID. Request:
{
  "jsonrpc": "2.0",
  "method": "session/load",
  "id": 3,
  "params": {
    "sessionId": "550e8400-e29b-41d4-a716-446655440000",
    "cwd": "/home/user/project"
  }
}
Response: Returns full conversation history as a series of session/notification notifications, followed by the response:
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "models": { /* same as session/new */ }
  }
}

session/prompt

Send a prompt and receive streaming responses. Request:
{
  "jsonrpc": "2.0",
  "method": "session/prompt",
  "id": 4,
  "params": {
    "sessionId": "550e8400-e29b-41d4-a716-446655440000",
    "prompt": [
      {"type": "text", "text": "What files are in this directory?"},
      {"type": "image", "data": "base64...", "mimeType": "image/png"}
    ]
  }
}
Response:
{
  "jsonrpc": "2.0",
  "id": 4,
  "result": {
    "stopReason": "endTurn"
  }
}
The response is sent only after all streaming notifications complete. Use notifications to receive real-time updates.

session/cancel

Cancel an in-progress prompt. Notification:
{
  "jsonrpc": "2.0",
  "method": "session/cancel",
  "params": {
    "sessionId": "550e8400-e29b-41d4-a716-446655440000"
  }
}

Notifications (Agent → Client)

Notifications stream updates during session/prompt.

session/notification - Agent Message

{
  "jsonrpc": "2.0",
  "method": "session/notification",
  "params": {
    "sessionId": "550e8400-e29b-41d4-a716-446655440000",
    "update": {
      "sessionUpdate": "agentMessageChunk",
      "chunk": {
        "content": {"type": "text", "text": "Let me check the files"}
      }
    }
  }
}

session/notification - Tool Call

{
  "jsonrpc": "2.0",
  "method": "session/notification",
  "params": {
    "sessionId": "550e8400-e29b-41d4-a716-446655440000",
    "update": {
      "sessionUpdate": "toolCall",
      "toolCall": {
        "id": "call_abc123",
        "title": "Developer: List Files",
        "status": "pending"
      }
    }
  }
}

session/notification - Tool Call Update

Includes file locations for editor integration:
{
  "jsonrpc": "2.0",
  "method": "session/notification",
  "params": {
    "sessionId": "550e8400-e29b-41d4-a716-446655440000",
    "update": {
      "sessionUpdate": "toolCallUpdate",
      "toolCallUpdate": {
        "id": "call_abc123",
        "fields": {
          "status": "completed",
          "content": [
            {"type": "text", "text": "Found 5 files"}
          ],
          "locations": [
            {"path": "/home/user/project/main.rs", "line": 1}
          ]
        }
      }
    }
  }
}
The locations field enables IDE integrations to highlight or navigate to files modified by tools.

requestPermission - Interactive Approval

For sensitive operations, the agent requests permission:
{
  "jsonrpc": "2.0",
  "method": "requestPermission",
  "id": 100,
  "params": {
    "sessionId": "550e8400-e29b-41d4-a716-446655440000",
    "toolCallUpdate": {
      "id": "call_xyz789",
      "fields": {
        "title": "Execute Command",
        "status": "pending",
        "rawInput": {"command": "rm -rf /"},
        "content": [{"type": "text", "text": "Delete all files?"}]
      }
    },
    "options": [
      {"id": "allow_always", "name": "Allow Always", "kind": "allowAlways"},
      {"id": "allow_once", "name": "Allow Once", "kind": "allowOnce"},
      {"id": "reject_once", "name": "Reject Once", "kind": "rejectOnce"},
      {"id": "reject_always", "name": "Reject Always", "kind": "rejectAlways"}
    ]
  }
}
Client Response:
{
  "jsonrpc": "2.0",
  "id": 100,
  "result": {
    "outcome": {
      "type": "selected",
      "optionId": "allow_once"
    }
  }
}

Custom Methods

Goose extends ACP with custom methods for additional functionality.

_extensions/add

Add an MCP extension to a running session:
{
  "jsonrpc": "2.0",
  "method": "_extensions/add",
  "id": 5,
  "params": {
    "sessionId": "550e8400-e29b-41d4-a716-446655440000",
    "config": {
      "type": "stdio",
      "name": "slack",
      "cmd": "uvx",
      "args": ["mcp-slack"]
    }
  }
}

_session/list

List all sessions:
{
  "jsonrpc": "2.0",
  "method": "_session/list",
  "id": 6
}
Response:
{
  "jsonrpc": "2.0",
  "id": 6,
  "result": {
    "sessions": [
      {
        "sessionId": "550e8400-e29b-41d4-a716-446655440000",
        "title": "My Project",
        "cwd": "/home/user/project",
        "updatedAt": "2026-03-04T12:00:00Z"
      }
    ]
  }
}

Other Custom Methods

  • _session/get: Get full session details
  • _session/delete: Delete a session
  • _session/export: Export session data
  • _session/import: Import session data
  • _extensions/remove: Remove an extension
  • _tools: List available tools
  • _resource/read: Read an MCP resource
  • _working_dir/update: Change session working directory
  • _config/extensions: Get extension configuration

Transport Options

stdio (Default)

Communicate via standard input/output:
goose acp --with-builtin developer
Ideal for:
  • CLI tools
  • Editor plugins (VS Code, Neovim)
  • Process-based integrations

WebSocket (Future)

WebSocket transport for browser-based clients:
goose acp --transport websocket --port 8080

HTTP (Future)

HTTP transport for stateless clients:
goose acp --transport http --port 8080

Implementation Details

Source Code

  • ACP server: crates/goose-acp/src/server.rs
  • CLI integration: crates/goose-cli/src/cli.rs (Command::Acp)
  • Protocol library: sacp crate (vendored)
  • Custom methods: crates/goose-acp/src/custom_requests.rs
  • Test client: test_acp_client.py

Tool Location Extraction

Goose automatically extracts file locations from tool responses for developer tools:
// crates/goose-acp/src/server.rs
fn extract_tool_locations(
    tool_request: &ToolRequest,
    tool_response: &ToolResponse,
) -> Vec<ToolCallLocation> {
    // Parse tool output for file paths and line numbers
    // Returns locations for IDE navigation
}
Supported tools:
  • write: File creation (line 1)
  • edit: File modification (parsed from output)
  • view: File viewing (parsed from output)

Session State Management

ACP sessions are isolated per connection:
struct GooseAcpSession {
    agent: Arc<Agent>,
    messages: Conversation,
    tool_requests: HashMap<String, ToolRequest>,
    cancel_token: Option<CancellationToken>,
}
Sessions accumulate until the transport closes.

Best Practices

Error Handling

Handle JSON-RPC errors gracefully:
response = client.send_request("session/prompt", params)
if "error" in response:
    print(f"Error {response['error']['code']}: {response['error']['message']}")
    # Fallback logic

Notification Buffering

Buffer notifications during streaming:
def collect_response(client, request_id):
    notifications = []
    while True:
        line = client.process.stdout.readline()
        msg = json.loads(line)
        
        if "method" in msg:  # Notification
            notifications.append(msg)
        elif msg.get("id") == request_id:  # Response
            return msg, notifications

Cancellation

Implement cancellation for long-running operations:
import threading

def prompt_with_timeout(client, session_id, prompt, timeout=30):
    def cancel():
        time.sleep(timeout)
        client.send_notification("session/cancel", {"sessionId": session_id})
    
    threading.Thread(target=cancel, daemon=True).start()
    return client.send_request("session/prompt", {...})

Resources

  • ACP Specification: Anthropic Cookbook
  • Goose ACP Implementation: crates/goose-acp/
  • Test Client: test_acp_client.py
  • sacp Library: vendor/sacp/ (Rust ACP implementation)

Build docs developers (and LLMs) love