feat(#1065): web_tools tool group gates WebFetch/WebSearch built-ins
Replaces the earlier capability-based approach (closed #1069) with a ToolGroup — capabilities are for privileged system access, web egress is a tool permission. Add ToolGroup::WebTools to hive-sh4re: - tools() returns &[] (no MCP tools gated) - builtin_tools() returns &["WebFetch", "WebSearch"] — new method on ToolGroup - Present in ALL and as_str() → "web_tools" In hive-ag3nt/mcp.rs: - allowed_tools_arg() now iterates group.builtin_tools() to prepend any group-gated built-ins alongside the base ALLOWED_BUILTIN_TOOLS set - builtin_tools_arg_for_flavor(flavor) replaces builtin_tools_arg() so the flavor-correct effective groups are used when building --tools - builtin_tools_arg() kept as a flavor=Agent convenience alias - turn.rs updated to call builtin_tools_arg_for_flavor(files.flavor) so manager sessions also see web tools when web_tools is in their groups The dashboard T00L GR0UPS table gains a web_tools column automatically (ToolGroup::ALL drives the columns).
This commit is contained in:
parent
cd6cbf9997
commit
f6b80cf02e
3 changed files with 58 additions and 11 deletions
|
|
@ -1895,9 +1895,10 @@ 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
|
||||
/// Built-in claude tools always present in every session. Anything not
|
||||
/// in this list (or added by `extra_builtin_tools`) literally doesn't
|
||||
/// exist in the session. Web egress (`WebFetch`/`WebSearch`) are
|
||||
/// tool-group-gated (`web_tools`) — off by default. Nested agents
|
||||
/// (`Task`) are intentionally omitted. `Bash` is disallowed — shell
|
||||
/// execution goes through `mcp__hyperhive__bash_run` (background tasks
|
||||
/// with structured output) instead of a raw interactive shell. `TodoWrite`
|
||||
|
|
@ -2035,13 +2036,19 @@ pub fn allowed_mcp_tools(groups: &[hive_sh4re::ToolGroup]) -> Vec<String> {
|
|||
/// 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();
|
||||
let groups = effective_tool_groups(flavor);
|
||||
// Base built-ins always present.
|
||||
let mut all: Vec<String> = ALLOWED_BUILTIN_TOOLS.iter().map(|s| (*s).to_owned()).collect();
|
||||
// Extra built-ins gated by tool groups (e.g. WebFetch/WebSearch via web_tools).
|
||||
for group in &groups {
|
||||
for tool in group.builtin_tools() {
|
||||
if !all.iter().any(|t| t == *tool) {
|
||||
all.push((*tool).to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
all.extend(allowed_mcp_tools(&groups));
|
||||
// Capability-gated tools: added to --allowedTools when HIVE_CAPABILITIES
|
||||
// Capability-gated MCP tools: added to --allowedTools when HIVE_CAPABILITIES
|
||||
// includes the corresponding capability. hive-c0re performs a second
|
||||
// server-side check, so this is a usability gate (no annoying prompts),
|
||||
// not the security boundary.
|
||||
|
|
@ -2052,10 +2059,28 @@ pub fn allowed_tools_arg(flavor: Flavor) -> String {
|
|||
}
|
||||
|
||||
/// Built-in tools list for `--tools` (which built-ins exist in this
|
||||
/// session). Same as `ALLOWED_BUILTIN_TOOLS` but joined comma-separated.
|
||||
/// session). Base set plus any group-gated built-ins (e.g.
|
||||
/// `WebFetch`/`WebSearch` when the `web_tools` group is active).
|
||||
#[must_use]
|
||||
pub fn builtin_tools_arg() -> String {
|
||||
ALLOWED_BUILTIN_TOOLS.join(",")
|
||||
builtin_tools_arg_for_flavor(Flavor::Agent)
|
||||
}
|
||||
|
||||
/// Flavor-aware variant used by `turn.rs` via `builtin_tools_arg`. Reads
|
||||
/// the effective tool groups for `flavor` so `--tools` matches what
|
||||
/// `--allowedTools` includes for the same session.
|
||||
#[must_use]
|
||||
pub fn builtin_tools_arg_for_flavor(flavor: Flavor) -> String {
|
||||
let groups = effective_tool_groups(flavor);
|
||||
let mut tools: Vec<&str> = ALLOWED_BUILTIN_TOOLS.to_vec();
|
||||
for group in &groups {
|
||||
for t in group.builtin_tools() {
|
||||
if !tools.contains(t) {
|
||||
tools.push(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
tools.join(",")
|
||||
}
|
||||
|
||||
/// Where the NixOS module writes the per-agent extra-MCP spec (see
|
||||
|
|
|
|||
|
|
@ -672,7 +672,7 @@ async fn run_claude(prompt: &str, files: &TurnFiles, bus: &Bus) -> Result<(bool,
|
|||
.arg(&files.mcp_config)
|
||||
.arg("--strict-mcp-config")
|
||||
.arg("--tools")
|
||||
.arg(mcp::builtin_tools_arg())
|
||||
.arg(mcp::builtin_tools_arg_for_flavor(files.flavor))
|
||||
.arg("--allowedTools")
|
||||
.arg(mcp::allowed_tools_arg(files.flavor));
|
||||
let mut child = cmd
|
||||
|
|
|
|||
|
|
@ -774,10 +774,18 @@ pub enum ToolGroup {
|
|||
Diagnostics,
|
||||
/// `bash_run`, `bash_status`
|
||||
Execution,
|
||||
/// Claude built-in web egress tools: `WebFetch` (retrieve a URL) and
|
||||
/// `WebSearch` (search the web). Both are omitted from `--tools` by
|
||||
/// default; adding this group to an agent enables them in the session
|
||||
/// and in `--allowedTools` so they run without a confirmation prompt.
|
||||
/// Does not gate any MCP tools — `tools()` returns `&[]`.
|
||||
WebTools,
|
||||
}
|
||||
|
||||
impl ToolGroup {
|
||||
/// The MCP tool names (without the `mcp__hyperhive__` prefix) in this group.
|
||||
/// Returns `&[]` for `WebTools` — it enables Claude built-in tools,
|
||||
/// not MCP tools; see `builtin_tools()`.
|
||||
#[must_use]
|
||||
pub fn tools(self) -> &'static [&'static str] {
|
||||
match self {
|
||||
|
|
@ -804,6 +812,18 @@ impl ToolGroup {
|
|||
],
|
||||
Self::Diagnostics => &["get_logs"],
|
||||
Self::Execution => &["bash_run", "bash_status"],
|
||||
Self::WebTools => &[],
|
||||
}
|
||||
}
|
||||
|
||||
/// The Claude built-in tool names enabled by this group. Only
|
||||
/// `WebTools` returns a non-empty slice; all other groups return `&[]`
|
||||
/// (they control MCP tools via `tools()` instead).
|
||||
#[must_use]
|
||||
pub fn builtin_tools(self) -> &'static [&'static str] {
|
||||
match self {
|
||||
Self::WebTools => &["WebFetch", "WebSearch"],
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -837,6 +857,7 @@ impl ToolGroup {
|
|||
Self::Scheduling,
|
||||
Self::Diagnostics,
|
||||
Self::Execution,
|
||||
Self::WebTools,
|
||||
];
|
||||
|
||||
/// The `snake_case` wire name for this group (matches `serde(rename_all =
|
||||
|
|
@ -852,6 +873,7 @@ impl ToolGroup {
|
|||
Self::Scheduling => "scheduling",
|
||||
Self::Diagnostics => "diagnostics",
|
||||
Self::Execution => "execution",
|
||||
Self::WebTools => "web_tools",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue