Session log: MCP persistence + Claude Code OAuth bug + mcp-remote shim
Document the five-layer breakage we unwound (WDF block rules, rmcp host allowlist, our middleware intercepting OAuth probes, Claude Code ignoring static bearer, mcp-remote --allow-http) and the working stdio-shim recipe.
This commit is contained in:
parent
799f507c3c
commit
b14b450577
1 changed files with 47 additions and 0 deletions
47
memory.md
47
memory.md
|
|
@ -51,6 +51,53 @@ Durable memory for this project. Read at session start, update before session en
|
|||
|
||||
## Session log
|
||||
|
||||
### 2026-05-26 — MCP persistence + Claude Code OAuth bug + `mcp-remote` shim
|
||||
|
||||
Set out to fix two paper cuts (port + token re-rolled every server restart, so firewall rules and `.mcp.json` had to be re-pasted). Ended up unwinding a multi-layer breakage in Claude Code's HTTP-MCP client.
|
||||
|
||||
**Persistence (the actual goal, in commit `799f507`):**
|
||||
- Added `McpPersistedConfig { port, token }` saved to `%APPDATA%\com.megaproxy.tiletopia\mcp.json`. Default port **47821** (IANA-unassigned range). `start_server` tries the saved port first, falls back to OS-picked + warning log if it's taken (saved port is preserved for the next attempt — transient conflicts shouldn't burn the user's firewall rule).
|
||||
- New `mcp_regenerate_token` command + **Regenerate** button in `McpPanel`. Confirms before rotating (existing clients break). If server is running, stops + restarts with the new token so the live auth middleware picks it up.
|
||||
- Token loaded on every `start_server`, so `McpState.bearer_token` is always in sync with `mcp.json`.
|
||||
|
||||
**The chain of failures (each fix exposed the next layer):**
|
||||
1. **WSL → Windows TCP timeouts.** User had auto-created Windows Defender Firewall **Block (Public)** rules for `tiletopia.exe` from earlier launches. Block rules win over Allow rules in WDF. Fix: nuke all `tiletopia*` rules, create one `Allow Any-profile LocalPort 47821` rule. Confirmed working with curl 401 from Windows + WSL.
|
||||
2. **rmcp DNS-rebinding allowlist** (`StreamableHttpServerConfig.allowed_hosts` defaults to `["localhost", "127.0.0.1", "::1"]`). WSL clients hit via the gateway IP `172.x.x.1`, which isn't in the list — rmcp logged `rejected request with disallowed Host header`. Fix: `.disable_allowed_hosts()` on the config. Bearer auth handles the real gatekeeping; we're not in a browser context.
|
||||
3. **Bearer auth middleware intercepted OAuth-discovery probes.** Claude Code probes `/.well-known/oauth-protected-resource`, `/.well-known/oauth-authorization-server`, `/register`, etc. before sending the static bearer. Our middleware was returning `401 + WWW-Authenticate: Bearer` on those paths — Claude interpreted that as "OAuth supported" and abandoned the static bearer in `.mcp.json`. Fix: skip auth enforcement for any path outside `/mcp` (`mcp.rs:bearer_auth`).
|
||||
4. **Claude Code's HTTP-MCP client is OAuth-only-ish.** Even with discovery paths returning bare 404s, Claude's `/mcp` UI hung in `Needs authentication`, never sent a real `POST /mcp`, and offered an "Authenticate" button that opened a (non-existent) browser flow. Logs confirmed: not a single `MCP request` after `MCP server listening`. The `headers: { Authorization: "Bearer ..." }` field IS the [documented mechanism](https://code.claude.com/docs/en/mcp), but it's broken in Claude Code per [#17152](https://github.com/anthropics/claude-code/issues/17152) (cosmetic UI bug) and [#46879](https://github.com/anthropics/claude-code/issues/46879) (auth requirement triggered by the *existence* of well-known endpoints, not by 401 responses).
|
||||
|
||||
**The working path: `mcp-remote` stdio shim.** Replace the HTTP server entry in `.mcp.json` with:
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"tiletopia": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y", "mcp-remote",
|
||||
"http://127.0.0.1:47821/mcp",
|
||||
"--allow-http",
|
||||
"--header", "Authorization: Bearer <token>"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
From Claude's perspective tiletopia is now stdio; `mcp-remote` proxies every JSON-RPC call over HTTP with the bearer baked in, bypassing Claude Code's HTTP-MCP machinery entirely. **`--allow-http` is required** because mcp-remote blocks non-HTTPS URLs except for `localhost`. The panel's "Copy config snippet" generates this shape now.
|
||||
|
||||
**Cleanups after the shim worked:**
|
||||
- Dropped the experimental `json_not_found` fallback handler (was added when we thought a JSON-bodied 404 would satisfy Claude's discovery parser; not needed once we went stdio).
|
||||
- Diagnostic `tracing::info!` for per-request auth state dropped to `tracing::debug!` (silent by default, available behind `RUST_LOG=tiletopia_lib::mcp=debug`).
|
||||
- README + help-overlay tip rewritten around the shim recipe + WSL firewall + WSL gateway-IP / mirrored-networking choice.
|
||||
|
||||
**Root-cause sequence worth remembering:** five distinct failures masked each other, and each new error message looked like a config bug. Methodical curl-from-WSL + log inspection was what cut through it — never trust the client's "auth failed" string without seeing whether the server was even reached.
|
||||
|
||||
Open follow-ups specific to this session:
|
||||
- **CLAUDE.md (root) still says Svelte 5** in stack — was noted in 2026-05-25's entry too; still not fixed.
|
||||
- **`.mcpb` bundle** would let Claude Desktop install the shim + bearer without hand-editing `.mcp.json`. Was already in the previous MCP TODO list; this session reinforces the need.
|
||||
- **Direct HTTP-MCP** can drop the shim once Claude Code fixes #17152 / #46879. Worth watching those issues.
|
||||
- **Panel could pre-flight check** for `npx` / Node presence on the WSL/host side and warn if missing. Currently the user only discovers the shim needs Node when Claude fails to spawn it.
|
||||
- **Server-side OAuth metadata** (RFC 9728 PRM at `/.well-known/oauth-protected-resource`) is the spec-blessed path but requires actually implementing OAuth dynamic client registration. Big scope; not worth it for the shim's lifetime.
|
||||
|
||||
### 2026-05-25 — Reflow bug fix + titlebar tidy-up
|
||||
|
||||
- **Bug: terminal text reflowing every few seconds.** User reported "redrawing every few seconds" with text changing lines. Added a `console.trace` inside the `ResizeObserver` in `XtermPane.tsx`, then expanded the diagnostic to log titlebar/pane-wrap/leaf/toolbar heights. Caught it: titlebar was oscillating between **34px and 50px** in sync with pane heights changing by ±15.4px (one button-row).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue