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-streamin the request headers. MCP requires both content types to be acceptable, even though CapOwn always responds withapplication/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:
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 |