Arcana::MCP
MCP (Model Context Protocol) bridge to a running Arcana server.
Reads JSON-RPC 2.0 from stdin, translates to REST calls against the Arcana server, writes responses to stdout.
Implements the MCP tool server protocol so Claude Code (or any MCP client) gets native tool access to the Arcana bus.
Constants
PROTOCOL_VERSION = "2024-11-05"
TOOLS = [{
name: "arcana_directory",
description: "List or search the Arcana service directory. Returns agents and services registered on the bus.",
inputSchema: {
type: "object",
properties: {
query: {type: "string", description: "Search query (matches name, description, tags)"},
tag: {type: "string", description: "Filter by tag. Providers auto-tag their tool names, so `tag: \"chat\"` finds every entity that offers a chat tool."},
kind: {type: "string", enum: ["agent", "service"], description: "Filter by kind"},
address: {type: "string", description: "Look up a specific address"},
},
},
}, {
name: "arcana_deliver",
description: "Send a message on the Arcana bus. By default (ordering: auto), the bus decides sync vs async based on the target: services get sync (blocks for reply), agents get async (fire and forget, check arcana_receive later). Override with ordering 'sync' or 'async'. The response tells you which mode was used and the correlation_id for tracking.",
inputSchema: {
type: "object",
properties: {
from: {type: "string", description: "Your address on the bus (so replies come back to you)"},
to: {type: "string", description: "Target address on the bus"},
subject: {type: "string", description: "Message subject/intent"},
payload: {description: "Message payload — pass a JSON OBJECT with the fields the target service expects (e.g. {\"text\":\"hi\",\"output_path\":\"/tmp/x.opus\"} for openai:tts). Not a stringified JSON blob — pass the object directly. The server does auto-unwrap stringified JSON as a fallback, but the tool schema wants an object."},
ordering: {type: "string", enum: ["auto", "sync", "async"], description: "Message ordering: auto (default, resolved by target kind), sync (block for reply), or async (fire and forget)"},
timeout_ms: {type: "integer", description: "Timeout in milliseconds for sync ordering (default: 30000)"},
},
required: ["to"],
},
}, {
name: "arcana_publish",
description: "Publish a message to a topic on the Arcana bus. All subscribers receive a copy.",
inputSchema: {
type: "object",
properties: {
topic: {type: "string", description: "Topic to publish to"},
subject: {type: "string", description: "Message subject"},
payload: {description: "Message payload (any JSON value)"},
},
required: ["topic"],
},
}, {
name: "arcana_register",
description: "Register or unregister on the Arcana bus. Default action is 'register' which creates a mailbox and optionally adds a directory listing. Use action 'unregister' to remove your mailbox and listing. Use action 'busy' or 'idle' to update your availability status.",
inputSchema: {
type: "object",
properties: {
address: {type: "string", description: "Your routing address on the bus. Legal shapes: `foo` (bare token, typically a service), `@foo` (agent handle — leading `@` sigil, for conversational identities like a project's Claude agent), or `owner:capability` (two-token colon form for services). Pick something stable; other agents will remember it. `@mj` and `mj` are distinct entities — the sigil lets Claude/Codex register alongside a same-project tool service without colliding."},
action: {type: "string", enum: ["register", "unregister", "busy", "idle"], description: "Action to perform (default: register)"},
token: {type: "string", description: "Secret token to protect your mailbox (optional, you choose it)"},
name: {type: "string", description: "Display name for the directory"},
description: {type: "string", description: "What you do (for the directory)"},
kind: {type: "string", enum: ["agent", "service"], description: "What kind of listing this is. Default: agent. Set to 'service' if you handle requests via a fixed schema."},
guide: {type: "string", description: "How-to guide for interacting with you"},
tags: {type: "array", items: {type: "string"}, description: "Tags for discovery"},
listed: {type: "boolean", description: "Whether to add a directory listing. Default true. Set false for pure consumers that send/subscribe but don't accept addressed messages — they get a mailbox but stay invisible to discovery."},
},
required: ["address"],
},
}, {
name: "arcana_inbox",
description: "List messages in your mailbox WITHOUT consuming them. Returns metadata (correlation_id, from, subject, timestamp) for each message. Use this to see what's waiting, then arcana_receive with an id to selectively consume specific messages.",
inputSchema: {
type: "object",
properties: {
address: {type: "string", description: "Your address on the bus"},
token: {type: "string", description: "Your mailbox token (if set during register)"},
},
required: ["address"],
},
}, {
name: "arcana_receive",
description: "Check your mailbox for incoming messages. Returns an array of envelopes. Use timeout_ms to wait for messages if the mailbox is empty. Use id to selectively receive a specific message (from arcana_inbox) without consuming the rest. Combine id + timeout_ms to block until a specific message arrives.",
inputSchema: {
type: "object",
properties: {
address: {type: "string", description: "Your address on the bus"},
token: {type: "string", description: "Your mailbox token (if set during register)"},
timeout_ms: {type: "integer", description: "How long to wait for a message if mailbox is empty (0 = don't wait, default: 0)"},
id: {type: "string", description: "Correlation ID of a specific message to receive (from arcana_inbox). Only that message is consumed."},
},
required: ["address"],
},
}, {
name: "arcana_expect",
description: "Manage expected response tracking. Use 'check' to see how many outstanding expectations exist, or 'await' to block until all expected responses have arrived.",
inputSchema: {
type: "object",
properties: {
address: {type: "string", description: "Your address on the bus"},
token: {type: "string", description: "Your mailbox token (if set during register)"},
action: {type: "string", enum: ["check", "await"], description: "Action: check (count outstanding) or await (block until all met)"},
timeout_ms: {type: "integer", description: "Timeout for await in milliseconds (default: 30000)"},
},
required: ["address", "action"],
},
}, {
name: "arcana_freeze",
description: "Manage frozen messages. Freeze holds a message out of the receive queue; thaw releases it back. Use 'list' to see frozen messages.",
inputSchema: {
type: "object",
properties: {
address: {type: "string", description: "Mailbox address"},
token: {type: "string", description: "Mailbox token (if set during register)"},
action: {type: "string", enum: ["freeze", "thaw", "thaw_all", "list"], description: "Action to perform"},
id: {type: "string", description: "Correlation ID of the message (required for freeze/thaw)"},
by: {type: "string", description: "Who is freezing the message (optional, for freeze action)"},
},
required: ["address", "action"],
},
}, {
name: "arcana_health",
description: "Check the health of the Arcana server.",
inputSchema: {
type: "object",
properties: {} of String => String,
},
}, {
name: "arcana_events",
description: "Query the Arcana audit event log. Returns an array of events matching the filters (type, subject, since, limit). Useful for debugging message flow, tracking auth failures, and auditing who registered when.",
inputSchema: {
type: "object",
properties: {
type: {type: "string", description: "Filter by event type (e.g. 'message.sent', 'listing.registered', 'auth.failed')"},
subject: {type: "string", description: "Filter by subject address (usually the primary actor of the event)"},
since: {type: "string", description: "RFC3339 timestamp; only events at or after this time are returned"},
limit: {type: "integer", description: "Max events to return (default 100, cap 1000)"},
},
},
}]