Post

Krill MCP & Claude Skill on macOS

Wire Claude Desktop on macOS up to a local krill-mcp server via the mcp-remote stdio bridge, and install the companion Krill Claude skill so every project gets it.

Krill MCP & Claude Skill on macOS

Setting up Claude Desktop on macOS

The main krill-mcp post covers the swarm-side install — apt install krill-mcp on a Pi, paste the bearer into a Custom Connector, done. That works as written for Claude Code and for Claude Desktop on Linux. On macOS, Claude Desktop’s Custom Connector UI rejects http:// URLs outright — it only accepts https://. krill-mcp itself speaks plain HTTP on port 50052; security comes from the PIN-derived bearer, not TLS. So the Custom Connector path doesn’t work on macOS for a LAN-local Krill swarm without a TLS terminator in front.

The clean way around it is the same path the wider MCP ecosystem uses for self-hosted servers: run mcp-remote as a stdio bridge between Claude Desktop and the HTTP MCP endpoint. No certs, no reverse proxy, just one entry in Claude Desktop’s config file.

This post is the macOS-specific recipe. It assumes you already followed the main post and have krill-mcp running on a Pi (or any Debian box on your LAN) with a valid bearer.


Why a bridge?

Three facts in tension:

  1. krill-mcp is HTTP. It’s a small Ktor service on :50052 that authenticates every /mcp request against a 64-char bearer derived from your cluster PIN. There’s no TLS listener — the wire contract is Bearer over plain HTTP, intended for LAN traffic.
  2. Claude Desktop on macOS requires HTTPS. The Custom Connector dialog won’t even let you save an http:// URL. (Linux Claude Desktop and Claude Code have no such restriction.)
  3. Claude Desktop does support stdio MCP servers via ~/Library/Application Support/Claude/claude_desktop_config.json. That path doesn’t go through the HTTPS-only UI.

mcp-remote is a tiny Node.js package that speaks MCP over stdio on one end and over HTTP on the other. Drop it into the stdio config and Claude Desktop gets the full MCP tool surface without ever knowing the upstream is HTTP.

1
2
3
4
5
┌──────────────────┐   stdio   ┌─────────────┐   HTTP/Bearer   ┌──────────────┐
│  Claude Desktop  │──────────▶│  mcp-remote │────────────────▶│   krill-mcp  │
│   (macOS app)    │  JSON-RPC │  (npx, on   │   :50052/mcp    │   (on Pi)    │
└──────────────────┘           │   your Mac) │                 └──────────────┘
                               └─────────────┘

1. Install Node.js

mcp-remote is distributed via npm and run with npx. On macOS the easiest source is Homebrew:

1
brew install node

Verify:

1
2
which npx           # /opt/homebrew/bin/npx (Apple Silicon) or /usr/local/bin/npx (Intel)
node --version      # v22+ is fine; anything modern works

You don’t need to run npm install for mcp-remote separately — npx -y mcp-remote@latest handles fetching and caching on first launch.

2. Install the companion Claude skill (once, for all projects)

The skill lives in the OSS repo. Clone it somewhere stable on your Mac, then symlink it into ~/.claude/skills/ so it’s picked up by every Claude install on the machine — and so git pull updates the skill without any further action:

1
2
3
cd ~/Code   # or wherever you keep source trees
git clone https://github.com/bsautner/krill-oss.git
ln -s "$(pwd)/krill-oss/krill-mcp/skill/krill" ~/.claude/skills/krill

Verify:

1
ls -l ~/.claude/skills/krill   # should show the symlink target

Pulling updates is just git pull in the cloned repo — the symlink keeps your skill current with whatever’s on main. If you previously copied the skill (rather than symlinked), remove the static directory first or the symlink command will fail.

3. Grab the bearer token

On the Pi running krill-mcp:

1
sudo krill-mcp-token

That prints the bearer and the Custom Connector URL. The bearer is 64 hex characters. Copy it — you’ll paste it into the next step.

If you’ve never seen krill-mcp-token, you’re on an older build; the bearer is also the contents of /etc/krill-mcp/credentials/pin_derived_key on the Pi. Same value either way.

4. Wire up Claude Desktop’s config

Quit Claude Desktop fully (⌘Q — closing the window isn’t enough; the menu bar icon needs to be gone) so it doesn’t overwrite your edits.

Edit ~/Library/Application Support/Claude/claude_desktop_config.json. If the file doesn’t exist yet, create it. If it does, back it up first and merge the new block into the existing JSON:

1
cp ~/Library/Application\ Support/Claude/claude_desktop_config.json{,.bak.$(date +%Y%m%d-%H%M%S)}

Add (or merge) an mcpServers block. Replace <your-host> with whatever resolves to the Pi on your LAN (e.g. pi-krill-05.local if you’re using mDNS) and <bearer> with the token from step 3:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
  "mcpServers": {
    "krill": {
      "command": "/opt/homebrew/bin/npx",
      "args": [
        "-y",
        "mcp-remote@latest",
        "http://<your-host>:50052/mcp",
        "--allow-http",
        "--transport",
        "http-only",
        "--header",
        "Authorization:Bearer <bearer>"
      ]
    }
  }
}

Three details that matter:

  • Use the absolute path to npx. Claude Desktop launches subprocesses with a minimal PATH that doesn’t include Homebrew. /opt/homebrew/bin/npx on Apple Silicon, /usr/local/bin/npx on Intel. If you skip the absolute path you’ll get a silent “MCP server failed to start” with no useful error.
  • --allow-http is required. mcp-remote refuses HTTP URLs by default; this flag opts in. Safe for LAN traffic to your own Pi; do not use it for anything traversing the public internet.
  • --transport http-only tells mcp-remote to skip the legacy SSE handshake and go straight to MCP Streamable HTTP. krill-mcp is Streamable-HTTP only, and without this flag you’ll see extra error noise on startup (harmless but ugly).

If you already have other entries under mcpServers, just add krill alongside them. The whole file is a single JSON object — make sure the brackets and commas balance after your edit. A quick sanity check:

1
python3 -m json.tool ~/Library/Application\ Support/Claude/claude_desktop_config.json > /dev/null && echo OK

The config file is mode 600 by default — owner-only — which is the right place to keep the bearer.

5. Launch Claude Desktop

Reopen Claude Desktop. The first launch runs npx -y mcp-remote@latest once and populates the npx cache (~5–10 seconds the first time, instant after that).

Verify the connector is live: open a new chat and ask Claude something like “list my Krill servers” — Claude should call list_servers and come back with the swarm. The krill skill activates automatically as soon as your prompt mentions a Krill swarm, a node type, or a dashboard.


What you’ll see in the logs

mcp-remote’s stderr ends up in Claude Desktop’s MCP log pane. Expect a few lines that look alarming but are benign:

1
Error from remote server: StreamableHTTPError: Failed to open SSE stream: Not Acceptable (406)

mcp-remote always tries to open an SSE notification channel alongside the main JSON-RPC stream, even with --transport http-only. krill-mcp doesn’t implement SSE notifications — it only does the per-call request/response over POST /mcp — so the server returns 406 and mcp-remote logs it as an error. Tool calls are completely unaffected; this is just noise.

If you want to see the actual JSON-RPC traffic, the logs in Claude Desktop → Settings → Developer → MCP Logs show every Local→Remote and Remote→Local frame.

Rotating the bearer

If you change the cluster PIN on a Pi, the bearer rotates with it. After the new PIN is in place, repeat step 3 to print the new token, swap it into the Authorization:Bearer ... arg in claude_desktop_config.json, and restart Claude Desktop. There’s no other state to clean up.

Troubleshooting

  • “MCP server failed to start” on launch. Almost always command not found — use the absolute path to npx (step 4).
  • tools/list works but tool calls hang. The Pi’s :50052 is open from your Mac (try curl -i http://<host>:50052/healthz) but the bearer is wrong. Re-run sudo krill-mcp-token on the Pi and confirm the value matches.
  • curl to :50052 from your Mac times out. Firewall or routing. mDNS names need the .local suffix; if mDNS isn’t resolving (corporate networks sometimes block it), use the Pi’s IP directly.
  • Claude finds tools but doesn’t seem to understand the swarm. The skill isn’t loaded. Check ~/.claude/skills/krill exists and that you fully quit and relaunched Claude Desktop after creating the symlink.

What this gets you

Once it’s wired up, every prompt that touches a Krill swarm — the four examples in the main post all work the same here — flows through the bridge with no perceptible latency. Claude Desktop on macOS gets parity with Claude Code and with Claude Desktop on Linux. The skill auto-activates on Krill-shaped prompts. And because the skill is symlinked into ~/.claude/skills/, it’s picked up by every project on this Mac without per-project configuration.


Last verified: 2026-04-29

This post is licensed under CC BY 4.0 by Sautner Studio, LLC.