Repository navigation
β¨ feat: π remote-mcp registers a bearer-token MCP server per Claude Code profile - #100
Merged
Merged
Conversation
The file's other fixtures are invented names (octo-alpha, demo); the note value was the one that was not. Same assertion, same vocabulary as the rest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
β¦ode profile `claude mcp add` covers a public server with a static header. It does not cover a self-hosted one: a token that must not sit in a config file, on a host reachable only over a tailnet the Tailscale app may not be on. The token comes from 1Password at connect time through a headers helper. Measured on Claude Code 2.1.296: `claude mcp add-json` keeps `headersHelper` (`claude mcp add` has no flag for it), the helper may carry arguments, it is handed CLAUDE_CODE_MCP_SERVER_NAME and CLAUDE_CONFIG_DIR so one helper serves every server and profile, and it gets about ten seconds β after which the request goes out with no Authorization header at all. The tailnet path is a per-server loopback forwarder rather than a proxy variable. The MCP client does honour HTTPS_PROXY, but process-wide: the API connection went through it in the same test, and every command a session runs would inherit it. The forwarder is plain HTTP on loopback and TLS to the real name through the tailnet daemon's CONNECT proxy, so the certificate and Host the far side sees are unchanged. It holds no secret, refuses an Origin header or a non-loopback Host, and identifies the config it was started for so a stale one is replaced instead of trusted. It assumes a single-user machine, as the tailnet daemons' own loopback proxies do, and says so. Every name is machine-local git config: the script contains no server, and with nothing configured it is one status line and never calls op. remote-mcp-doctor joins `doctor` and reads config and loopback only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The three measurements that shape it, why the forwarder is per server, what an unconfigured clone gets, and the config block an overlay supplies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hadees
force-pushed
the
feat/remote-mcp
branch
from
October 12, 2026 20:23
78669ee to
baf3c54
Compare
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.
What
bin/remote-mcpregisters a remote, bearer-token MCP server in each Claude Code profile pinned for it, and relays one that lives on a tailnet through a per-server loopback forwarder. It names no server: URL, token references, tailnet and health path all come from machine-local git config (remote-mcp.<server>.*), which the overlays supply.remote-mcp-doctorjoinsdoctor.Why
claude mcp addcovers a public server with a static header. It does not cover a self-hosted one whose token must not sit in a config file and whose host is reachable only over a tailnet the Tailscale app may not be on.Measured on Claude Code 2.1.296
claude mcp add-jsonkeepsheadersHelper;claude mcp addhas no flag for it.connectreads the written entry back.CLAUDE_CODE_MCP_SERVER_NAMEandCLAUDE_CONFIG_DIR, so one helper serves every server and profile.Authorizationheader and the server answers 401.HTTPS_PROXY, but process-wide: the API connection used it in the same test, and every child process would inherit it. Hence a forwarder per server instead.Shape
Hostthe far side sees are unchanged. It holds no secret, refuses anOriginheader or a non-loopbackHost, and identifies the config it was started for so a stale one is replaced.headersstarts it on demand; nothing is installed or supervised..git/config: these keys decide where a token is sent.headersfails without callingop, nothing runs at apply.Also replaces one workset test fixture string with a name from the suite's own vocabulary.
Verified
bats tests/remote-mcp.bats: 26 tests (stub op/claude/tailnet/curl, sandboxed HOME and git config; the forwarder runs for real against a dead far side)π€ Generated with Claude Code