Skip to content

MCP (Model Context Protocol) Support

CapOwn Master exposes an MCP (Model Context Protocol) endpoint that lets AI agents (Claude Code, Codex, etc.) interact with CapOwn workers directly. The endpoint is served at /mcp using the Streamable HTTP transport (stateless, JSON responses). MCP is an optional northbound interface: the existing REST API, stdlib-only capown CLI, and agent skill remain supported.

Endpoint

POST http://<master-host>:9210/mcp
Content-Type: application/json
Accept: application/json, text/event-stream
Authorization: Bearer <client-token>

Important: The client must send Accept: application/json, text/event-stream in the request headers. MCP requires both content types to be acceptable, even though CapOwn always responds with application/json.

Authentication

MCP uses the same client bearer tokens as the REST API. Admin tokens, worker session tokens, and expired/revoked tokens are rejected. Authentication applies to every MCP operation, including initialization and tools/list.

# Create a client token (via admin API)
curl -X POST https://<master>/api/admin/users/<username>/tokens \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '{"token_type": "client"}'

Then use the returned token value in the Authorization header:

Authorization: Bearer 4638a10accf1...

Origin Validation

When a request includes an Origin header, the Master requires a configured public_url (via MASTER_PUBLIC_URL or [master] public_url) and validates the Origin's scheme, host, and port against it. Malformed, scheme-less, and non-matching Origins are rejected with HTTP 403. If public_url is unset, all requests that supply an Origin are rejected because no browser origin is trusted.

Non-browser MCP clients normally omit Origin; those requests remain allowed and are authenticated with the client bearer token. Configure public_url before using a browser-based MCP client.

Tool Reference

Every tool accepts arguments as a single Pydantic input model passed under the input key:

{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "tools/call",
  "params": {
    "name": "shell_run",
    "arguments": {
      "input": {
        "worker": "wrk_abc123",
        "command": "ls -la"
      }
    }
  }
}

All worker-targeted tools take a worker argument that accepts either the canonical worker_id (e.g. wrk_a1b2c3d4e5f6) or the worker_name (e.g. my-server). The name is only resolved to a worker the authenticated user owns, so there is no ambiguity.

Worker Tools

Tool Description Key args
workers_list List all workers owned by the user (none)
system_info Get OS, hostname, capabilities worker
shell_run Run a command and wait for output worker, command, timeout
shell_dispatch Fire-and-forget command dispatch worker, command, timeout

File Tools

Tool Description Key args
file_list List directory contents worker, path
file_read Read a file worker, path, offset, limit
file_write Write content to a file worker, path, content
file_edit Apply text replacements to a file worker, path, operations
file_find Find files by glob pattern worker, pattern, root
file_search Search file contents by pattern worker, pattern, root, regex

Task Management Tools

Tool Description Key args
task_get Query task status worker, task_id
task_wait Wait for task completion worker, task_id, timeout
task_list List recent tasks on a worker worker
task_cancel Cancel a running task worker, task_id

Response Format

Tools return TextContent blocks with the output of the corresponding operation. Successful structured values are serialized as JSON text. Tool failures return isError: true; the text contains structured CapOwn error details:

{
  "jsonrpc": "2.0",
  "id": "1",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{...output or error JSON...}"
      }
    ],
    "isError": true
  }
}

Error content includes the standard MCP error fields plus CapOwn-specific error codes:

{
  "capown_error_code": "worker_offline",
  "mcp_error_code": -32000,
  "message": "worker offline or not connected"
}

Soft Timeouts

When a shell_run command exceeds its timeout, the tool returns a running status with the task_id so you can poll for the result later:

{
  "status": "running",
  "task_id": "a1b2c3d4e5f6",
  "preview": "partial output so far...",
  "hint": "Task is still running. Use task_get or task_wait to check progress.",
  "next_actions": ["check_later", "wait_longer", "cancel"]
}

Running operational tasks include task_id and MCP follow-up actions. A non-task operation that times out, such as task_list, preserves its own hint and does not advertise an empty task ID.

Health Check

GET /health includes "mcp_enabled": true when the MCP adapter initialized successfully. This reports adapter availability, not authentication or Worker connectivity.

Connection Examples

Claude Code (claude.ai/code)

Add to your ~/.claude/settings.json or workspace .claude/settings.local.json:

{
  "mcpServers": {
    "capown": {
      "transport": "streamable-http",
      "url": "http://localhost:9210/mcp",
      "headers": {
        "Authorization": "Bearer <your-client-token>"
      }
    }
  }
}

Generic MCP Client (Python)

import httpx

response = httpx.post(
    "http://localhost:9210/mcp",
    json={"jsonrpc": "2.0", "id": "1", "method": "tools/list", "params": {}},
    headers={
        "Accept": "application/json, text/event-stream",
        "Authorization": "Bearer <your-client-token>",
    },
)
print(response.json()["result"]["tools"])

The example sends a direct JSON-RPC call for diagnostics. In normal use, let an MCP-capable host manage initialization and calls.

Manual (curl)

curl -X POST http://localhost:9210/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer <token>" \
  -d '{"jsonrpc":"2.0","id":"1","method":"tools/list","params":{}}'

Deploying Behind Nginx

Public reverse-proxy setup for CLI/skill and MCP (path mapping, dual location blocks, public_url vs master_url) lives in the deploy guide:

See Configure Nginx in the Deployment guide. Chinese: switch the site language to 简体中文.

Do not duplicate those snippets here; keep proxy and path policy in one place.

CLI and Skill Compatibility

MCP does not replace the existing Client. Keep using the capown CLI and skills/capown-client/SKILL.md when the agent host does not support MCP, when only short HTTPS requests are reliable, or when a stdlib-only client is preferred. MCP and the REST/CLI path share authentication, ownership checks, task routing, and Worker execution behavior.


Deferred Features

The following are documented as out of scope for the initial MCP implementation:

Feature Reason
OAuth 2.1 / OAuth-based MCP auth The existing token system covers all auth needs; OAuth would require a new token endpoint
Dynamic tools/list_changed All tools are statically registered at startup
MCP Tasks (experimental) Soft timeouts return running + task_id; MCP Tasks spec is not yet stable
Worker protocol changes No changes were made to worker transport - all communication still flows through the Master
Third-party MCP server proxying CapOwn exposes its own tools; hosting external MCP servers is a separate feature