matrix: hive-matrix-mcp crate (daemon+stdio bridge) + harness wiring (#548 phase 3)
This commit is contained in:
parent
23ed124881
commit
e6c53045ad
16 changed files with 3310 additions and 35 deletions
101
hive-matrix-mcp/src/protocol.rs
Normal file
101
hive-matrix-mcp/src/protocol.rs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
//! Wire types for the daemon ↔ stdio-MCP-bridge unix socket protocol.
|
||||
//!
|
||||
//! Same shape as `damocles-daemon`'s `DaemonRequest`/`DaemonResponse`
|
||||
//! (which this is forked from in spirit). Each request is a single
|
||||
//! JSON line; each response is a single JSON line back. The stdio MCP
|
||||
//! bridge holds a fresh connection per tool call — claude's tool
|
||||
//! lifecycle is shorter than a persistent matrix-sdk Client wants to
|
||||
//! live, so the daemon stays alive and the MCP reconnects per call.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Request from the stdio MCP bridge to the daemon. The MCP bridge
|
||||
/// owns the on-wire shape claude sees; this enum is the internal
|
||||
/// shape the daemon dispatches over.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(tag = "method")]
|
||||
pub enum DaemonRequest {
|
||||
/// Post a plain-text or markdown message to a room. `room` accepts
|
||||
/// either a matrix room id (`!abc:server`) or a canonical alias
|
||||
/// (`#name:server`); the daemon resolves aliases server-side.
|
||||
#[serde(rename = "send_message")]
|
||||
SendMessage { room: String, body: String },
|
||||
|
||||
/// Open (or reuse) a DM with `user_id` and post `body`. Creates
|
||||
/// the DM room if one doesn't already exist between this agent
|
||||
/// and the user.
|
||||
#[serde(rename = "send_dm")]
|
||||
SendDm { user_id: String, body: String },
|
||||
|
||||
/// React to a specific event with an emoji `key`. Matrix-spec
|
||||
/// `m.reaction` annotation.
|
||||
#[serde(rename = "send_reaction")]
|
||||
SendReaction {
|
||||
room: String,
|
||||
event_id: String,
|
||||
key: String,
|
||||
},
|
||||
|
||||
/// Reply to `event_id` in `room` with `body` as a threaded reply.
|
||||
/// Sets the `m.in_reply_to` relation so matrix clients render the
|
||||
/// thread.
|
||||
#[serde(rename = "send_reply")]
|
||||
SendReply {
|
||||
room: String,
|
||||
event_id: String,
|
||||
body: String,
|
||||
},
|
||||
|
||||
/// Mark `event_id` (in `room`) as read for this agent. Sends a
|
||||
/// read receipt; bumps the room's "unread" indicator down on
|
||||
/// matrix clients (and for other agents).
|
||||
#[serde(rename = "mark_read")]
|
||||
MarkRead { room: String, event_id: String },
|
||||
|
||||
/// List rooms the agent has joined. Returns each room's id +
|
||||
/// canonical alias (when present) + name + member count.
|
||||
#[serde(rename = "list_rooms")]
|
||||
ListRooms,
|
||||
|
||||
/// List the members of a room. Each entry carries the matrix
|
||||
/// user id + the resolved display name (when set).
|
||||
#[serde(rename = "list_room_members")]
|
||||
ListRoomMembers { room: String },
|
||||
|
||||
/// Read the last `limit` events from a room's timeline. Caller
|
||||
/// gets each event's id, sender, `server_ts`, type, and body (best-
|
||||
/// effort plain-text extraction from `m.text` / `m.notice` etc.).
|
||||
#[serde(rename = "read_room")]
|
||||
ReadRoom { room: String, limit: Option<usize> },
|
||||
|
||||
/// Liveness probe used by the stdio MCP bridge on connect — fast
|
||||
/// "are you up?" round-trip that doesn't touch matrix-sdk.
|
||||
#[serde(rename = "ping")]
|
||||
Ping,
|
||||
}
|
||||
|
||||
/// Response shape: `ok` carries the payload (any JSON; the MCP bridge
|
||||
/// passes it back to claude as the tool result), `error` carries a
|
||||
/// human-readable error string.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(tag = "kind", rename_all = "snake_case")]
|
||||
pub enum DaemonResponse {
|
||||
Ok { payload: serde_json::Value },
|
||||
Error { message: String },
|
||||
}
|
||||
|
||||
impl DaemonResponse {
|
||||
/// Convenience: build an Ok response from any serializable value.
|
||||
pub fn ok<T: Serialize>(payload: &T) -> Self {
|
||||
Self::Ok {
|
||||
payload: serde_json::to_value(payload).unwrap_or(serde_json::Value::Null),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience: build an Error response from any `Display` value.
|
||||
pub fn error(msg: impl std::fmt::Display) -> Self {
|
||||
Self::Error {
|
||||
message: msg.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue