feat(#2659): serve hive-matrix-mcp over persistent streamable-http, drop stdio bridge
This commit is contained in:
parent
ae8d1aaac4
commit
a66b7ab298
21 changed files with 411 additions and 856 deletions
|
|
@ -1,7 +1,8 @@
|
|||
# Per-agent matrix integration: the `hyperhive.matrix.*` +
|
||||
# `hyperhive.matrixAccounts` options, the long-running
|
||||
# hive-matrix-daemon, its token-arrival path trigger, and the
|
||||
# auto-injected stdio MCP bridge entry.
|
||||
# hive-matrix-daemon (serves its MCP tools directly over
|
||||
# streamable-http), its token-arrival path trigger, and the
|
||||
# auto-injected extraMcpServers entry.
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
|
|
@ -31,7 +32,7 @@ in
|
|||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Enable per-agent matrix integration via `hive-matrix-mcp`.
|
||||
Enable per-agent matrix integration via `hive-matrix-daemon`.
|
||||
When true (the default), the harness:
|
||||
|
||||
- runs `hive-matrix-daemon` as a systemd unit that holds a
|
||||
|
|
@ -45,9 +46,10 @@ in
|
|||
- exposes the matrix tool surface (send_message, send_dm,
|
||||
send_reaction, send_reply, mark_read, list_rooms,
|
||||
list_room_members, read_room) to claude via an auto-injected
|
||||
`extraMcpServers.matrix` entry. Claude spawns the stdio
|
||||
`hive-matrix-mcp` bridge per turn, which forwards each tool
|
||||
call to the daemon over `/run/hive-matrix/socket`.
|
||||
`extraMcpServers.matrix` entry pointed at the daemon's own
|
||||
streamable-http listener (`hyperhive.mcp.matrixHttpPort`) — no
|
||||
stdio bridge, no per-turn respawn, same shape as the built-in
|
||||
hyperhive surface and `hive-bash-daemon`.
|
||||
- wakes the agent on incoming room events via a short teaser
|
||||
Wake signal (`[matrix] <sender> in <room>: <first 100c>…`)
|
||||
to the hyperhive control socket; the full event stays
|
||||
|
|
@ -146,6 +148,24 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
options.hyperhive.mcp.matrixHttpPort = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 8792;
|
||||
example = 8793;
|
||||
description = ''
|
||||
Loopback port `hive-matrix-daemon` serves its MCP tools
|
||||
(`send_message`, `list_rooms`, `read_room`, …) on. Same shape as
|
||||
`hyperhive.mcp.bashHttpPort`: HTTP is the *sole* transport (no
|
||||
stdio bridge — the daemon that owns the matrix-sdk `Client`
|
||||
registry serves the MCP tools directly in-process),
|
||||
`Restart = "always"` keeps the listener self-healing, and
|
||||
loopback-only binding means no auth token is needed (same
|
||||
`allowed_hosts` reasoning as `hyperhive.mcp.httpPort`). Safe as a
|
||||
single fixed default across all agents (private per-container
|
||||
network namespace — see docs/network.md).
|
||||
'';
|
||||
};
|
||||
|
||||
config = {
|
||||
assertions = [
|
||||
# Extra matrix accounts only make sense alongside the hive-internal
|
||||
|
|
@ -190,35 +210,30 @@ in
|
|||
}
|
||||
];
|
||||
|
||||
# Auto-inject the matrix stdio MCP bridge alongside the bash entry
|
||||
# from ./mcp.nix. `lib.mkDefault` so the operator's own agent.nix
|
||||
# can override the entry.
|
||||
# Auto-inject the matrix MCP entry alongside the bash entry from
|
||||
# ./mcp.nix. `lib.mkDefault` so the operator's own agent.nix can
|
||||
# override it. Points at the daemon's own persistent
|
||||
# streamable-http listener — no stdio bridge, no per-turn spawn.
|
||||
hyperhive.extraMcpServers = lib.mkIf config.hyperhive.matrix.enable {
|
||||
matrix = lib.mkDefault {
|
||||
command = "${config.hyperhive.packages.hive-matrix-mcp}/bin/hive-matrix-mcp";
|
||||
args = [ ];
|
||||
# Same socket path the hive-matrix-daemon service binds
|
||||
# via its `RuntimeDirectory = "hive-matrix"`. Keeps the
|
||||
# bridge + daemon in sync without baking the path into
|
||||
# the Rust default — the env override wins for both.
|
||||
env.HIVE_MATRIX_SOCKET = "/run/hive-matrix/socket";
|
||||
type = "http";
|
||||
url = "http://127.0.0.1:${toString config.hyperhive.mcp.matrixHttpPort}/mcp";
|
||||
allowedTools = [ "*" ];
|
||||
};
|
||||
};
|
||||
|
||||
# Long-running matrix-sdk client + sync per agent. Holds the unix
|
||||
# socket the stdio `hive-matrix-mcp` bridge connects to + emits
|
||||
# hyperhive wake signals on incoming room events via
|
||||
# `/run/hive/mcp.sock`. See
|
||||
# Long-running matrix-sdk client + sync per agent. Serves the MCP
|
||||
# tools directly over streamable-http + emits hyperhive wake
|
||||
# signals on incoming room events via `/run/hive/mcp.sock`. See
|
||||
# `docs/persistence.md::Matrix per-agent daemon + token-arrival
|
||||
# trigger` for the socket-path / first-boot-ordering rationale.
|
||||
# trigger` for the first-boot-ordering rationale.
|
||||
systemd.services.hive-matrix-daemon = lib.mkIf config.hyperhive.matrix.enable {
|
||||
description = "long-running matrix-sdk Client + MCP daemon socket";
|
||||
description = "long-running matrix-sdk Client + MCP daemon";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
before = [ "hive-agent.service" ];
|
||||
after = [ "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
environment = {
|
||||
HIVE_MATRIX_SOCKET = "/run/hive-matrix/socket";
|
||||
# In-agent todo socket the harness serves (loose-ends v2): the
|
||||
# matrix sweep pushes unread-room + pending-invite todos here
|
||||
# instead of firing wakes at hive-c0re's mcp.sock.
|
||||
|
|
@ -263,25 +278,21 @@ in
|
|||
HIVE_ICON_PNG = "${iconPng}";
|
||||
};
|
||||
serviceConfig = {
|
||||
ExecStart = "${config.hyperhive.packages.hive-matrix-daemon}/bin/hive-matrix-daemon";
|
||||
ExecStart = "${config.hyperhive.packages.hive-matrix-daemon}/bin/hive-matrix-daemon --http 127.0.0.1:${toString config.hyperhive.mcp.matrixHttpPort}";
|
||||
SyslogIdentifier = "hive-matrix-daemon";
|
||||
# `on-failure`, not `always`: the daemon deliberately exits 0
|
||||
# (a clean, non-failure exit) when no token is provisioned yet
|
||||
# (see the module doc above) — the `systemd.paths` watcher
|
||||
# below re-fires it the moment hive-c0re provisions one,
|
||||
# instead of `always` busy-looping every `RestartSec` until
|
||||
# then. Once a token exists this is no different from
|
||||
# `hive-bash-daemon`'s reasoning (a down window loses the MCP
|
||||
# tools with no stdio fallback) — a genuine crash is a
|
||||
# non-zero exit, which `on-failure` already restarts.
|
||||
Restart = "on-failure";
|
||||
RestartSec = 5;
|
||||
User = userName;
|
||||
Group = userName;
|
||||
RuntimeDirectory = "hive-matrix";
|
||||
# Keep /run/hive-matrix across restarts. With the default
|
||||
# `RuntimeDirectoryPreserve=no`, a `switch-to-configuration`
|
||||
# restart races the outgoing instance's stop-time cleanup
|
||||
# (which deletes the dir) against the incoming instance's
|
||||
# start (which creates it + binds the socket inside it). The
|
||||
# cleanup can win and delete the dir out from under the fresh
|
||||
# daemon, which then fails to mkdir under root-owned /run and
|
||||
# exits — looping on Restart=on-failure until the next boot.
|
||||
# `yes` stops systemd removing it on stop; it still creates it
|
||||
# on first start, and it lives on tmpfs so it's gone at
|
||||
# container reboot regardless. See hive-bash-daemon (./mcp.nix).
|
||||
RuntimeDirectoryPreserve = "yes";
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
hyperhive package outputs consumed by the harness modules: the
|
||||
per-binary daemon/CLI packages (`hive-agent`, `hive-agent-mcp`,
|
||||
`hive-agent-wake`, `hive-bash-daemon`,
|
||||
`hive-forge`, `hive-matrix-daemon`, `hive-matrix-mcp`,
|
||||
`hive-forge`, `hive-matrix-daemon`,
|
||||
`hive-metric`, `hive-screen-mcp`) plus the `assets`, `frontend` and
|
||||
`reference-docs` trees. Wired by the flake's agent-base/ruth
|
||||
nixosModules to `hyperhive.packages.<system>.*`; override an
|
||||
|
|
|
|||
Loading…
Reference in a new issue