Repository navigation
docs: day-to-day Claude Code setup (closes #9) #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -182,34 +182,79 @@ tunnel/port-forward — see [Security](#security-model) for why the LAN address | |
| discouraged) and `<token>` with the value of `NANOKVM_MCP_TOKEN`, or the token printed to | ||
| `/data/nanokvm-mcp/daemon.log` if you didn't set one. | ||
|
|
||
| ### Over an SSH tunnel | ||
| ### Claude Code over an SSH tunnel (tested end-to-end) | ||
|
|
||
| With the default loopback bind, forward the port from your machine (the keepalive matters | ||
| — an idle MCP session will otherwise drop the tunnel): | ||
| This is the setup validated against real hardware. It uses an SSH master | ||
| connection that carries the port-forward and authenticates once; every later | ||
| `ssh`/`scp` rides the same socket without re-prompting. (The master exits after | ||
| an hour *idle*, so this is a per-session flow, not a permanent one — a durable | ||
| path is tracked in [#16](https://github.com/tylervick/nanokvm-mcp/issues/16).) | ||
|
|
||
| **1. Open the master connection + tunnel** (one password prompt): | ||
|
|
||
| ```sh | ||
| ssh -N -L 8080:127.0.0.1:8080 -o ServerAliveInterval=30 root@<device> | ||
| ssh -f -N -M -S /tmp/nkvm.sock -o ControlPersist=3600 -o ServerAliveInterval=30 \ | ||
| -o ExitOnForwardFailure=yes \ | ||
| -o PubkeyAuthentication=no -o PreferredAuthentications=password -o IdentitiesOnly=yes \ | ||
| -L 8080:127.0.0.1:8080 root@<device> | ||
| ``` | ||
|
|
||
| Then the endpoint is `http://127.0.0.1:8080/` locally. Keep the token out of your | ||
| shell history: export it once (`export NANOKVM_MCP_TOKEN=...` with a leading space, | ||
| or `export NANOKVM_MCP_TOKEN=$(cat token-file)`) and reference the variable so the | ||
| literal value never appears on a command line. Verify with MCP Inspector: | ||
| `ExitOnForwardFailure=yes` makes a failed port-forward (e.g. 8080 already taken | ||
| by a stale tunnel) fail loudly here instead of backgrounding a broken master. | ||
|
|
||
| The `PubkeyAuthentication=no` trio matters: the stock firmware has no authorized | ||
| keys, and an ssh-agent holding several keys will exhaust dropbear's auth attempts | ||
| before password auth is ever offered ("Too many authentication failures"). | ||
|
|
||
| Check whether the tunnel is still alive later (`ControlPersist=3600` means the | ||
| master exits after 3600 s with no client connections — active use keeps it open): | ||
|
|
||
| ```sh | ||
| npx @modelcontextprotocol/inspector --cli http://127.0.0.1:8080/ \ | ||
| --transport http --header "Authorization: Bearer ${NANOKVM_MCP_TOKEN}" --method tools/list | ||
| ssh -S /tmp/nkvm.sock -O check root@<device> # "Master running" = alive | ||
| ``` | ||
|
|
||
| **2. Pull the bearer token to a local file** — by reference from here on, so the | ||
| secret never enters shell history or configs: | ||
|
|
||
| ```sh | ||
| (umask 077; ssh -S /tmp/nkvm.sock root@<device> \ | ||
| 'sed -n "s/^NANOKVM_MCP_TOKEN=//p" /root/nanokvm-mcp/nanokvm-mcp.env' \ | ||
| > ~/.nanokvm-token) && test -s ~/.nanokvm-token || echo "no token in env file" | ||
| ``` | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| Or add it to Claude Code — single quotes matter: they pass the `${VAR}` reference | ||
| through unexpanded, and Claude Code's `.mcp.json` expands it at connection time, | ||
| so neither your shell history nor the saved config ever contains the secret: | ||
| The `test -s` guard catches the case where no explicit `NANOKVM_MCP_TOKEN` is | ||
| set on the device (the generated-token path prints to `daemon.log` instead — | ||
| but set an explicit one; see the [first-run checklist](#first-run-checklist-learned-on-real-hardware)). | ||
|
|
||
| **3. Register with Claude Code** — the single quotes are load-bearing: they pass | ||
| the `${VAR}` reference through unexpanded, and Claude Code's `.mcp.json` expands | ||
| it at connection time, so the saved config holds the reference, not the secret: | ||
|
|
||
| ```sh | ||
| export NANOKVM_MCP_TOKEN=$(cat ~/.nanokvm-token) # add to your shell profile | ||
| claude mcp add --transport http nanokvm http://127.0.0.1:8080/ \ | ||
| --header 'Authorization: Bearer ${NANOKVM_MCP_TOKEN}' | ||
| ``` | ||
|
|
||
| **4. Use it.** Start a *fresh* Claude Code session (new MCP servers are picked up | ||
| at session start) from a shell where the variable is set, and ask for a | ||
| screenshot or device info. In the default read-only mode the 7 read-only tools | ||
| appear; mutating tools require `NANOKVM_MCP_READONLY=false` in the device env | ||
| plus an init-script restart, after which they show up annotated as destructive — | ||
| Claude asks before using them, and every call lands in the audit log. | ||
|
|
||
| To sanity-check the endpoint without Claude, use MCP Inspector: | ||
|
|
||
| ```sh | ||
| npx @modelcontextprotocol/inspector --cli http://127.0.0.1:8080/ \ | ||
| --transport http --header "Authorization: Bearer ${NANOKVM_MCP_TOKEN}" --method tools/list | ||
| ``` | ||
|
Comment on lines
+246
to
+251
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# Show the relevant README section with line numbers
sed -n '220,255p' README.md
# Find other Inspector references in the repo
rg -n "modelcontextprotocol/inspector|MCP Inspector|NANOKVM_MCP_TOKEN|Authorization: Bearer" -S .Repository: tylervick/nanokvm-mcp Length of output: 5632 🌐 Web query:
💡 Result: The Citations:
🌐 Web query:
💡 Result: The Citations:
🌐 Web query:
💡 Result: To configure an authorization bearer token in the Model Context Protocol (MCP) Inspector, you can use the web interface or command-line arguments depending on your setup. Web Interface Configuration The Inspector UI allows you to add custom headers to your connection settings to support bearer token authentication [1][2][3]. 1. Open the MCP Inspector interface [4][5]. 2. Click the Configuration button (or "Add Header" in the connection setup area) [6][7][2]. 3. Add a new header entry [2]. 4. Set the Header field to Authorization and the Value field to Bearer YOUR_TOKEN [2][3]. Recent updates have enhanced header management, allowing for multiple custom headers and flexible configurations [1]. The UI now supports both form-based and JSON editor modes for these headers [1]. These settings are stored in your browser's local storage for persistence between sessions [8][3]. Command-Line Configuration When starting the Inspector from the command line, you can pass custom headers directly using the --header argument [6][9]. Example command: npx Citations:
Avoid interpolating the token in the Inspector CLI example. Shell expansion puts 🤖 Prompt for AI Agents
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Caveat added in 5f6613c rather than restructuring: per your own research above, Inspector has no file/config-based header mechanism — --header interpolation is its official method — and the primary flow (claude mcp add) already keeps the secret out of argv via the unexpanded ${VAR}. The note flags the ps exposure and points shared-host users at curl's @file header form. |
||
|
|
||
| Note the shell expands the token into the process's arguments here (Inspector | ||
| has no file-based header option), so it's briefly visible to `ps` — fine on a | ||
| single-user machine, but on a shared host prefer checking with curl's `@file` | ||
| header form instead. | ||
|
|
||
| ## Tools | ||
|
|
||
| 14 tools are registered by default (7 in read-only mode): | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.