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 itstextargument verbatim. Performs schema validation (missing or non-stringtextis 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"}'
# → hiEvery 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.
- CLI —
tools listandtools call.