MCP integration

Covenant integrates the Model Context Protocol (MCP) for tool discovery and invocation. The same Tool trait backs native Rust implementations and external MCP servers reached over JSON-RPC; the daemon and its clients list and invoke tools through a single surface.

Wire shapes

Covenant's MCP types match the published MCP shapes verbatim, so a tool catalogue is portable between Covenant and any other MCP-aware host.

ToolSpec {
  name:        "echo",
  description: "Returns the provided text argument verbatim.",
  inputSchema: {
    "type": "object",
    "properties": { "text": { "type": "string" } },
    "required": ["text"],
    "additionalProperties": false
  }
}

ToolCallResult {
  content: [ Content::Text { text: "…" }, Content::Json { value: {…} } ],
  isError: false
}

Native tools

Native tools are Rust types implementing the Tool trait and registered with ToolRegistry at daemon startup. The registry exposes list_specs for catalogue queries and call(name, arguments) for invocation.

Two native tools are bundled with the daemon:

  • echo — returns its text argument verbatim. Performs schema validation (missing or non-string text is rejected). Serves as the reference implementation for argument validation against the registry.
  • clock — no arguments; returns { "epoch_ms": <u64> }. Provides a daemon liveness probe that does not require additional capability grants.

Calling a tool

covenant tools list --json

# Grant the per-tool capability first.
covenant capabilities grant tool.call.echo

# Then call the tool with JSON arguments.
covenant tools call echo --args '{"text":"hi"}'
# → hi

Every tools/call requires the capability tool.call.<name> in the issuer's active set. Capability checks are audited via the same CapabilityCheck event used by agent dispatch; the audit row carries an agent_id of tool:<name> so operators can distinguish tool calls from agent dispatches at a glance.

External MCP servers

External MCP servers extend the catalogue without modifying the daemon. Configure them in ~/.covenant/secrets.toml:

[[mcp.server]]
name    = "filesystem"
command = "npx"
args    = ["-y", "@modelcontextprotocol/server-filesystem", "$HOME/notes"]
env     = { LOG_LEVEL = "info" }

On startup the daemon spawns each configured server, performs the MCP handshake (initialize notifications/initialized tools/list), and merges the discovered tools into the same ToolRegistry the native tools live in. Per-server failures are fail-soft: a server that fails to start or returns an error during handshake is logged and skipped without preventing the daemon from coming up.

Transport

Covenant uses line-delimited JSON-RPC 2.0 over the spawned process's stdin/stdout. Each request carries a stable id; the daemon correlates responses by id and discards unrecognised messages. Child processes are started with kill_on_drop(true), so an unclean daemon exit also terminates the child and prevents orphan tool processes.

Security

External MCP servers execute with the operator's privileges and have access to whatever the daemon's user account can reach. The capability gate (tool.call.<name>) controls invocation through the daemon but cannot constrain server behavior post-invocation. Operators should review external servers before configuration and select the narrowest-scoped server appropriate for the task.

Related

  • Capability tokens — the gate on every tools/call.
  • Audit log — where every capability check and every tool call lands.
  • CLItools list and tools call.