Repository navigation
docs: Claude Desktop and other MCP client setup (closes #17) - #28
Conversation
Extends the tunnel + bearer-token-by-reference pattern from #9/PR #18 to Claude Desktop, Cursor, VS Code, and Windsurf. Claude Desktop needs a bridge: its config registers stdio servers only, and the Connectors UI offers OAuth alone for custom remote servers with no bearer or custom-header field (anthropics/claude-ai-mcp#112, closed as not planned). mcp-remote wrapped in /bin/sh keeps the token in ~/.nanokvm-token rather than in the config JSON, which is what mcp-remote's own README suggests. The single quotes are load-bearing for the same reason as the Claude Code flow: mcp-remote does its own ${VAR} substitution from its environment, so the reference has to survive the shell. Claude Desktop not expanding ${VAR} in args works in our favour here. Cursor, VS Code, and Windsurf speak streamable HTTP natively. Their schemas differ in ways that fail confusingly: VS Code keys on "servers" and needs an explicit "type": "http", Windsurf uses "serverUrl" rather than "url", and each has its own interpolation syntax. VS Code's promptString input is the best of them — the token goes to secret storage and never reaches the file. Also notes the GUI-launch gotcha: an editor started from Finder inherits launchd's environment, not the shell's, so ${env:...} may resolve empty where it works fine for Claude Code in a terminal. Verified: all five README JSON blocks parse; the Claude Desktop args string was extracted from that JSON and run through /bin/sh with npx stubbed, confirming mcp-remote receives the literal ${VAR} reference and a populated environment. --allow-http is omitted because mcp-remote already exempts 127.0.0.1. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
Keeps the stacked branch current with the corrections made on PR #26 (ControlPath=none, restricted SSH key, plist XML validity). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Summary
Extends the tunnel + bearer-token-by-reference pattern from #9 / PR #18 to Claude Desktop, Cursor, VS Code, and Windsurf.
Claude Desktop needs a bridge
It can't reach this daemon directly, and both reasons are worth knowing before reaching for the config file:
url/headersform.So the documented setup wraps
mcp-remotein/bin/sh, which keeps the token in~/.nanokvm-tokeninstead of writing it into the config JSON —mcp-remote's own README suggests anenvblock, which puts the secret in the file. The single quotes are load-bearing for the same reason as the Claude Code flow, with a different consumer:mcp-remotedoes its own${VAR}substitution from its environment, so the reference has to survive the shell intact. Claude Desktop's well-known refusal to expand${VAR}inargsworks in our favour here.Two things I checked rather than copied from a guide:
--allow-httpis omitted.mcp-remotealready exemptslocalhost/127.0.0.1from its HTTPS check (source), so the flag buys nothing and would relax that check for every other host.npxpath is called out. Claude Desktop launches servers from the GUI, not a login shell, so a barenpxis the most common way this config silently fails to start.The other three speak HTTP natively
No bridge needed — but their schemas differ in ways that fail confusingly, so each gets its exact form:
~/.cursor/mcp.json,.cursor/mcp.jsonurl, notypeneeded,${env:VAR}.vscode/mcp.json, user profileservers, explicit"type": "http",${input:id}~/.codeium/windsurf/mcp_config.jsonserverUrlnoturl,${env:VAR}or${file:}VS Code's
promptStringinput is the best of the three for this project's purposes — it prompts once, stores the value in secret storage, and the token never reaches the file.Also documents a cross-cutting gotcha:
export NANOKVM_MCP_TOKENin a shell profile works for Claude Code because it runs in your terminal, but a GUI-launched editor inherits launchd's environment, not your shell's, so${env:...}can resolve empty.launchctl setenv"fixes" it by exporting the token to every process you own, which is the worse trade — VS Code's${input:}and Windsurf's${file:}sidestep it properly.One honest caveat in the text: Windsurf's
${file:}reads the file verbatim, so the token file's trailing newline has to be stripped or auth fails in a way that reads like a bad token.Verification
json.loads).argsstring was extracted from that JSON and executed through/bin/sh -cwithnpxswapped for an argv dumper.mcp-remotereceivesAuthorization: Bearer ${NANOKVM_MCP_TOKEN}as a literal reference — not empty, not pre-expanded — and theVAR=$(cat ...)prefix populates the child environment thatmcp-remotesubstitutes from.Not tested against a running Claude Desktop or the device — no tunnel to the hardware from this workspace. The shell/JSON mechanics are verified; the round trip through each client UI is not.
Docs only; no code, no
CHANGELOG.md(release PR in flight).Closes #17.
🤖 Generated with Claude Code