From 392a448656b43fd0d9e3f082195a5e1457d2978b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?m=C3=BCde?= Date: Fri, 15 May 2026 16:35:18 +0200 Subject: [PATCH] =?UTF-8?q?mcp:=20SocketReply=20+=20format=5F{ack,recv,sta?= =?UTF-8?q?tus}=20helpers=20=E2=80=94=20dedupe=20tool=20wrappers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hive-ag3nt/src/mcp.rs | 238 +++++++++++++++++++++++++----------------- 1 file changed, 140 insertions(+), 98 deletions(-) diff --git a/hive-ag3nt/src/mcp.rs b/hive-ag3nt/src/mcp.rs index 6d3a653..c33c319 100644 --- a/hive-ag3nt/src/mcp.rs +++ b/hive-ag3nt/src/mcp.rs @@ -27,6 +27,79 @@ use rmcp::{ use crate::client; +/// Wire-protocol-agnostic view of a hyperhive socket response. Both +/// `AgentResponse` and `ManagerResponse` convert into this so the tool +/// formatters can be shared between `AgentServer` and `ManagerServer`. +#[derive(Debug)] +pub enum SocketReply { + Ok, + Err(String), + Message { from: String, body: String }, + Empty, + Status(u64), +} + +impl From for SocketReply { + fn from(r: hive_sh4re::AgentResponse) -> Self { + match r { + hive_sh4re::AgentResponse::Ok => Self::Ok, + hive_sh4re::AgentResponse::Err { message } => Self::Err(message), + hive_sh4re::AgentResponse::Message { from, body } => Self::Message { from, body }, + hive_sh4re::AgentResponse::Empty => Self::Empty, + hive_sh4re::AgentResponse::Status { unread } => Self::Status(unread), + } + } +} + +impl From for SocketReply { + fn from(r: hive_sh4re::ManagerResponse) -> Self { + match r { + hive_sh4re::ManagerResponse::Ok => Self::Ok, + hive_sh4re::ManagerResponse::Err { message } => Self::Err(message), + hive_sh4re::ManagerResponse::Message { from, body } => Self::Message { from, body }, + hive_sh4re::ManagerResponse::Empty => Self::Empty, + hive_sh4re::ManagerResponse::Status { unread } => Self::Status(unread), + } + } +} + +/// Format helper for "send-like" tools (anything that expects an `Ok`). +/// `tool` and `ok_msg` only appear in the result string; they don't change +/// behavior. +pub fn format_ack( + resp: Result, + tool: &str, + ok_msg: String, +) -> String { + match resp { + Ok(SocketReply::Ok) => ok_msg, + Ok(SocketReply::Err(m)) => format!("{tool} failed: {m}"), + Ok(other) => format!("{tool} unexpected response: {other:?}"), + Err(e) => format!("{tool} transport error: {e:#}"), + } +} + +/// Format helper for `recv` tools: `Message` → from + body block; +/// `Empty` → marker; anything else surfaces as an error. +pub fn format_recv(resp: Result) -> String { + match resp { + Ok(SocketReply::Message { from, body }) => format!("from: {from}\n\n{body}"), + Ok(SocketReply::Empty) => "(empty)".into(), + Ok(SocketReply::Err(m)) => format!("recv failed: {m}"), + Ok(other) => format!("recv unexpected response: {other:?}"), + Err(e) => format!("recv transport error: {e:#}"), + } +} + +/// Format helper for the status peek used in the status line. +pub fn format_status(resp: Result) -> String { + match resp { + Ok(SocketReply::Status(unread)) => format!("{unread} unread message(s) in inbox"), + Ok(other) => format!("status: unexpected response {other:?}"), + Err(e) => format!("status: transport error: {e:#}"), + } +} + /// Common envelope around every MCP tool handler: pre-log → run → append /// a status line → post-log. Free function so both `AgentServer` and /// `ManagerServer` use the same shape; the per-server `status_line` @@ -77,18 +150,13 @@ impl AgentServer { /// note rather than failing the whole tool call when the socket /// hiccups. async fn status_line(&self) -> String { - match client::request::<_, hive_sh4re::AgentResponse>( + let resp = client::request::<_, hive_sh4re::AgentResponse>( &self.socket, &hive_sh4re::AgentRequest::Status, ) .await - { - Ok(hive_sh4re::AgentResponse::Status { unread }) => { - format!("{unread} unread message(s) in inbox") - } - Ok(other) => format!("status: unexpected response {other:?}"), - Err(e) => format!("status: transport error: {e:#}"), - } + .map(SocketReply::from); + format_status(resp) } } @@ -102,16 +170,16 @@ impl AgentServer { let log = format!("{args:?}"); let to = args.to.clone(); run_tool_envelope("send", log, self.status_line(), async move { - let req = hive_sh4re::AgentRequest::Send { - to: args.to, - body: args.body, - }; - match client::request::<_, hive_sh4re::AgentResponse>(&self.socket, &req).await { - Ok(hive_sh4re::AgentResponse::Ok) => format!("sent to {to}"), - Ok(hive_sh4re::AgentResponse::Err { message }) => format!("send failed: {message}"), - Ok(other) => format!("send unexpected response: {other:?}"), - Err(e) => format!("send transport error: {e:#}"), - } + let resp = client::request::<_, hive_sh4re::AgentResponse>( + &self.socket, + &hive_sh4re::AgentRequest::Send { + to: args.to, + body: args.body, + }, + ) + .await + .map(SocketReply::from); + format_ack(resp, "send", format!("sent to {to}")) }) .await } @@ -120,19 +188,15 @@ impl AgentServer { description = "Pop one message from this agent's inbox. Returns the sender and body, \ or an empty marker if nothing is waiting." )] - async fn recv(&self, Parameters(args): Parameters) -> String { - let log = format!("{args:?}"); - run_tool_envelope("recv", log, self.status_line(), async move { - let req = hive_sh4re::AgentRequest::Recv; - match client::request::<_, hive_sh4re::AgentResponse>(&self.socket, &req).await { - Ok(hive_sh4re::AgentResponse::Message { from, body }) => { - format!("from: {from}\n\n{body}") - } - Ok(hive_sh4re::AgentResponse::Empty) => "(empty)".into(), - Ok(hive_sh4re::AgentResponse::Err { message }) => format!("recv failed: {message}"), - Ok(other) => format!("recv unexpected response: {other:?}"), - Err(e) => format!("recv transport error: {e:#}"), - } + async fn recv(&self, Parameters(_args): Parameters) -> String { + run_tool_envelope("recv", String::new(), self.status_line(), async move { + let resp = client::request::<_, hive_sh4re::AgentResponse>( + &self.socket, + &hive_sh4re::AgentRequest::Recv, + ) + .await + .map(SocketReply::from); + format_recv(resp) }) .await } @@ -199,18 +263,21 @@ impl ManagerServer { } async fn status_line(&self) -> String { - match client::request::<_, hive_sh4re::ManagerResponse>( + let resp = client::request::<_, hive_sh4re::ManagerResponse>( &self.socket, &hive_sh4re::ManagerRequest::Status, ) .await - { - Ok(hive_sh4re::ManagerResponse::Status { unread }) => { - format!("{unread} unread message(s) in inbox") - } - Ok(other) => format!("status: unexpected response {other:?}"), - Err(e) => format!("status: transport error: {e:#}"), - } + .map(SocketReply::from); + format_status(resp) + } + + /// Helper: issue any `ManagerRequest`, convert the reply through + /// `SocketReply`. Manager tools that just need an `Ok` ack share this. + async fn dispatch(&self, req: hive_sh4re::ManagerRequest) -> Result { + client::request::<_, hive_sh4re::ManagerResponse>(&self.socket, &req) + .await + .map(SocketReply::from) } } @@ -224,18 +291,13 @@ impl ManagerServer { let log = format!("{args:?}"); let to = args.to.clone(); run_tool_envelope("send", log, self.status_line(), async move { - let req = hive_sh4re::ManagerRequest::Send { - to: args.to, - body: args.body, - }; - match client::request::<_, hive_sh4re::ManagerResponse>(&self.socket, &req).await { - Ok(hive_sh4re::ManagerResponse::Ok) => format!("sent to {to}"), - Ok(hive_sh4re::ManagerResponse::Err { message }) => { - format!("send failed: {message}") - } - Ok(other) => format!("send unexpected response: {other:?}"), - Err(e) => format!("send transport error: {e:#}"), - } + let resp = self + .dispatch(hive_sh4re::ManagerRequest::Send { + to: args.to, + body: args.body, + }) + .await; + format_ack(resp, "send", format!("sent to {to}")) }) .await } @@ -244,21 +306,10 @@ impl ManagerServer { description = "Pop one message from the manager inbox. Returns sender + body, or \ empty." )] - async fn recv(&self, Parameters(args): Parameters) -> String { - let log = format!("{args:?}"); - run_tool_envelope("recv", log, self.status_line(), async move { - let req = hive_sh4re::ManagerRequest::Recv; - match client::request::<_, hive_sh4re::ManagerResponse>(&self.socket, &req).await { - Ok(hive_sh4re::ManagerResponse::Message { from, body }) => { - format!("from: {from}\n\n{body}") - } - Ok(hive_sh4re::ManagerResponse::Empty) => "(empty)".into(), - Ok(hive_sh4re::ManagerResponse::Err { message }) => { - format!("recv failed: {message}") - } - Ok(other) => format!("recv unexpected response: {other:?}"), - Err(e) => format!("recv transport error: {e:#}"), - } + async fn recv(&self, Parameters(_args): Parameters) -> String { + run_tool_envelope("recv", String::new(), self.status_line(), async move { + let resp = self.dispatch(hive_sh4re::ManagerRequest::Recv).await; + format_recv(resp) }) .await } @@ -271,15 +322,14 @@ impl ManagerServer { let log = format!("{args:?}"); let name = args.name.clone(); run_tool_envelope("request_spawn", log, self.status_line(), async move { - let req = hive_sh4re::ManagerRequest::RequestSpawn { name: args.name }; - match client::request::<_, hive_sh4re::ManagerResponse>(&self.socket, &req).await { - Ok(hive_sh4re::ManagerResponse::Ok) => format!("spawn approval queued for {name}"), - Ok(hive_sh4re::ManagerResponse::Err { message }) => { - format!("request_spawn failed: {message}") - } - Ok(other) => format!("request_spawn unexpected response: {other:?}"), - Err(e) => format!("request_spawn transport error: {e:#}"), - } + let resp = self + .dispatch(hive_sh4re::ManagerRequest::RequestSpawn { name: args.name }) + .await; + format_ack( + resp, + "request_spawn", + format!("spawn approval queued for {name}"), + ) }) .await } @@ -292,15 +342,10 @@ impl ManagerServer { let log = format!("{args:?}"); let name = args.name.clone(); run_tool_envelope("kill", log, self.status_line(), async move { - let req = hive_sh4re::ManagerRequest::Kill { name: args.name }; - match client::request::<_, hive_sh4re::ManagerResponse>(&self.socket, &req).await { - Ok(hive_sh4re::ManagerResponse::Ok) => format!("killed {name}"), - Ok(hive_sh4re::ManagerResponse::Err { message }) => { - format!("kill failed: {message}") - } - Ok(other) => format!("kill unexpected response: {other:?}"), - Err(e) => format!("kill transport error: {e:#}"), - } + let resp = self + .dispatch(hive_sh4re::ManagerRequest::Kill { name: args.name }) + .await; + format_ack(resp, "kill", format!("killed {name}")) }) .await } @@ -322,20 +367,17 @@ impl ManagerServer { log, self.status_line(), async move { - let req = hive_sh4re::ManagerRequest::RequestApplyCommit { - agent: args.agent, - commit_ref: args.commit_ref, - }; - match client::request::<_, hive_sh4re::ManagerResponse>(&self.socket, &req).await { - Ok(hive_sh4re::ManagerResponse::Ok) => { - format!("apply approval queued for {agent} @ {commit_ref}") - } - Ok(hive_sh4re::ManagerResponse::Err { message }) => { - format!("request_apply_commit failed: {message}") - } - Ok(other) => format!("request_apply_commit unexpected response: {other:?}"), - Err(e) => format!("request_apply_commit transport error: {e:#}"), - } + let resp = self + .dispatch(hive_sh4re::ManagerRequest::RequestApplyCommit { + agent: args.agent, + commit_ref: args.commit_ref, + }) + .await; + format_ack( + resp, + "request_apply_commit", + format!("apply approval queued for {agent} @ {commit_ref}"), + ) }, ) .await