mcp: SocketReply + format_{ack,recv,status} helpers — dedupe tool wrappers
This commit is contained in:
parent
edf42b7e93
commit
392a448656
1 changed files with 140 additions and 98 deletions
|
|
@ -27,6 +27,79 @@ use rmcp::{
|
||||||
|
|
||||||
use crate::client;
|
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<hive_sh4re::AgentResponse> 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<hive_sh4re::ManagerResponse> 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<SocketReply, anyhow::Error>,
|
||||||
|
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<SocketReply, anyhow::Error>) -> 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<SocketReply, anyhow::Error>) -> 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
|
/// Common envelope around every MCP tool handler: pre-log → run → append
|
||||||
/// a status line → post-log. Free function so both `AgentServer` and
|
/// a status line → post-log. Free function so both `AgentServer` and
|
||||||
/// `ManagerServer` use the same shape; the per-server `status_line`
|
/// `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
|
/// note rather than failing the whole tool call when the socket
|
||||||
/// hiccups.
|
/// hiccups.
|
||||||
async fn status_line(&self) -> String {
|
async fn status_line(&self) -> String {
|
||||||
match client::request::<_, hive_sh4re::AgentResponse>(
|
let resp = client::request::<_, hive_sh4re::AgentResponse>(
|
||||||
&self.socket,
|
&self.socket,
|
||||||
&hive_sh4re::AgentRequest::Status,
|
&hive_sh4re::AgentRequest::Status,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
.map(SocketReply::from);
|
||||||
Ok(hive_sh4re::AgentResponse::Status { unread }) => {
|
format_status(resp)
|
||||||
format!("{unread} unread message(s) in inbox")
|
|
||||||
}
|
|
||||||
Ok(other) => format!("status: unexpected response {other:?}"),
|
|
||||||
Err(e) => format!("status: transport error: {e:#}"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,16 +170,16 @@ impl AgentServer {
|
||||||
let log = format!("{args:?}");
|
let log = format!("{args:?}");
|
||||||
let to = args.to.clone();
|
let to = args.to.clone();
|
||||||
run_tool_envelope("send", log, self.status_line(), async move {
|
run_tool_envelope("send", log, self.status_line(), async move {
|
||||||
let req = hive_sh4re::AgentRequest::Send {
|
let resp = client::request::<_, hive_sh4re::AgentResponse>(
|
||||||
|
&self.socket,
|
||||||
|
&hive_sh4re::AgentRequest::Send {
|
||||||
to: args.to,
|
to: args.to,
|
||||||
body: args.body,
|
body: args.body,
|
||||||
};
|
},
|
||||||
match client::request::<_, hive_sh4re::AgentResponse>(&self.socket, &req).await {
|
)
|
||||||
Ok(hive_sh4re::AgentResponse::Ok) => format!("sent to {to}"),
|
.await
|
||||||
Ok(hive_sh4re::AgentResponse::Err { message }) => format!("send failed: {message}"),
|
.map(SocketReply::from);
|
||||||
Ok(other) => format!("send unexpected response: {other:?}"),
|
format_ack(resp, "send", format!("sent to {to}"))
|
||||||
Err(e) => format!("send transport error: {e:#}"),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
@ -120,19 +188,15 @@ impl AgentServer {
|
||||||
description = "Pop one message from this agent's inbox. Returns the sender and body, \
|
description = "Pop one message from this agent's inbox. Returns the sender and body, \
|
||||||
or an empty marker if nothing is waiting."
|
or an empty marker if nothing is waiting."
|
||||||
)]
|
)]
|
||||||
async fn recv(&self, Parameters(args): Parameters<RecvArgs>) -> String {
|
async fn recv(&self, Parameters(_args): Parameters<RecvArgs>) -> String {
|
||||||
let log = format!("{args:?}");
|
run_tool_envelope("recv", String::new(), self.status_line(), async move {
|
||||||
run_tool_envelope("recv", log, self.status_line(), async move {
|
let resp = client::request::<_, hive_sh4re::AgentResponse>(
|
||||||
let req = hive_sh4re::AgentRequest::Recv;
|
&self.socket,
|
||||||
match client::request::<_, hive_sh4re::AgentResponse>(&self.socket, &req).await {
|
&hive_sh4re::AgentRequest::Recv,
|
||||||
Ok(hive_sh4re::AgentResponse::Message { from, body }) => {
|
)
|
||||||
format!("from: {from}\n\n{body}")
|
.await
|
||||||
}
|
.map(SocketReply::from);
|
||||||
Ok(hive_sh4re::AgentResponse::Empty) => "(empty)".into(),
|
format_recv(resp)
|
||||||
Ok(hive_sh4re::AgentResponse::Err { message }) => format!("recv failed: {message}"),
|
|
||||||
Ok(other) => format!("recv unexpected response: {other:?}"),
|
|
||||||
Err(e) => format!("recv transport error: {e:#}"),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
@ -199,18 +263,21 @@ impl ManagerServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn status_line(&self) -> String {
|
async fn status_line(&self) -> String {
|
||||||
match client::request::<_, hive_sh4re::ManagerResponse>(
|
let resp = client::request::<_, hive_sh4re::ManagerResponse>(
|
||||||
&self.socket,
|
&self.socket,
|
||||||
&hive_sh4re::ManagerRequest::Status,
|
&hive_sh4re::ManagerRequest::Status,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
.map(SocketReply::from);
|
||||||
Ok(hive_sh4re::ManagerResponse::Status { unread }) => {
|
format_status(resp)
|
||||||
format!("{unread} unread message(s) in inbox")
|
|
||||||
}
|
|
||||||
Ok(other) => format!("status: unexpected response {other:?}"),
|
|
||||||
Err(e) => format!("status: transport error: {e:#}"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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<SocketReply, anyhow::Error> {
|
||||||
|
client::request::<_, hive_sh4re::ManagerResponse>(&self.socket, &req)
|
||||||
|
.await
|
||||||
|
.map(SocketReply::from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -224,18 +291,13 @@ impl ManagerServer {
|
||||||
let log = format!("{args:?}");
|
let log = format!("{args:?}");
|
||||||
let to = args.to.clone();
|
let to = args.to.clone();
|
||||||
run_tool_envelope("send", log, self.status_line(), async move {
|
run_tool_envelope("send", log, self.status_line(), async move {
|
||||||
let req = hive_sh4re::ManagerRequest::Send {
|
let resp = self
|
||||||
|
.dispatch(hive_sh4re::ManagerRequest::Send {
|
||||||
to: args.to,
|
to: args.to,
|
||||||
body: args.body,
|
body: args.body,
|
||||||
};
|
})
|
||||||
match client::request::<_, hive_sh4re::ManagerResponse>(&self.socket, &req).await {
|
.await;
|
||||||
Ok(hive_sh4re::ManagerResponse::Ok) => format!("sent to {to}"),
|
format_ack(resp, "send", 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:#}"),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
@ -244,21 +306,10 @@ impl ManagerServer {
|
||||||
description = "Pop one message from the manager inbox. Returns sender + body, or \
|
description = "Pop one message from the manager inbox. Returns sender + body, or \
|
||||||
empty."
|
empty."
|
||||||
)]
|
)]
|
||||||
async fn recv(&self, Parameters(args): Parameters<RecvArgs>) -> String {
|
async fn recv(&self, Parameters(_args): Parameters<RecvArgs>) -> String {
|
||||||
let log = format!("{args:?}");
|
run_tool_envelope("recv", String::new(), self.status_line(), async move {
|
||||||
run_tool_envelope("recv", log, self.status_line(), async move {
|
let resp = self.dispatch(hive_sh4re::ManagerRequest::Recv).await;
|
||||||
let req = hive_sh4re::ManagerRequest::Recv;
|
format_recv(resp)
|
||||||
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:#}"),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
@ -271,15 +322,14 @@ impl ManagerServer {
|
||||||
let log = format!("{args:?}");
|
let log = format!("{args:?}");
|
||||||
let name = args.name.clone();
|
let name = args.name.clone();
|
||||||
run_tool_envelope("request_spawn", log, self.status_line(), async move {
|
run_tool_envelope("request_spawn", log, self.status_line(), async move {
|
||||||
let req = hive_sh4re::ManagerRequest::RequestSpawn { name: args.name };
|
let resp = self
|
||||||
match client::request::<_, hive_sh4re::ManagerResponse>(&self.socket, &req).await {
|
.dispatch(hive_sh4re::ManagerRequest::RequestSpawn { name: args.name })
|
||||||
Ok(hive_sh4re::ManagerResponse::Ok) => format!("spawn approval queued for {name}"),
|
.await;
|
||||||
Ok(hive_sh4re::ManagerResponse::Err { message }) => {
|
format_ack(
|
||||||
format!("request_spawn failed: {message}")
|
resp,
|
||||||
}
|
"request_spawn",
|
||||||
Ok(other) => format!("request_spawn unexpected response: {other:?}"),
|
format!("spawn approval queued for {name}"),
|
||||||
Err(e) => format!("request_spawn transport error: {e:#}"),
|
)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
@ -292,15 +342,10 @@ impl ManagerServer {
|
||||||
let log = format!("{args:?}");
|
let log = format!("{args:?}");
|
||||||
let name = args.name.clone();
|
let name = args.name.clone();
|
||||||
run_tool_envelope("kill", log, self.status_line(), async move {
|
run_tool_envelope("kill", log, self.status_line(), async move {
|
||||||
let req = hive_sh4re::ManagerRequest::Kill { name: args.name };
|
let resp = self
|
||||||
match client::request::<_, hive_sh4re::ManagerResponse>(&self.socket, &req).await {
|
.dispatch(hive_sh4re::ManagerRequest::Kill { name: args.name })
|
||||||
Ok(hive_sh4re::ManagerResponse::Ok) => format!("killed {name}"),
|
.await;
|
||||||
Ok(hive_sh4re::ManagerResponse::Err { message }) => {
|
format_ack(resp, "kill", format!("killed {name}"))
|
||||||
format!("kill failed: {message}")
|
|
||||||
}
|
|
||||||
Ok(other) => format!("kill unexpected response: {other:?}"),
|
|
||||||
Err(e) => format!("kill transport error: {e:#}"),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
@ -322,20 +367,17 @@ impl ManagerServer {
|
||||||
log,
|
log,
|
||||||
self.status_line(),
|
self.status_line(),
|
||||||
async move {
|
async move {
|
||||||
let req = hive_sh4re::ManagerRequest::RequestApplyCommit {
|
let resp = self
|
||||||
|
.dispatch(hive_sh4re::ManagerRequest::RequestApplyCommit {
|
||||||
agent: args.agent,
|
agent: args.agent,
|
||||||
commit_ref: args.commit_ref,
|
commit_ref: args.commit_ref,
|
||||||
};
|
})
|
||||||
match client::request::<_, hive_sh4re::ManagerResponse>(&self.socket, &req).await {
|
.await;
|
||||||
Ok(hive_sh4re::ManagerResponse::Ok) => {
|
format_ack(
|
||||||
format!("apply approval queued for {agent} @ {commit_ref}")
|
resp,
|
||||||
}
|
"request_apply_commit",
|
||||||
Ok(hive_sh4re::ManagerResponse::Err { message }) => {
|
format!("apply approval queued for {agent} @ {commit_ref}"),
|
||||||
format!("request_apply_commit failed: {message}")
|
)
|
||||||
}
|
|
||||||
Ok(other) => format!("request_apply_commit unexpected response: {other:?}"),
|
|
||||||
Err(e) => format!("request_apply_commit transport error: {e:#}"),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue