feat(#2112): make http-mcp the sole transport for the built-in surface
This commit is contained in:
parent
603c3b79dd
commit
1ba44b77ac
6 changed files with 110 additions and 103 deletions
|
|
@ -11,6 +11,14 @@
|
|||
/// tools as `mcp__<this>__<tool>` (e.g. `mcp__hyperhive__send`).
|
||||
pub const SERVER_NAME: &str = "hyperhive";
|
||||
|
||||
/// Default loopback port the built-in hyperhive MCP surface is served on
|
||||
/// (streamable HTTP, via the persistent `hive-mcp-http` daemon). Overridable
|
||||
/// via `hyperhive.mcp.httpPort`; **must match that option's default** in
|
||||
/// `nix/templates/harness-base.nix`. Safe as a single fixed value across all
|
||||
/// agents because each container runs in its own private network namespace,
|
||||
/// so `127.0.0.1:<port>` is per-container-private (no cross-agent collision).
|
||||
pub const DEFAULT_MCP_HTTP_PORT: u16 = 8790;
|
||||
|
||||
/// 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
|
||||
|
|
@ -330,34 +338,30 @@ fn load_extra_mcp() -> std::collections::BTreeMap<String, ExtraMcpServer> {
|
|||
}
|
||||
|
||||
/// Render the MCP config blob claude reads from `--mcp-config <path>`.
|
||||
/// `mcp_binary` is the path (or PATH-resolvable name) of the
|
||||
/// `hive-agent-mcp` bridge executable; `socket` is the hyperhive per-agent
|
||||
/// socket bind-mounted into the container (forwarded to the child as
|
||||
/// `--socket <path>`). Merges in any extra MCP servers declared via
|
||||
/// `hyperhive.extraMcpServers` in the agent's NixOS config.
|
||||
/// The built-in `hyperhive` surface is an HTTP entry pointing at the
|
||||
/// persistent `hive-mcp-http` daemon (see [`DEFAULT_MCP_HTTP_PORT`]); there
|
||||
/// is no per-turn stdio child for it. Merges in any extra MCP servers
|
||||
/// declared via `hyperhive.extraMcpServers` (those stay stdio bridges).
|
||||
#[must_use]
|
||||
pub fn render_claude_config(mcp_binary: &str, socket: &std::path::Path) -> String {
|
||||
pub fn render_claude_config() -> String {
|
||||
let mut servers = serde_json::Map::new();
|
||||
// When the harness is configured to run the built-in server as a
|
||||
// persistent streamable-http daemon (loopback port in
|
||||
// `HYPERHIVE_MCP_HTTP_PORT`), point claude at the stable URL instead of
|
||||
// respawning a fresh stdio child each turn. The URL survives the per-turn
|
||||
// claude re-spawn, so there is no per-turn re-registration race for the
|
||||
// hyperhive surface. Extra servers (matrix/bash) stay stdio bridges.
|
||||
let hyperhive_entry = match std::env::var("HYPERHIVE_MCP_HTTP_PORT")
|
||||
// The built-in hyperhive surface is served exclusively over streamable
|
||||
// HTTP by the persistent `hive-mcp-http` daemon (loopback, inside the
|
||||
// agent's private network namespace). Point claude at the stable URL
|
||||
// rather than respawning a fresh stdio child each turn: the URL survives
|
||||
// the per-turn claude re-spawn, so there is no per-turn re-registration
|
||||
// race for the hyperhive surface. Extra servers (matrix/bash) stay stdio
|
||||
// bridges. The port comes from `HYPERHIVE_MCP_HTTP_PORT` (always set by
|
||||
// the harness); `DEFAULT_MCP_HTTP_PORT` is the fallback matching the nix
|
||||
// default.
|
||||
let port = std::env::var("HYPERHIVE_MCP_HTTP_PORT")
|
||||
.ok()
|
||||
.and_then(|p| p.trim().parse::<u16>().ok())
|
||||
{
|
||||
Some(port) => serde_json::json!({
|
||||
"type": "http",
|
||||
"url": format!("http://127.0.0.1:{port}/mcp"),
|
||||
}),
|
||||
None => serde_json::json!({
|
||||
"command": mcp_binary,
|
||||
"args": ["--socket", socket.display().to_string()],
|
||||
"env": {}
|
||||
}),
|
||||
};
|
||||
.unwrap_or(DEFAULT_MCP_HTTP_PORT);
|
||||
let hyperhive_entry = serde_json::json!({
|
||||
"type": "http",
|
||||
"url": format!("http://127.0.0.1:{port}/mcp"),
|
||||
});
|
||||
servers.insert(SERVER_NAME.to_owned(), hyperhive_entry);
|
||||
// Auto-inject HYPERHIVE_STATE_DIR so extra MCP servers can resolve the
|
||||
// agent's durable state dir without the agent author hard-coding it.
|
||||
|
|
|
|||
|
|
@ -113,30 +113,25 @@ impl TurnFiles {
|
|||
/// Returns an error if any of the config files cannot be written to disk.
|
||||
pub async fn prepare(socket: &Path, label: &str) -> Result<Self> {
|
||||
Ok(Self {
|
||||
mcp_config: write_mcp_config(socket).await?,
|
||||
mcp_config: write_mcp_config().await?,
|
||||
system_prompt: write_system_prompt(socket, label).await?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Drop the MCP config blob claude reads from `--mcp-config <path>`.
|
||||
/// `socket` is the hyperhive per-container socket (forwarded to the child
|
||||
/// as `--socket <path>`). The MCP server is the `hive-agent-mcp` binary
|
||||
/// installed next to the running `hive-agent` (resolved as a sibling of
|
||||
/// `/proc/self/exe`; PATH-resolvable name as the fallback).
|
||||
/// The built-in hyperhive surface is served over HTTP by the persistent
|
||||
/// `hive-mcp-http` daemon, so no per-turn stdio child is spawned; extra
|
||||
/// servers declared via `hyperhive.extraMcpServers` are still stdio bridges.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the config file cannot be written.
|
||||
pub async fn write_mcp_config(socket: &Path) -> Result<PathBuf> {
|
||||
pub async fn write_mcp_config() -> Result<PathBuf> {
|
||||
let parent = crate::paths::config_dir();
|
||||
tokio::fs::create_dir_all(&parent).await.ok();
|
||||
let path = parent.join("claude-mcp-config.json");
|
||||
let exe = std::env::current_exe()
|
||||
.ok()
|
||||
.and_then(|p| Some(p.parent()?.join("hive-agent-mcp")))
|
||||
.map_or_else(|| "hive-agent-mcp".into(), |p| p.display().to_string());
|
||||
let body = mcp_config::render_claude_config(&exe, socket);
|
||||
let body = mcp_config::render_claude_config();
|
||||
tokio::fs::write(&path, body).await?;
|
||||
tracing::info!(path = %path.display(), "wrote claude MCP config");
|
||||
Ok(path)
|
||||
|
|
|
|||
Loading…
Reference in a new issue