From 7261992fdaab838b6b790cdd204236602d8db86d Mon Sep 17 00:00:00 2001 From: damocles Date: Thu, 4 Jun 2026 00:14:02 +0200 Subject: [PATCH] =?UTF-8?q?remove=20flavor=20from=20AgentServer=20?= =?UTF-8?q?=E2=80=94=20dumb=20dispatcher,=20tool=20groups=20gate=20access?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hive-ag3nt/src/mcp.rs | 74 ++++++++----------------------------------- 1 file changed, 14 insertions(+), 60 deletions(-) diff --git a/hive-ag3nt/src/mcp.rs b/hive-ag3nt/src/mcp.rs index 85b6a40b..b1938e24 100644 --- a/hive-ag3nt/src/mcp.rs +++ b/hive-ag3nt/src/mcp.rs @@ -8,13 +8,11 @@ //! broker-routed protocol. Unaffected by this module. //! - **MCP stdio** owned by this module — what claude actually speaks. //! -//! One `AgentServer { socket, flavor }` struct handles both flavors: -//! - `Flavor::Agent` — restricted send allow-list, no manager-only tools. -//! - `Flavor::Manager` — manager-only tools unlocked via `require_manager()`. +//! One `AgentServer { socket }` struct shared by agent and manager roles. //! `ManagerServer` is a backward-compat type alias for `AgentServer`. -//! -//! All tools live in one `#[tool_router] impl AgentServer`. Both flavors go -//! through the same `run_tool_envelope` helper so logging stays uniform. +//! Tool access is gated upstream by `--allowedTools` (derived from the +//! agent's `ToolGroup` config); the server itself is a dumb dispatcher. +//! All tools go through the same `run_tool_envelope` helper. use std::future::Future; use std::path::PathBuf; @@ -578,12 +576,9 @@ pub struct RemindArgs { /// `dispatch` call covers both sockets — the only real difference is which /// socket path is used and which tools the flavor enables. /// -/// Manager-only tools return a clear error when called from an agent context. -/// In practice the `--allowedTools` gate prevents that — this is belt-and-suspenders. #[derive(Debug, Clone)] pub struct AgentServer { socket: PathBuf, - flavor: Flavor, } /// Backward-compat alias — callers that reference `ManagerServer` by name still compile. @@ -591,8 +586,8 @@ pub type ManagerServer = AgentServer; impl AgentServer { #[must_use] - pub fn new(socket: PathBuf, flavor: Flavor) -> Self { - Self { socket, flavor } + pub fn new(socket: PathBuf) -> Self { + Self { socket } } /// Issue any `Request` through the retry-aware client and pull @@ -610,16 +605,6 @@ impl AgentServer { Err(e) => (Err(e), 0), } } - - /// Returns an error string when called from a non-manager flavor. - /// Manager-only tool methods call this first and early-return on Some. - fn require_manager(&self) -> Option { - if !matches!(self.flavor, Flavor::Manager) { - Some("error: this tool is only available to manager agents".to_owned()) - } else { - None - } - } } // IMPORTANT: when adding a new `#[tool]` fn to this impl, also add @@ -635,12 +620,10 @@ impl AgentServer { async fn send(&self, Parameters(args): Parameters) -> String { let log = format!("{args:?}"); let to = args.to.clone(); - // Agent flavor: check per-agent allow-list (hyperhive.allowedRecipients). - // Manager flavor: unrestricted send. - if matches!(self.flavor, Flavor::Agent) { - if let Err(refusal) = check_send_allowed(&to) { - return run_tool_envelope("send", log, async move { refusal }).await; - } + // Check per-agent allow-list (hyperhive.allowedRecipients). When no + // policy file is present (e.g. manager containers) the check is a no-op. + if let Err(refusal) = check_send_allowed(&to) { + return run_tool_envelope("send", log, async move { refusal }).await; } run_tool_envelope("send", log, async move { let (resp, retries) = self @@ -1214,11 +1197,6 @@ impl AgentServer { .await } - // ------------------------------------------------------------------------- - // Manager-only tools — guarded by require_manager(). The `--allowedTools` - // gate prevents agents from ever calling these in practice; the guard is - // belt-and-suspenders in case the gate is misconfigured. - // ------------------------------------------------------------------------- #[tool( description = "Fetch recent journal log lines for a sub-agent container. Useful \ @@ -1228,9 +1206,6 @@ impl AgentServer { `lines` defaults to 50 (max capped at 500 on the host side)." )] async fn get_logs(&self, Parameters(args): Parameters) -> String { - if let Some(e) = self.require_manager() { - return e; - } let log = format!("{args:?}"); let agent = args.agent.clone(); run_tool_envelope("get_logs", log, async move { @@ -1270,9 +1245,6 @@ impl AgentServer { &self, Parameters(args): Parameters, ) -> String { - if let Some(e) = self.require_manager() { - return e; - } let log = format!("{args:?}"); run_tool_envelope("request_update_meta_inputs", log, async move { let label = if args.inputs.is_empty() { @@ -1316,9 +1288,6 @@ impl AgentServer { &self, Parameters(args): Parameters, ) -> String { - if let Some(e) = self.require_manager() { - return e; - } let log = format!("{args:?}"); run_tool_envelope("request_schedule_prompt", log, async move { let target_count = args.targets.len(); @@ -1355,9 +1324,6 @@ impl AgentServer { owned by a sub-agent in your subtree per topology.json." )] async fn fire_schedule_now(&self, Parameters(args): Parameters) -> String { - if let Some(e) = self.require_manager() { - return e; - } let log = format!("{args:?}"); run_tool_envelope("fire_schedule_now", log, async move { let id = args.id; @@ -1381,9 +1347,6 @@ impl AgentServer { owner is one of its sub-agents per topology.json. Other owners are refused." )] async fn cancel_schedule(&self, Parameters(args): Parameters) -> String { - if let Some(e) = self.require_manager() { - return e; - } let log = format!("{args:?}"); run_tool_envelope("cancel_schedule", log, async move { let id = args.id; @@ -1416,9 +1379,6 @@ impl AgentServer { Refuses cancelled schedules (the row's terminal — submit a fresh one)." )] async fn edit_schedule(&self, Parameters(args): Parameters) -> String { - if let Some(e) = self.require_manager() { - return e; - } let log = format!("{args:?}"); run_tool_envelope("edit_schedule", log, async move { let id = args.id; @@ -1449,9 +1409,6 @@ impl AgentServer { the swarm is going to be woken up about next." )] async fn list_schedules(&self) -> String { - if let Some(e) = self.require_manager() { - return e; - } run_tool_envelope("list_schedules", String::new(), async move { let (resp, retries) = self .dispatch(hive_sh4re::Request::ListSchedules) @@ -1482,8 +1439,8 @@ impl ServerHandler for AgentServer {} /// /// Returns an error if the MCP server fails to initialize or the transport /// encounters a fatal error. -pub async fn serve_stdio(socket: PathBuf, flavor: Flavor) -> Result<()> { - let server = AgentServer::new(socket, flavor); +pub async fn serve_stdio(socket: PathBuf) -> Result<()> { + let server = AgentServer::new(socket); let service = server.serve(stdio()).await?; service.waiting().await?; Ok(()) @@ -1491,15 +1448,12 @@ pub async fn serve_stdio(socket: PathBuf, flavor: Flavor) -> Result<()> { /// Convenience wrapper: run the agent MCP server over stdio. pub async fn serve_agent_stdio(socket: PathBuf) -> Result<()> { - serve_stdio(socket, Flavor::Agent).await + serve_stdio(socket).await } /// Convenience wrapper: run the manager MCP server over stdio. pub async fn serve_manager_stdio(socket: PathBuf) -> Result<()> { - let server = AgentServer::new(socket, Flavor::Manager); - let service = server.serve(stdio()).await?; - service.waiting().await?; - Ok(()) + serve_stdio(socket).await } // -----------------------------------------------------------------------------