bypass-mode perms + deny list, drop allow-list plumbing

claude-settings.json now sets permissions.defaultMode=bypassPermissions
with a small deny list (WebFetch, WebSearch, Task, TodoWrite). The
per-flavor allow list and --tools / --allowedTools CLI flags are gone
— anything not denied auto-approves. mcp.rs loses ALLOWED_BUILTIN_TOOLS,
builtin_tools_arg, allow_list, allowed_mcp_tools. The extraMcpServers
allowedTools field is parsed for back-compat but no longer wired
anywhere; restrict via permissions.deny instead.
This commit is contained in:
müde 2026-05-16 15:17:30 +02:00
parent 3d2a7ffec7
commit 8e7405db13
3 changed files with 27 additions and 82 deletions

View file

@ -548,86 +548,23 @@ impl ServerHandler for ManagerServer {}
/// tools as `mcp__<this>__<tool>` (e.g. `mcp__hyperhive__send`).
pub const SERVER_NAME: &str = "hyperhive";
/// Built-in claude tools the turn loop enables via `--tools`. Anything not
/// in this list literally doesn't exist in the session (claude won't even
/// try to call it). Web egress (`WebFetch`/`WebSearch`) and nested agents
/// (`Task`) are intentionally omitted for now; `Bash` is allowed pending a
/// finer-grained allow-list system for shell command patterns. Edit later
/// as our trust model evolves.
pub const ALLOWED_BUILTIN_TOOLS: &[&str] =
&["Bash", "Edit", "Glob", "Grep", "Read", "TodoWrite", "Write"];
/// Which MCP tool surface to advertise via `--allowedTools`. The agent
/// list is the strict subset of the manager list, so we just thread the
/// flavor through.
/// Which hyperhive MCP surface to advertise — sub-agent (short tool
/// list) or manager (full lifecycle surface). Threaded through the
/// system-prompt renderer and the per-flavor web UI dispatch; tool
/// gating itself now lives in `claude-settings.json`'s
/// `permissions.{defaultMode, deny}`, not here.
#[derive(Debug, Clone, Copy)]
pub enum Flavor {
Agent,
Manager,
}
/// MCP tools claude is allowed to call without prompting. Mirrors the
/// hyperhive surface so a new tool added in the corresponding `#[tool_router]`
/// impl needs to be listed here too.
#[must_use]
pub fn allowed_mcp_tools(flavor: Flavor) -> Vec<String> {
let names: &[&str] = match flavor {
Flavor::Agent => &["send", "recv", "ask_operator"],
Flavor::Manager => &[
"send",
"recv",
"request_spawn",
"kill",
"start",
"restart",
"update",
"request_apply_commit",
"ask_operator",
],
};
let mut out: Vec<String> = names
.iter()
.map(|t| format!("mcp__{SERVER_NAME}__{t}"))
.collect();
// Extra MCP servers declared via `hyperhive.extraMcpServers` in
// the agent's NixOS config. Each entry maps its `allowedTools`
// pattern list to `mcp__<server>__<pattern>` so claude can call
// them without per-tool operator approval. `["*"]` (the default)
// expands to `mcp__<server>__*` — every tool from that server.
for (server, spec) in load_extra_mcp() {
if server == SERVER_NAME {
continue;
}
for pat in spec.allowed_tools {
out.push(format!("mcp__{server}__{pat}"));
}
}
out
}
/// Combined allow-list passed to `--allowedTools` (auto-approve) — covers
/// both the built-ins and the MCP surface.
#[must_use]
pub fn allowed_tools_arg(flavor: Flavor) -> String {
let mut all: Vec<String> = ALLOWED_BUILTIN_TOOLS
.iter()
.map(|s| (*s).to_owned())
.collect();
all.extend(allowed_mcp_tools(flavor));
all.join(",")
}
/// Built-in tools list for `--tools` (which built-ins exist in this
/// session). Same as `ALLOWED_BUILTIN_TOOLS` but joined comma-separated.
#[must_use]
pub fn builtin_tools_arg() -> String {
ALLOWED_BUILTIN_TOOLS.join(",")
}
/// Where the NixOS module writes the per-agent extra-MCP spec (see
/// `nix/templates/harness-base.nix`). Each entry becomes an additional
/// `mcpServers.<key>` block in the rendered claude config + a
/// `mcp__<key>__<tool>` pattern in `--allowedTools`.
/// `mcpServers.<key>` block in the rendered claude config; the
/// `allowedTools` field is parsed for back-compat but no longer wired
/// anywhere — under `bypassPermissions` every MCP tool auto-approves
/// unless listed in `permissions.deny`.
const EXTRA_MCP_PATH: &str = "/etc/hyperhive/extra-mcp.json";
/// Where the NixOS module writes the per-agent send allow-list (see
@ -687,6 +624,7 @@ struct ExtraMcpServer {
env: std::collections::BTreeMap<String, String>,
#[serde(default = "default_allowed_tools")]
#[serde(rename = "allowedTools")]
#[allow(dead_code)] // back-compat: superseded by `permissions.deny`
allowed_tools: Vec<String>,
}