Repository navigation
feat(relay): forward local MCP tools and coding agents to Hub - #122
Merged
Merged
Conversation
Stamp every frame with a fixed EnvelopeVersion so hubs that predate protocol 2 still accept a newer relay's register, and negotiate the protocol in register/registered instead. Protocol 2 defines the tools and call frames, their limits, and the tool error codes. The relay offers [1,2], refuses a hub that picks a version it did not offer, and always advertises a concurrency limit of at least 1.
Add a user-only relay.mcp block naming stdio or streamable HTTP MCP servers. The relay starts each server, lists its tools, and sends them to protocol 2 hubs in a tools frame, re-listing on change and restarting a server that exits. Hub calls a tool by the server and tool names the relay listed; the command, URL, headers and environment never cross the wire. Stdio servers get a filtered environment and their own process group, each server has its own concurrency limit and call timeout, and a server serves only its owner unless marked shareable. A relay may now offer MCP servers without any providers.
Relay protocol 3 adds optional appTitle and appUrl fields to infer frames, which Hub sets to name where a request came from: its chat, `tap launch <harness>`, or its API. Both are validated (printable ASCII, an absolute http(s) URL, length limits) because they become HTTP headers. The relay now offers protocols 1 through 3 and passes the app to OpenRouter providers only, as X-Title and HTTP-Referer, so usage is attributed in OpenRouter's logs.
Remove the owner-side shareable flag from relay.mcp config, the tools frame (ToolServer.Shareable) and the call frame (Call.Shared), and stop refusing shared calls on the relay. Who may call a relayed MCP server is now decided only on Hub, where its owner enables it and shares it with namespaces or people. Incompatible change: the relay.mcp.<name>.shareable option no longer exists. A config that still sets it loads, and the key is ignored.
tap mcp now connects in the background to the selected Hub's /mcp/relay endpoint and serves the caller's relayed MCP tools (mcp__<owner>__<server>__<tool>) beside the KEG tools, refreshing every 60s and whenever Hub announces a change. With TAP_AGENT set it asks as that agent, so Hub applies the agent's tool gate. Relayed tools carry no KEG authority, so they bypass the flight gate, the agent allowlist and the public-response rewrite. TAP_RELAY_TOOLS=off turns them off, as does running inside a delegated runner.
tap runner serve is a stdio MCP server with one tool per coding agent CLI installed on this machine: claude_code_run, codex_run, opencode_run and pi_run. Each runs a task non-interactively in a working directory under the allowed roots (TAP_RUNNER_ROOTS), within a timeout (TAP_RUNNER_TIMEOUT), and returns the agent's reply and session id. TAP_RUNNER_DEPTH stops a delegated agent from delegating again. Every runner keeps its own permission settings.
A new relay.runners block turns on built-in MCP servers, all off by default: claude, codex, opencode and pi each run `tap runner serve --runner NAME`, and claude-tools runs `claude mcp serve`. Each is offered only when its CLI is on PATH, and a relay.mcp entry of the same name replaces it, or removes it with enabled: false. A relay can now start with built-in servers alone, and the config schema accepts a disabled relay.mcp entry with no endpoint and no longer lists shareable.
tap launch claude now defaults to split mode: Claude Code keeps the user's own Claude login for Claude models, and Hub catalog models join its /model picker. The forwarder routes each request by model under a /t/<key>/ path prefix, and the user's Claude credential never reaches Hub. --hub restores Hub-only inference and --subscription leaves models alone. A Hub agent's subagents become Claude Code --agents limited to their own tools plus --subagent-builtins, and the forwarder sends X-Tap-Harness so Hub can label requests by harness. Incompatible change: `tap launch claude` previously sent every model through Hub by default; pass --hub for that behaviour.
node_read now returns content_hash and meta_hash beside the combined hash, and node_edit takes expected_content_hash and expected_meta_hash, so editing one part never conflicts with a concurrent edit to the other. Edit results and conflict errors carry the new or current part hashes, and the remote keg sends and decodes them; node_delete and node_move keep the combined hash. node_read and the full keg_settings_read also send each document once, in structured content, with a short text summary. Incompatible change: node_edit no longer accepts expected_hash; pass expected_content_hash and/or expected_meta_hash from node_read.
Describe where relayed tools appear (Hub's chat, /mcp/relay and tap mcp), how owners enable and share servers on Hub, with a checklist for a shared server that does not show up, protocol 3 app attribution, and the built-in runner servers and their tool arguments.
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.
Summary
Lets a
tap relayoffer MCP tools, including the machine's coding agents, to Hub, and letstap mcpserve the caller's relayed tools. Also includes a split-inference mode fortap launch claudeand per-part hashes fornode_edit.Relay
relay.mcpblock names stdio or streamable-HTTP MCP servers. The relay runs them, lists their tools to Hub, and runs calls. Commands, URLs, headers and environment never cross the wire.shareableflag,ToolServer.ShareableandCall.Sharedare removed. Owners enable and share servers on Hub.appTitle/appUrlon infer frames, validated and sent to OpenRouter providers asX-Title/HTTP-Referer.relay.runnersblock (all off by default) offersclaude,codex,opencode,piandclaude-toolswhen their CLIs are on PATH.tap runner serve: a stdio MCP server with one tool per installed coding agent CLI. Runs a task in an allowed root with a timeout, and guards against recursive delegation (TAP_RUNNER_ROOTS,TAP_RUNNER_TIMEOUT,TAP_RUNNER_DEPTH).tap mcp: proxies Hub's/mcp/relayso relayed tools (mcp__<owner>__<server>__<tool>) appear beside the KEG tools. WhenTAP_AGENTis set, Hub applies that agent's gate. Disable withTAP_RELAY_TOOLS=off.tap launch claude: split mode is now the default. Claude models use the user's own Claude login, Hub catalog models join the/modelpicker, and the Claude credential never reaches Hub.--hubrestores Hub-only inference. Hub agent subagents become--agentsscoped to their tools.node_editpreconditions:node_readreturnscontent_hashandmeta_hash, andnode_edittakesexpected_content_hash/expected_meta_hash, so editing content and metadata at the same time no longer conflicts. Reads send each document once, in structured content.Incompatible changes (v0.x)
relay.mcp.<name>.shareableis gone; the key is ignored.node_editno longer acceptsexpected_hash.tap launch claudedefaults to split mode; pass--hubfor the old behaviour.Hub
Hub support is in jlrickert/tapper-hub
feat/relay-mcp-tools. It will pin the release that contains this.Test plan
go build ./...,go vet ./...,go test ./...go test -race ./pkg/keg ./pkg/tapper ./pkg/paritytask lint:docs,task lint:fs;task render-integrationsleaves no diff