refactor(hive-ag3nt): split mcp into submodules
mod.rs keeps the #[tool_router] impl + server wiring untouched; rendering/format helpers move to mcp/render.rs, the 24 tool arg structs to mcp/args.rs
This commit is contained in:
parent
c84028ddcf
commit
b0736e6f3e
3 changed files with 827 additions and 797 deletions
359
hive-ag3nt/src/mcp/args.rs
Normal file
359
hive-ag3nt/src/mcp/args.rs
Normal file
|
|
@ -0,0 +1,359 @@
|
|||
//! Argument structs for the MCP tools: `serde::Deserialize` +
|
||||
//! `schemars::JsonSchema` derives whose field doc-comments become the
|
||||
//! parameter descriptions claude sees in each tool's input schema.
|
||||
|
||||
use rmcp::schemars;
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct SendArgs {
|
||||
/// Logical agent name to deliver the message to (e.g. `"manager"`,
|
||||
/// `"alice"`, or the literal `"operator"` for the dashboard's T4LK box).
|
||||
pub to: String,
|
||||
/// Message body. Plain text; the broker doesn't parse it.
|
||||
pub body: String,
|
||||
/// Optional broker row-id of the message this is a reply to. Lets
|
||||
/// the dashboard render conversation threads. Pass the `id` from the
|
||||
/// `DeliveredMessage` you're responding to; omit for new threads.
|
||||
/// Silently ignored if the id is unknown or out of retention.
|
||||
#[serde(default)]
|
||||
pub in_reply_to: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct RecvArgs {
|
||||
/// How long to long-poll for the FIRST message before returning
|
||||
/// the empty marker. Capped at 60s server-side. Default (None)
|
||||
/// is 30s. Useful when an agent wants to park its turn waiting
|
||||
/// for any new work — pick a longer wait to coalesce bursts.
|
||||
#[serde(default)]
|
||||
pub wait_seconds: Option<u64>,
|
||||
/// Maximum number of messages to pop in this round-trip. Default
|
||||
/// (None) is 1 (single-message behaviour — exactly what you want
|
||||
/// when you're called to drive a turn off the first wake). Pass
|
||||
/// a higher value (capped at 5 server-side) when you've been
|
||||
/// told the inbox has more queued (the wake prompt mentions
|
||||
/// pending count) and want to drain everything in one tool call.
|
||||
/// Once the long-poll wakes up, the call drains up to `max` in
|
||||
/// total before returning — no extra round-trip needed.
|
||||
#[serde(default)]
|
||||
pub max: Option<u32>,
|
||||
}
|
||||
|
||||
/// MCP tool args for `ack_until`.
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct AckUntilArgs {
|
||||
/// Highest broker message id to mark handled: every inbox message
|
||||
/// with `id <= up_to` (ids show as `[msg #<id>]` in recv output)
|
||||
/// is acked in one sweep. Pass the highest id you've actually
|
||||
/// seen/triaged — anything above it stays queued for later turns.
|
||||
pub up_to: i64,
|
||||
}
|
||||
|
||||
/// MCP tool args for `remind`. Exactly one of `delay_seconds` or
|
||||
/// `at_unix_timestamp` must be set; both / neither is a tool-side error.
|
||||
/// Hides the tagged `ReminderTiming` enum behind a flatter schema so the
|
||||
/// model picks one field instead of building `{"timing_type": "in_seconds",
|
||||
/// "seconds": 60}` shaped objects.
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct RemindArgs {
|
||||
/// Body that lands in your inbox when the reminder fires (sender
|
||||
/// will appear as `reminder`). Soft cap at 4 KiB inline — anything
|
||||
/// larger gets auto-persisted to a file under
|
||||
/// `/agents/<you>/state/reminders/auto-<ts>.md` and the inbox
|
||||
/// message becomes a short pointer. Pass `file_path` if you want
|
||||
/// to control the destination yourself.
|
||||
pub message: String,
|
||||
/// Fire `delay_seconds` from now (relative). Set this OR
|
||||
/// `at_unix_timestamp`, not both.
|
||||
#[serde(default)]
|
||||
pub delay_seconds: Option<u64>,
|
||||
/// Fire at this absolute unix timestamp (seconds since epoch). Set
|
||||
/// this OR `delay_seconds`, not both.
|
||||
#[serde(default)]
|
||||
pub at_unix_timestamp: Option<i64>,
|
||||
/// Optional path to a file the scheduler should reference instead of
|
||||
/// inlining a long `message`. Use this for large payloads (research
|
||||
/// notes, file lists, intermediate state). Path must be reachable from
|
||||
/// the agent's container — typically under `/agents/<you>/state/`.
|
||||
#[serde(default)]
|
||||
pub file_path: Option<String>,
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Privileged tool arg types (lifecycle, approvals, scheduling, diagnostics)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct RequestInitConfigArgs {
|
||||
/// New sub-agent name (≤9 chars). Queues an `InitConfig` approval; on
|
||||
/// approval hive-c0re seeds the proposed config repo at
|
||||
/// `/agents/<name>/config/agent.nix` with the default template. After
|
||||
/// the approval the manager edits + commits the config and calls
|
||||
/// `request_apply_commit` to pin the customised sha for the container's
|
||||
/// first build.
|
||||
pub name: String,
|
||||
/// Optional description shown on the dashboard approval card.
|
||||
#[serde(default)]
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct KillArgs {
|
||||
/// Sub-agent name (without the `h-` container prefix).
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct SetStatusArgs {
|
||||
/// Status text to display on the dashboard card. Pass an empty string to clear.
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct CreateRepoArgs {
|
||||
/// Repo name — a single segment of letters, digits, `-`, `_`, `.`
|
||||
/// (no leading `-`/`.`). The repo is created as `agents/<repo>`.
|
||||
pub repo: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct GetAgentMetaArgs {
|
||||
/// Logical name of the agent to query (e.g. `"iris"`, `"manager"`).
|
||||
/// Omit to query your own identity + status — replaces the
|
||||
/// previous `whoami` self-introspection tool.
|
||||
#[serde(default)]
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct StartArgs {
|
||||
/// Sub-agent name (without the `h-` container prefix).
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct RestartArgs {
|
||||
/// Sub-agent name (without the `h-` container prefix).
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct UpdateArgs {
|
||||
/// Sub-agent name (without the `h-` container prefix).
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct AskArgs {
|
||||
/// The question to surface.
|
||||
pub question: String,
|
||||
/// Optional fixed-choice answers. The dashboard renders these as
|
||||
/// chips alongside a free-text fallback ("Other…") so the operator
|
||||
/// is never trapped by an incomplete list; peer-agent recipients
|
||||
/// see the list in their inbox event and can return any string.
|
||||
#[serde(default)]
|
||||
pub options: Vec<String>,
|
||||
/// When true, options are rendered as checkboxes — the answerer
|
||||
/// can pick any subset. The answer comes back as a single string
|
||||
/// with selections joined by ", ". Ignored when `options` is empty.
|
||||
#[serde(default)]
|
||||
pub multi: bool,
|
||||
/// Optional auto-cancel after `ttl_seconds` (capped server-side at
|
||||
/// 6 hours). On expiry the question resolves with answer
|
||||
/// `[expired]` and the asker receives the usual
|
||||
/// `question_answered` system event (with `answerer:
|
||||
/// "ttl-watchdog"`). `None` (default) = wait indefinitely.
|
||||
#[serde(default)]
|
||||
pub ttl_seconds: Option<u64>,
|
||||
/// Recipient. Omit (or pass `"operator"`) to ask the human
|
||||
/// operator via the dashboard. Pass another agent's logical name
|
||||
/// to ask that peer — they receive a `question_asked` event in
|
||||
/// their inbox and answer via `mcp__hyperhive__answer`.
|
||||
#[serde(default)]
|
||||
pub to: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct AnswerArgs {
|
||||
/// Id of the question being answered — comes from the
|
||||
/// `question_asked` event in your inbox.
|
||||
pub id: i64,
|
||||
/// Free-text answer body. Soft-capped at 4 KiB by the same
|
||||
/// `MESSAGE_MAX_BYTES` limit as `send`; keep it short or write the
|
||||
/// detail to a file and pass a path.
|
||||
pub answer: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct CancelLooseEndArgs {
|
||||
/// Which kind of thread to cancel — `"question"` for an open
|
||||
/// `ask` that's still waiting on an answer, `"reminder"` for a
|
||||
/// scheduled `remind` that hasn't fired yet. Use the `kind`
|
||||
/// field straight off the `get_loose_ends` row.
|
||||
pub kind: String,
|
||||
/// Row id from the matching `get_loose_ends` entry (or the
|
||||
/// `question_queued` reply when you submitted it).
|
||||
pub id: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct AgentGetLooseEndsArgs {
|
||||
/// Whose loose ends to list. Omit (or `null`) for your own. You may
|
||||
/// also pass a direct child agent's name without any extra capability.
|
||||
/// Pass any other agent name to inspect their threads — requires the
|
||||
/// `query_agent_state` capability; without it the request is rejected
|
||||
/// with an error. The `"*"` hive-wide value is not available on the
|
||||
/// agent socket.
|
||||
#[serde(default)]
|
||||
pub agent: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct RequestApplyCommitArgs {
|
||||
/// Logical agent name whose config repo the commit lives in.
|
||||
pub agent: String,
|
||||
/// Commit sha (full or short, 7-40 hex chars) in that agent's
|
||||
/// proposed config repo. Must be a sha — a branch or tag name
|
||||
/// (e.g. `main`) is rejected; the approval pins the exact commit.
|
||||
pub commit_ref: String,
|
||||
/// Optional description shown on the dashboard approval card so the
|
||||
/// operator knows what the change does without opening the diff.
|
||||
#[serde(default)]
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct UpdateMetaInputsArgs {
|
||||
/// Flake input names to update (e.g. `["bitburner-agent", "nixpkgs"]`).
|
||||
/// Pass an empty list to update ALL inputs.
|
||||
#[serde(default)]
|
||||
pub inputs: Vec<String>,
|
||||
/// Optional description shown on the dashboard approval card.
|
||||
#[serde(default)]
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct RequestSchedulePromptArgs {
|
||||
/// Recipient agents — one schedule fires to many inboxes at the
|
||||
/// scheduled time. `operator` is a legitimate target (mara: "we
|
||||
/// want to get rid of the manager special case so yes manager
|
||||
/// can be recipient" — the operator slot follows the same rule).
|
||||
pub targets: Vec<String>,
|
||||
/// Message body delivered to each target's inbox at fire time.
|
||||
/// Same size budget as `send` bodies.
|
||||
pub body: String,
|
||||
/// Absolute unix timestamp (seconds) for the FIRST fire. For
|
||||
/// recurring schedules the worker re-arms in
|
||||
/// `interval_seconds` steps from this point on.
|
||||
pub first_fire_at_unix: i64,
|
||||
/// `None` / absent = one-shot. `Some(n > 0)` = recurring every
|
||||
/// `n` seconds. The worker clamps catch-up so a long downtime
|
||||
/// fires ONCE on resume (skipped-cycle count surfaces in the
|
||||
/// per-target `last_result`), not N delayed pulses in a row.
|
||||
#[serde(default)]
|
||||
pub interval_seconds: Option<u64>,
|
||||
/// Optional description shown on the dashboard approval card +
|
||||
/// preserved on the schedule row for later operator reference.
|
||||
#[serde(default)]
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct FireScheduleNowArgs {
|
||||
/// Schedule id to fire out of band. Get this from a prior
|
||||
/// `list_schedules` call or the approval-resolved event for
|
||||
/// the originating `request_schedule_prompt`.
|
||||
pub id: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct CancelScheduleArgs {
|
||||
/// Schedule id from a prior `list_schedules` call or the
|
||||
/// approval-resolved event for a `request_schedule_prompt`.
|
||||
pub id: i64,
|
||||
/// Optional target list. `None` / empty = cancel the entire
|
||||
/// schedule. `Some(["alice", "bob"])` = cancel just those
|
||||
/// recipients (the schedule keeps firing for any remaining
|
||||
/// active targets, and auto-cancels its parent row when every
|
||||
/// target is gone).
|
||||
#[serde(default)]
|
||||
pub targets: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct EditScheduleArgs {
|
||||
/// Schedule id from a prior `list_schedules` call or the
|
||||
/// approval-resolved event for a `request_schedule_prompt`.
|
||||
pub id: i64,
|
||||
/// New body text. Omit to keep the existing one.
|
||||
#[serde(default)]
|
||||
pub body: Option<String>,
|
||||
/// New description. Omit to keep the existing one. (To CLEAR
|
||||
/// the description, use the dashboard PATCH endpoint
|
||||
/// directly — the agent surface intentionally keeps the args
|
||||
/// flat / non-nullable to dodge the doubly-wrapped Option
|
||||
/// schemars quirk; clearing fields is rare and operator-side.)
|
||||
#[serde(default)]
|
||||
pub description: Option<String>,
|
||||
/// Recurring interval in seconds. Omit to keep the existing
|
||||
/// cadence; pass an explicit value to set a new one. Toggling
|
||||
/// recurring↔one-shot (clearing the interval) is operator-only
|
||||
/// for the same reason as `description` above.
|
||||
#[serde(default)]
|
||||
pub interval_seconds: Option<u64>,
|
||||
/// New absolute unix timestamp for the next fire. Omit to
|
||||
/// leave the schedule on its current cadence.
|
||||
#[serde(default)]
|
||||
pub next_fire_at_unix: Option<i64>,
|
||||
/// Names of new targets to add. Replace-on-conflict: re-adding
|
||||
/// a previously cancelled target resets its history (operator
|
||||
/// intent on re-add = "this target is active again").
|
||||
#[serde(default)]
|
||||
pub targets_add: Option<Vec<String>>,
|
||||
/// Names of targets to cancel. Tombstones preserve per-target
|
||||
/// audit; when no active targets remain the schedule
|
||||
/// auto-cancels.
|
||||
#[serde(default)]
|
||||
pub targets_remove: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct GetLogsArgs {
|
||||
/// Logical agent name to fetch logs for (e.g. `gui`, `iris`).
|
||||
/// hive-c0re maps it to the underlying machine name (`h-gui`)
|
||||
/// itself — pass the plain agent name, not the `h-` form.
|
||||
pub agent: String,
|
||||
/// How many journal lines to return (default: 50, max: 500).
|
||||
#[serde(default)]
|
||||
pub lines: Option<u32>,
|
||||
}
|
||||
|
||||
/// Arguments for `get_host_journal` (capability-gated: `read_host_journal`).
|
||||
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
|
||||
pub struct GetHostJournalArgs {
|
||||
/// Systemd unit to filter (e.g. `hive-c0re.service`). Omit for all units.
|
||||
#[serde(default)]
|
||||
pub unit: Option<String>,
|
||||
/// nspawn machine name verbatim (e.g. `h-iris`). Omit for host journal.
|
||||
/// Agent containers use the `h-` prefix (e.g. `h-iris`); infrastructure
|
||||
/// containers use their full name (e.g. `hive-ci`, `hive-forge`,
|
||||
/// `hive-matrix`, `hive-gateway`).
|
||||
#[serde(default)]
|
||||
pub container: Option<String>,
|
||||
/// Number of lines to return (default 30, max 100).
|
||||
#[serde(default)]
|
||||
pub lines: Option<u32>,
|
||||
/// Minimum syslog priority level.
|
||||
#[serde(default)]
|
||||
pub priority: Option<hive_sh4re::JournalPriority>,
|
||||
/// Regex to match against log message fields (journalctl --grep).
|
||||
#[serde(default)]
|
||||
pub grep: Option<String>,
|
||||
/// Show entries on or newer than this timestamp (e.g. `-1h`).
|
||||
#[serde(default)]
|
||||
pub since: Option<String>,
|
||||
/// Show entries on or older than this timestamp.
|
||||
#[serde(default)]
|
||||
pub until: Option<String>,
|
||||
}
|
||||
Loading…
Reference in a new issue