Matrix MCP tools and extra MCP servers
Built-in matrix MCP (mcp__matrix__*)
When hyperhive.matrix.enable = true and the host-level matrix
tuwunel is configured, the harness auto-injects hive-matrix-mcp as
a second stdio MCP server. Tools land as mcp__matrix__<name>:
Messaging
send_message(room, body)— send a markdown message;roomaccepts!id:serveror#alias:server, daemon resolves eithersend_dm(user_id, body)— send a direct message to a matrix usersend_reply(room, event_id, body)— threaded reply to a specific eventsend_reaction(room, event_id, key)— react to a message with an emoji key
Reading
read_room(room, limit?)— recent timeline eventslist_rooms()— enumerate joined rooms ({ id, canonical_alias, name, member_count }per room)list_room_members(room)— members of a room
Room membership
invite_user(room, user_id)— invite@user:serverinto a room you're already in; you must have a high enough power level. The invitee sees a pending invite and resolves it viaresolve_invite.resolve_invite(room, action)— accept or reject a pending invite.actionis"accept"(join the room) or"reject"(decline and leave). This is the path for invites;join_roomis for joining a public room you weren't invited to.join_room(room)— join a public room by id or alias. Also accepts a pending invite if one exists, but preferresolve_invitefor invites (it can reject too).list_invites()— rooms this agent has been invited to but not yet joined ({ id, canonical_alias, name }per room).
Receipts
mark_read(room, event_id)— advance the read receipt
Architecture
The daemon (hive-matrix-daemon) holds the long-running matrix-sdk
Client + sync loop; the stdio bridge (hive-matrix-mcp) is spawned
per turn and forwards tool calls over /run/hive-matrix.sock. Both
silently exit when <state>/matrix-token is absent (account not yet
provisioned).
Incoming room events wake the agent via AgentRequest::Wake with
from: "matrix". The wake body format depends on the unread state:
- Single room, one message:
[matrix] <sender> in <room>: <body> — use read_room to view, mark_read to clear - Single room, multiple messages:
[matrix] N unread in <room> — use read_room to view, mark_read to clear - Multiple rooms:
[matrix] unread messages:followed by a bulleted list (- <room>: <sender>: <body>or- <room>: N unreadper room)
The same per-room breakdown is included in the UnreadMatrix entry
returned by get_loose_ends so unread rooms surface in the
loose-ends list between turns.
Invite wakes: when the daemon's sync loop receives an
m.room.member invite event, it writes the invite to
mcp-loose-ends/matrix.json and fires a hyperhive wake. The daemon
does not auto-join — the agent calls list_invites to see pending
invites and resolve_invite to accept or reject them.
Pending invites as loose ends: pending invites are written to
mcp-loose-ends/matrix.json and appear in get_loose_ends output as
[matrix] pending invite: <room> — use list_invites to see, resolve_invite to accept or reject. The file is updated atomically
after each invite event and after each resolve_invite (or
join_room) call clears the entry.
See docs/matrix.md for the homeserver setup,
provisioning flow, and federation config.
Extra MCP servers (per-agent)
Each agent's NixOS config can declare additional MCP servers via
hyperhive.extraMcpServers.<key> = { command, args, env, allowedTools }. The module writes the map to
/etc/hyperhive/extra-mcp.json; the harness reads it at boot and
merges every entry into --mcp-config (under mcpServers.<key>)
and --allowedTools (as mcp__<key>__<pattern>).
The agent's flake.nix forwards every flake input to agent.nix as
the flakeInputs module arg, so external MCP-server flakes are pulled
in by adding them to inputs.* and referenced as
flakeInputs.<name>.packages.${pkgs.system}.default — the resolved
sha lands in the agent's own flake.lock and rolls up to meta's.
allowedTools defaults to ["*"], which expands to
mcp__<key>__* (every tool from that server auto-approved). Restrict
to specific tool names when you want finer control.