hive mcp: own MCP servers and their credentials - #3
Merged
Conversation
Buzz cannot configure an HTTP MCP server. `buzz-acp`'s McpServer is
{name, command, args, env} — stdio only, no url, no headers — and the
backend-provider deploy payload carries no MCP field at all. That is a
boundary in the surface, not a gap to route around, so hive owns MCP
configuration outright.
Until now the only way to attach one was two flat fields on the desktop
provider config, which could express exactly ONE server and hardcoded its
name to "mcp". hive-spec has always had `mcp: Vec<Mcp>`; the format was
never the limit.
hive mcp add parachute --url https://vault/mcp --agent uni
hive mcp login parachute --agent uni
hive mcp list --agent uni
Specs are edited with toml_edit rather than regenerated. The README calls
generated specs "safe to edit and to commit", and any generator that
rewrites the document silently deletes comments, ordering, and blocks it
does not know about.
`login` walks the flow the MCP specification defines: unauthenticated
probe -> WWW-Authenticate resource_metadata -> protected-resource metadata
(RFC 9728) -> authorization-server metadata (RFC 8414) -> dynamic client
registration (RFC 7591) -> PKCE S256 -> authorization code -> token, bound
to the resource with RFC 8707.
Why an interactive flow rather than `secret put` with a hand-minted token:
a minted token carries the minter's authority and expires without warning.
Verified against a live Parachute vault, whose access tokens last 900
SECONDS — an agent on a static token would fail its first tool call
fifteen minutes after setup, and the error would read as a broken server.
The refresh token is stored alongside, and matters only because the broker
serves credentials PER CONNECTION rather than injecting them once at
container start; a design that baked credentials into the environment
could not renew them without recreating the container.
`refresh` reads through the broker's grant-checked path under a named
`hive-cli` grant rather than reaching around it, so a manual renewal
appears in the audit log exactly like an agent's own fetch.
No new runtime dependency: HTTP goes through curl for the same reasons
hive_core::docker drives Docker through its CLI, and base64url is forty
lines rather than a crate.
Known limitation: the loopback redirect needs a browser that can reach the
host's 127.0.0.1, so a headless box needs someone at its screen. RFC 8628
device grant would fix that; Parachute advertises only authorization_code
and refresh_token today.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gw5rgVt1CQ8EYrLpZtQjdM
The loopback redirect only resolves on the machine running the command, and an agent host is exactly the machine nobody is sitting at. Authorizing from a laptop left the browser on a dead address while the box waited forever — hit on the first real login. Now the listener and stdin race, whichever arrives first. Paste the whole URL or just its query string; both are percent-decoded. This is the same escape hatch Claude Code offers for MCP auth. It costs nothing in security: the code is single-use, PKCE-bound and state-checked on both paths, so accepting it over the terminal is no weaker than accepting it over loopback. A callback belonging to a different login attempt is refused rather than exchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gw5rgVt1CQ8EYrLpZtQjdM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why hive has to own this
Buzz cannot configure an HTTP MCP server. I read the source rather than assuming:
buzz-acp'sMcpServeris{name, command, args, env}— stdio only, nourl, noheadersdeploy_payload_json) carries no MCP field at allThat's a boundary in Buzz's surface, not a gap to route around. If agents are to reach an HTTP MCP server, hive owns the configuration and the credential.
Until now the only route was two flat fields on the desktop provider config, which could express exactly one server and hardcoded its name to
"mcp".hive-spechas always hadmcp: Vec<Mcp>— the format was never the limit.What this adds
Specs are edited, not regenerated. The README calls generated specs "safe to edit and to commit" — but any generator that rewrites the document deletes comments, ordering, and blocks it doesn't know about.
toml_editkeeps them, sohive mcp addand a human editor can share one file. There's a test for exactly that.The login flow
Walks what the MCP spec defines: unauthenticated probe →
WWW-Authenticate: resource_metadata→ protected-resource metadata (RFC 9728) → authorization-server metadata (RFC 8414) → dynamic client registration (RFC 7591) → PKCE S256 → authorization code → token, resource-bound per RFC 8707.Why interactive rather than
secret putwith a minted token. A minted token carries the minter's authority and expires without warning. Verified against a live Parachute vault, whose access tokens last 900 seconds — an agent on a static token fails its first tool call fifteen minutes after setup, and the error reads as a broken MCP server.The refresh token is stored alongside, and it only helps because the broker serves credentials per connection rather than injecting them at container start. A design that baked credentials into the environment couldn't renew without recreating the container. That's an existing architectural choice paying off.
refreshreads through the broker's grant-checked path under a namedhive-cligrant rather than reaching around it, so a manual renewal shows in the audit log exactly like an agent's own fetch.No new runtime dependencies
HTTP goes through
curl, for the same reasonshive_core::dockerdrives Docker through its CLI — no large dependency tracking a moving API, and every request is a command you can re-run by hand. base64url is forty lines rather than a crate. Onlytoml_editandsha2were added, both already in the workspace.Verified against a live vault
serverInfo: parachute-vault/unforced)refreshproduced a genuinely different tokenKnown limitation
The loopback redirect needs a browser that can reach the host's
127.0.0.1, so a headless box needs someone at its screen. The RFC 8628 device grant would fix it — you'd get a code and type it anywhere — but Parachute advertises onlyauthorization_codeandrefresh_tokentoday. Noted rather than worked around.🤖 Generated with Claude Code
https://claude.ai/code/session_01Gw5rgVt1CQ8EYrLpZtQjdM