subagents: fold hive-extra-mcp into hive-agent-sock per mara's rescoping
This commit is contained in:
parent
670e0ccad3
commit
349f7096ba
12 changed files with 75 additions and 81 deletions
17
CLAUDE.md
17
CLAUDE.md
|
|
@ -123,15 +123,18 @@ hand-maintained per-file tree drifts out of sync with the code.
|
||||||
+ manager socket (`/run/hive/mcp.sock`), the protocol an agent's harness
|
+ manager socket (`/run/hive/mcp.sock`), the protocol an agent's harness
|
||||||
speaks to `hive-c0re`. Same split rationale as the two above; the shared
|
speaks to `hive-c0re`. Same split rationale as the two above; the shared
|
||||||
payload types it references stay in `hive-sh4re`.
|
payload types it references stay in `hive-sh4re`.
|
||||||
- **`hive-agent-sock/`** — wire types for the *in-agent* socket, served by
|
- **`hive-agent-sock/`** — what's common across the in-container "agent
|
||||||
the harness to in-container producers — matrix / bash MCP daemons and
|
plugins" (matrix/bash/subagent MCP daemons, `forge_notify`, any
|
||||||
`forge_notify` are the built-in ones, but any user-configured MCP server
|
user-configured MCP server). Wire types for the *in-agent* socket, served
|
||||||
can push todos here too, nothing restricts the `subsystem` set. Carries
|
by the harness to those producers — nothing restricts the `subsystem` set
|
||||||
the loose-ends-v2 **todo** ops plus
|
on the todo ops, any producer can push its own. Carries the loose-ends-v2
|
||||||
harness-local reminders. ⚠️ Distinct from
|
**todo** ops plus harness-local reminders. ⚠️ Distinct from
|
||||||
`hive-core-agent-sock` above: **this socket never leaves the container**
|
`hive-core-agent-sock` above: **this socket never leaves the container**
|
||||||
and `hive-c0re` is not in the path at all — no broker round-trip, no
|
and `hive-c0re` is not in the path at all — no broker round-trip, no
|
||||||
long-poll, no marker files.
|
long-poll, no marker files. Also carries `extra_mcp`: the
|
||||||
|
`hyperhive.extraMcpServers` spec + parsing, shared by `hive-agent` and
|
||||||
|
`hive-subagent-mcp` — not a socket protocol, just the natural home since
|
||||||
|
both already depend on this crate.
|
||||||
- **`hive-types/`** — zero-dependency (bar serde) leaf crate holding the
|
- **`hive-types/`** — zero-dependency (bar serde) leaf crate holding the
|
||||||
foundational newtypes, chiefly `Ident` (1–63 chars of `[a-z0-9-]`,
|
foundational newtypes, chiefly `Ident` (1–63 chars of `[a-z0-9-]`,
|
||||||
constructed only via the validating parser). Lets every wire-type crate
|
constructed only via the validating parser). Lets every wire-type crate
|
||||||
|
|
|
||||||
13
Cargo.lock
generated
13
Cargo.lock
generated
|
|
@ -1654,7 +1654,6 @@ dependencies = [
|
||||||
"hive-agent-sock",
|
"hive-agent-sock",
|
||||||
"hive-claude",
|
"hive-claude",
|
||||||
"hive-core-agent-sock",
|
"hive-core-agent-sock",
|
||||||
"hive-extra-mcp",
|
|
||||||
"hive-sh4re",
|
"hive-sh4re",
|
||||||
"hive-sock-client",
|
"hive-sock-client",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
|
|
@ -1706,6 +1705,8 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hive-sh4re",
|
"hive-sh4re",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1806,15 +1807,6 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hive-extra-mcp"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hive-forge"
|
name = "hive-forge"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
@ -2002,7 +1994,6 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"hive-agent-sock",
|
"hive-agent-sock",
|
||||||
"hive-claude",
|
"hive-claude",
|
||||||
"hive-extra-mcp",
|
|
||||||
"hive-sock-client",
|
"hive-sock-client",
|
||||||
"hive-types",
|
"hive-types",
|
||||||
"rmcp",
|
"rmcp",
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ members = [
|
||||||
"hive-core-agent-sock",
|
"hive-core-agent-sock",
|
||||||
"hive-bash-mcp",
|
"hive-bash-mcp",
|
||||||
"hive-c0re",
|
"hive-c0re",
|
||||||
"hive-extra-mcp",
|
|
||||||
"hive-screen-mcp",
|
"hive-screen-mcp",
|
||||||
"hive-forge",
|
"hive-forge",
|
||||||
"hive-forge-notify",
|
"hive-forge-notify",
|
||||||
|
|
@ -86,7 +85,6 @@ hive-jobq = { path = "hive-jobq" }
|
||||||
hive-jobq-metrics = { path = "hive-jobq-metrics" }
|
hive-jobq-metrics = { path = "hive-jobq-metrics" }
|
||||||
hive-jobq-wire = { path = "hive-jobq-wire" }
|
hive-jobq-wire = { path = "hive-jobq-wire" }
|
||||||
hive-core-agent-sock = { path = "hive-core-agent-sock" }
|
hive-core-agent-sock = { path = "hive-core-agent-sock" }
|
||||||
hive-extra-mcp = { path = "hive-extra-mcp" }
|
|
||||||
hive-claude = "0.1.1"
|
hive-claude = "0.1.1"
|
||||||
hive-host-sock = { path = "hive-host-sock" }
|
hive-host-sock = { path = "hive-host-sock" }
|
||||||
hive-priv-sock = { path = "hive-priv-sock" }
|
hive-priv-sock = { path = "hive-priv-sock" }
|
||||||
|
|
|
||||||
|
|
@ -9,4 +9,6 @@ workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
tracing.workspace = true
|
||||||
hive-sh4re.workspace = true
|
hive-sh4re.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
# hive-agent-sock
|
# hive-agent-sock
|
||||||
|
|
||||||
Wire types for the **in-agent socket** — the one the `hive-agent` harness serves
|
What's common across the in-container "agent plugins" — the matrix / bash /
|
||||||
_inside_ the container to its local producers. The matrix / bash MCP daemons
|
subagent MCP daemons and `forge_notify` today, plus any user-configured MCP
|
||||||
and `forge_notify` are the _built-in_ producers that ship today, but any
|
server declared in an agent's `agent.nix`. Two things live here:
|
||||||
user-configured MCP server declared in an agent's `agent.nix` can dial this
|
|
||||||
socket and push its own todos with an arbitrary `subsystem` marker — the
|
|
||||||
wire format doesn't restrict the set. Unlike the host-served sockets, this
|
|
||||||
one **never leaves the container**.
|
|
||||||
|
|
||||||
## What it carries
|
## Wire types for the in-agent socket
|
||||||
|
|
||||||
|
The socket the `hive-agent` harness serves _inside_ the container to those
|
||||||
|
local producers. Any producer can dial it and push its own todos with an
|
||||||
|
arbitrary `subsystem` marker — the wire format doesn't restrict the set.
|
||||||
|
Unlike the host-served sockets, this one **never leaves the container**.
|
||||||
|
|
||||||
- the loose-ends-v2 **todo** op family
|
- the loose-ends-v2 **todo** op family
|
||||||
- the harness-local **reminder** op family
|
- the harness-local **reminder** op family
|
||||||
|
|
@ -16,14 +17,23 @@ one **never leaves the container**.
|
||||||
More in-agent request families may be added over time — the socket is
|
More in-agent request families may be added over time — the socket is
|
||||||
deliberately named for the agent, not for the todos.
|
deliberately named for the agent, not for the todos.
|
||||||
|
|
||||||
## Why in-container
|
**Why in-container**: the harness owns the todo + reminder stores locally and
|
||||||
|
signals its own turn loop directly, so `hive-c0re` is not in either path: no
|
||||||
|
broker round-trip, no long-poll, no marker files. That locality is the
|
||||||
|
point — it's also what lets these stores travel with the agent for hive
|
||||||
|
portability.
|
||||||
|
|
||||||
The harness owns the todo + reminder stores locally and signals its own turn
|
**Not to be confused with `hive-core-agent-sock`**: that crate is the
|
||||||
loop directly, so `hive-c0re` is not in either path: no broker round-trip, no
|
_host_-served core↔agent protocol on `/run/hive/mcp.sock` (the harness
|
||||||
long-poll, no marker files. That locality is the point — it's also what lets
|
talking out to `hive-c0re`). This socket is purely intra-container.
|
||||||
these stores travel with the agent for hive portability.
|
|
||||||
|
|
||||||
## Not to be confused with `hive-core-agent-sock`
|
## `extra_mcp`: the `hyperhive.extraMcpServers` spec
|
||||||
|
|
||||||
That crate is the _host_-served core↔agent protocol on `/run/hive/mcp.sock`
|
Not a socket protocol — a shared config type. Parses
|
||||||
(the harness talking out to `hive-c0re`). This socket is purely intra-container.
|
`/etc/hyperhive/extra-mcp.json` (the nix-rendered `hyperhive.extraMcpServers`
|
||||||
|
option) and renders each entry into the shape claude expects in a
|
||||||
|
`--mcp-config` blob. Shared by `hive-agent` (the main session's servers) and
|
||||||
|
`hive-subagent-mcp` (the subagent-eligible subset, gated on
|
||||||
|
`availableToSubagents`) — both already depend on this crate for the socket
|
||||||
|
types above, so a new plugin-facing shared type belongs here rather than in
|
||||||
|
a crate of its own.
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
//! Shared spec for `hyperhive.extraMcpServers` entries: the on-disk JSON
|
//! Shared spec for `hyperhive.extraMcpServers` entries: the on-disk JSON
|
||||||
//! shape the nix module (`nix/agent-modules/mcp.nix`) renders to
|
//! shape the nix module (`nix/agent-modules/mcp.nix`) renders to
|
||||||
//! `/etc/hyperhive/extra-mcp.json`, plus the parsing and per-entry
|
//! `/etc/hyperhive/extra-mcp.json`, plus the parsing and per-entry
|
||||||
//! JSON-rendering logic every consumer needs. Split out of `hive-agent`
|
//! JSON-rendering logic every consumer needs. Lives in `hive-agent-sock`
|
||||||
//! (its only consumer until now) so `hive-subagent-mcp` can build its own
|
//! rather than a crate of its own: this crate is already what's common
|
||||||
//! filtered subagent `--mcp-config` without depending on a binary crate that
|
//! across the in-container "agent plugins" (matrix/bash/subagent MCP
|
||||||
//! has no lib target — same rationale as the `*-sock` crate splits
|
//! daemons, `forge_notify`) — the todo-socket wire types above, and now
|
||||||
//! elsewhere in this workspace.
|
//! this — so a new plugin-facing shared type belongs here rather than in
|
||||||
|
//! a fresh single-purpose crate.
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
@ -1,23 +1,31 @@
|
||||||
//! Wire types for the *in-agent* socket, served by the hive-agent harness
|
//! Rescoped beyond its original name: what's common across the in-container
|
||||||
//! to the in-container producers. Matrix / bash MCP daemons and
|
//! "agent plugins" (matrix/bash/subagent MCP daemons, `forge_notify`, and any
|
||||||
//! `forge_notify` are the built-in ones today, but `subsystem` on the todo
|
//! user-configured MCP server) — not just the in-agent socket wire types.
|
||||||
//! ops below is a plain string, not a closed set: any user-configured MCP
|
//!
|
||||||
//! server declared in an agent's `agent.nix` can dial this socket and push
|
//! **Wire types for the *in-agent* socket**, served by the hive-agent harness
|
||||||
//! its own todos the same way. Carries the loose-ends-v2 *todo* op family
|
//! to those producers. `subsystem` on the todo ops below is a plain string,
|
||||||
//! plus the harness-local *reminder* op family; more in-agent request
|
//! not a closed set: any user-configured MCP server declared in an agent's
|
||||||
//! families may be added over time (the socket is deliberately named for
|
//! `agent.nix` can dial this socket and push its own todos the same way.
|
||||||
//! the agent, not the todos).
|
//! Carries the loose-ends-v2 *todo* op family plus the harness-local
|
||||||
|
//! *reminder* op family; more in-agent request families may be added over
|
||||||
|
//! time (the socket is deliberately named for the agent, not the todos).
|
||||||
//!
|
//!
|
||||||
//! Distinct from `hive-core-agent-sock`, the *host*-served core↔agent
|
//! Distinct from `hive-core-agent-sock`, the *host*-served core↔agent
|
||||||
//! protocol on `/run/hive/mcp.sock`: this socket never leaves the
|
//! protocol on `/run/hive/mcp.sock`: this socket never leaves the
|
||||||
//! container. The harness owns the todo + reminder stores locally and
|
//! container. The harness owns the todo + reminder stores locally and
|
||||||
//! signals its own turn loop directly, so hive-c0re is not in either path —
|
//! signals its own turn loop directly, so hive-c0re is not in either path —
|
||||||
//! no broker round-trip, no long-poll, no marker files.
|
//! no broker round-trip, no long-poll, no marker files.
|
||||||
|
//!
|
||||||
|
//! **`extra_mcp`**: the `hyperhive.extraMcpServers` spec + parsing, shared by
|
||||||
|
//! `hive-agent` (the main session's `--mcp-config`) and `hive-subagent-mcp`
|
||||||
|
//! (the subagent-eligible subset) — not a socket protocol at all, but this
|
||||||
|
//! crate is the natural home since both consumers already depend on it.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use hive_sh4re::inbox::LooseEnd;
|
use hive_sh4re::inbox::LooseEnd;
|
||||||
|
|
||||||
|
pub mod extra_mcp;
|
||||||
pub mod paths;
|
pub mod paths;
|
||||||
|
|
||||||
/// In-container path of the harness-served in-agent socket. The harness
|
/// In-container path of the harness-served in-agent socket. The harness
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ clap.workspace = true
|
||||||
hive-claude.workspace = true
|
hive-claude.workspace = true
|
||||||
hive-agent-sock.workspace = true
|
hive-agent-sock.workspace = true
|
||||||
hive-core-agent-sock.workspace = true
|
hive-core-agent-sock.workspace = true
|
||||||
hive-extra-mcp.workspace = true
|
|
||||||
hive-sh4re.workspace = true
|
hive-sh4re.workspace = true
|
||||||
hive-sock-client.workspace = true
|
hive-sock-client.workspace = true
|
||||||
libc.workspace = true
|
libc.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ pub fn allowed_mcp_tools(groups: &[hive_sh4re::permissions::ToolGroup]) -> Vec<S
|
||||||
// pattern list to `mcp__<server>__<pattern>` so claude can call
|
// pattern list to `mcp__<server>__<pattern>` so claude can call
|
||||||
// them without per-tool operator approval. `["*"]` (the default)
|
// them without per-tool operator approval. `["*"]` (the default)
|
||||||
// expands to `mcp__<server>__*` — every tool from that server.
|
// expands to `mcp__<server>__*` — every tool from that server.
|
||||||
for (server, spec) in hive_extra_mcp::load_extra_mcp() {
|
for (server, spec) in hive_agent_sock::extra_mcp::load_extra_mcp() {
|
||||||
if server == SERVER_NAME || !extra_server_enabled(&server, groups) {
|
if server == SERVER_NAME || !extra_server_enabled(&server, groups) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -300,7 +300,7 @@ fn build_mcp_servers() -> serde_json::Map<String, serde_json::Value> {
|
||||||
// agent isn't entitled to must not even appear in the MCP config, or the
|
// agent isn't entitled to must not even appear in the MCP config, or the
|
||||||
// agent could call it directly (there is no later enforcement point).
|
// agent could call it directly (there is no later enforcement point).
|
||||||
let groups = effective_tool_groups();
|
let groups = effective_tool_groups();
|
||||||
for (name, spec) in hive_extra_mcp::load_extra_mcp() {
|
for (name, spec) in hive_agent_sock::extra_mcp::load_extra_mcp() {
|
||||||
if name == SERVER_NAME {
|
if name == SERVER_NAME {
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
"extra MCP server name `{SERVER_NAME}` collides with the built-in surface; ignoring",
|
"extra MCP server name `{SERVER_NAME}` collides with the built-in surface; ignoring",
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "hive-extra-mcp"
|
|
||||||
edition.workspace = true
|
|
||||||
version.workspace = true
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
serde.workspace = true
|
|
||||||
serde_json.workspace = true
|
|
||||||
tracing.workspace = true
|
|
||||||
|
|
||||||
# Shared spec for `hyperhive.extraMcpServers` — see src/lib.rs. Split out of
|
|
||||||
# `hive-agent` (its only consumer until now) so `hive-subagent-mcp` can build
|
|
||||||
# its own filtered subagent `--mcp-config` without depending on a binary
|
|
||||||
# crate that has no lib target — same rationale as the `*-sock` crate splits
|
|
||||||
# elsewhere in this workspace.
|
|
||||||
|
|
@ -13,7 +13,6 @@ axum.workspace = true
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
hive-agent-sock.workspace = true
|
hive-agent-sock.workspace = true
|
||||||
hive-claude.workspace = true
|
hive-claude.workspace = true
|
||||||
hive-extra-mcp.workspace = true
|
|
||||||
hive-sock-client.workspace = true
|
hive-sock-client.workspace = true
|
||||||
hive-types.workspace = true
|
hive-types.workspace = true
|
||||||
rmcp.workspace = true
|
rmcp.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
//! Builds a subagent's own filtered `--mcp-config`: only
|
//! Builds a subagent's own filtered `--mcp-config`: only
|
||||||
//! `hyperhive.extraMcpServers` entries with `availableToSubagents = true`
|
//! `hyperhive.extraMcpServers` entries with `availableToSubagents = true`
|
||||||
//! (see `hive_extra_mcp::ExtraMcpServer::available_to_subagents`) ever reach
|
//! (see `hive_agent_sock::extra_mcp::ExtraMcpServer::available_to_subagents`) ever reach
|
||||||
//! a subagent's claude invocation. Everything else — the built-in hyperhive
|
//! a subagent's claude invocation. Everything else — the built-in hyperhive
|
||||||
//! surface (todos/messaging), the automatically injected `bash` and `subagent`
|
//! surface (todos/messaging), the automatically injected `bash` and `subagent`
|
||||||
//! entries — stays unreachable by construction: none of those default to
|
//! entries — stays unreachable by construction: none of those default to
|
||||||
|
|
@ -24,11 +24,12 @@ const CONFIG_FILE: &str = "subagent-mcp-config.json";
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn build() -> Option<PathBuf> {
|
pub fn build() -> Option<PathBuf> {
|
||||||
let state_dir = crate::paths::state_dir();
|
let state_dir = crate::paths::state_dir();
|
||||||
let servers: serde_json::Map<String, serde_json::Value> = hive_extra_mcp::load_extra_mcp()
|
let servers: serde_json::Map<String, serde_json::Value> =
|
||||||
.into_iter()
|
hive_agent_sock::extra_mcp::load_extra_mcp()
|
||||||
.filter(|(_, spec)| spec.available_to_subagents())
|
.into_iter()
|
||||||
.map(|(name, spec)| (name, spec.to_json_entry(&state_dir)))
|
.filter(|(_, spec)| spec.available_to_subagents())
|
||||||
.collect();
|
.map(|(name, spec)| (name, spec.to_json_entry(&state_dir)))
|
||||||
|
.collect();
|
||||||
if servers.is_empty() {
|
if servers.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue