hyperhive/nix/templates/harness-base.nix
müde 7d6d8e96c1 per-agent extra MCP servers via hyperhive.extraMcpServers
new NixOS option in harness-base.nix:
  hyperhive.extraMcpServers.<key> = {
    command = "/path/to/server";
    args = [ ... ];
    env = { KEY = "value"; };
    allowedTools = [ "send_message" "join_room" ];  # or ["*"]
  };

declared as attrsOf submodule so agents stack arbitrarily many.
the module writes the whole map as JSON to
/etc/hyperhive/extra-mcp.json at activation; the harness reads
that file in mcp::render_claude_config and merges each entry
into the rendered --mcp-config under its own mcpServers.<key>
block. allowed_mcp_tools(flavor) extends the --allowedTools
arg with mcp__<key>__<pattern> for every entry — "*" (the
default) becomes mcp__<key>__* so every tool from that server
is auto-approved, or pass a concrete list to tighten.

collision guard: an extra server keyed "hyperhive" is dropped
with a warn-log so user config can't shadow the built-in
surface. malformed JSON / missing file fall back to "no
extras" silently.

prompt note added: agents see "(some agents only) extra MCP
tools surfaced as mcp__<server>__<tool>" and learn they're
declared via agent.nix. retires the matching TODO under
Per-agent extension. matrix-chat agents + bitburner-agent
migration unblocked.
2026-05-16 02:10:11 +02:00

103 lines
3.9 KiB
Nix

{ pkgs, lib, config, ... }:
{
# Shared scaffolding for any hyperhive harness container — both
# sub-agents (`agent-base.nix`) and the manager (`manager.nix`) extend
# this. The systemd service that actually runs the harness binary
# differs per role and lives in the child module.
options.hyperhive.extraMcpServers = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule {
options = {
command = lib.mkOption {
type = lib.types.str;
description = "Absolute path to the MCP server binary. Use `\${pkgs.foo}/bin/foo` or `/run/current-system/sw/bin/foo`.";
};
args = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Args passed to the MCP server binary.";
};
env = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
description = "Environment variables for the MCP server child process.";
};
allowedTools = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "*" ];
example = [ "send_message" "join_room" ];
description = ''
Tool names this MCP server is auto-approved to call via
`--allowedTools`. Single entry `"*"` (the default) means
"every tool from this server" convenient but trusting.
Tighten to a specific list when you only want a subset.
Names are bare (e.g. `send_message`); the harness prepends
`mcp__<server-key>__` at build time.
'';
};
};
});
default = { };
example = lib.literalExpression ''
{
matrix = {
command = "/run/current-system/sw/bin/mcp-matrix";
args = [ "--config" "/state/matrix.toml" ];
env.MATRIX_HOMESERVER = "https://matrix.example.org";
allowedTools = [ "send_message" "join_room" ];
};
}
'';
description = ''
Extra MCP servers claude sees alongside the hyperhive tool surface.
Keys are the server names (claude addresses tools as
`mcp__<key>__<tool>`). Rendered to `/etc/hyperhive/extra-mcp.json`
at activation time; the harness reads that file at boot and merges
it into `--mcp-config` + `--allowedTools`. Take effect on the
agent's next harness restart (no operator approval needed beyond
whatever brought the new agent.nix into deployed/*).
'';
};
environment.etc."hyperhive/extra-mcp.json".text =
builtins.toJSON config.hyperhive.extraMcpServers;
boot.isNspawnContainer = true;
# `claude-code` is unfree. Each per-agent container's nixosConfiguration
# evaluates its own `nixpkgs` instance, so the operator's host-level
# `nixpkgs.config.allowUnfreePredicate` does not propagate into here —
# we have to allow it inside the container's config as well.
nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (pkgs.lib.getName pkg) [ "claude-code" ];
environment.systemPackages = with pkgs; [
hyperhive
claude-code
bashInteractive
coreutils-full
# procps for pkill — used by the web UI's /api/cancel to SIGINT the
# in-flight claude turn.
procps
];
# Git is needed by claude's Bash tool (for the agent <-> manager config
# request flow) and by hive-c0re's own setup_applied / setup_proposed.
# The per-agent `applied/<name>/flake.nix` overrides `user.name` and
# `user.email` with the agent's identity — values here are `mkDefault`
# so the per-agent override wins without needing `mkForce`.
programs.git = {
enable = true;
config = {
user = {
name = lib.mkDefault "hyperhive";
email = lib.mkDefault "hyperhive@local";
};
init.defaultBranch = lib.mkDefault "main";
};
};
# claude's Bash tool refuses to run without a POSIX shell + $SHELL set.
environment.variables.SHELL = "${pkgs.bashInteractive}/bin/bash";
system.stateVersion = "25.11";
}