brain MCP: accept static Bearer without OAuth challenge for LAN/Tailscale CLI clients #9
Closed
opened 2026-05-13 05:00:42 +00:00 by mathias
·
1 comment
No Branch/Tag Specified
main
fix/pipeline-writes-158
fix/edit-path-io-192
fix/create-regular-188
fix/guard-special-179
fix/linkcheck-keeps-write-178
fix/write-dir-redirect-173
fix/tunnel-detect-174
fix/pipeline-root-157
w3/154-mcp-auth-wiring
fix/tunnel-traversal
test/in-brain-targets-171
fix/brain-scan-root-157
fix/graph-wiki-root-157
fix/vectorstore-root-157
fix/notes-root-157
fix/search-root-157
fix/brainwide-root-157
fix/raw-symlinks-153
feat/symlink-tripwire-157
fix/rest-auth-149
fix/123-brain-edit-envelope-check
feat/106-page-aware-extraction
fix/105-source-drift-on-main
fix/104-surface-merge-conflicts-on-main
agent/b1-compile-on-ingest
agent/104-surface-merge-conflicts
agent/105-source-drift
agent/107-loud-unconfigured-alerting
feat/brain-read-edit
fix/capture-i1-refusal-names-target
renovate/configure
feat/consolidate-single-harness
icebox/cmd-routing
feat/brain-pending-promote
fix/close-session-classification-refresh
fix/capture-summarywriter
v0.24.0
v0.23.1
v0.23.0
v0.22.0
v0.21.1
v0.21.0
v0.20.1
v0.20.0
v0.19.1
v0.19.0
v0.18.4
v0.18.3
v0.18.2
v0.18.1
v0.18.0
v0.17.0
v0.16.1
v0.16.0
v0.15.0
v0.14.0
v0.13.2
v0.13.1
v0.13.0
v0.12.1
v0.12.0
v0.11.2
icebox/cmd-routing-2026-07-01
v0.11.1
v0.11.0
v0.10.0
v0.9.0
v0.8.0
v0.7.0
v0.6.0
v0.5.0
v0.4.1
v0.4.0
v0.3.0
v0.2.0
v0.1.0
Labels
Clear labels
agent/ready
blocked/client
blocked/supplier
blocked/upstream
risk/high
risk/low
risk/medium
size/l
size/m
size/s
size/xs
status:dispatch
status:dispatched
status:review
status:reviewing
type/chore
type/decision
type/docs
type/feat
type/fix
type/ops
type/research
type/test
Meets the agent-ready contract; authorizes unattended execution
Waiting on the client
Waiting on a supplier
Waiting on an upstream project or vendor
Irreversible, security-relevant, or control plane
Reversible, local, test-covered
Touches shared state, deploys or data
Too big - split it
Under two days
Under half a day
Under an hour
Maintenance, no behaviour change
Human decision required; never agent/ready
Documentation only
New capability
Corrects broken behaviour
Infrastructure, deploy, runtime
Investigate; output is a finding, not code
Test coverage only
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: mathias/hyperguild#9
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
Running
claudeCLI on koala/flamingo with project.mcp.json:…fails with:
Workaround today: use claude.ai custom MCP connector (OAuth via Dex). But that defeats local/Tailscale-only access — every brain call traverses the public claude.ai tunnel.
Root cause
Brain MCP server (
ingestion-brainv0.1.0, code iningestion/+brain/) is configured with two auth modes:BRAIN_MCP_TOKENBearer (used by supervisor and other internal services)DEX_ISSUER_URL=https://auth.d-ma.be,MCP_AUDIENCE=claude-ai,MCP_RESOURCE_URL=https://brain-mcp.d-ma.be)On any 401, the server emits OAuth resource metadata pointing to Dex. Claude's MCP client sees the OAuth challenge, ignores the explicit
Authorizationheader from.mcp.json, and attempts RFC 7591 dynamic client registration. Dex is static-only → handshake dies.Static Bearer is wired and functional:
So it's the challenge ordering on 401 that breaks MCP clients using header auth.
Desired behavior
Auth middleware precedence:
Authorization: Bearer <token>and<token> == BRAIN_MCP_TOKEN→ authenticated, no OAuth challenge.MCP_AUDIENCE, issuer).WWW-Authenticate+/.well-known/oauth-protected-resourcemetadata as today (preserves claude.ai connector flow).Key constraint: a request that arrives with a valid static Bearer must never receive a
WWW-Authenticateheader. That header is what flips claude into OAuth-discovery mode.Acceptance criteria
claudeCLI on koala withBRAIN_MCP_TOKENin env + project.mcp.jsonconnects to brain MCP and lists tools (tools/listreturns non-empty)claude-aistatic client) still works for external/web sessions — manual test by re-adding the connectorWWW-Authenticateheader in 200 responses (regression guard)Test plan
Out of scope
Authorizationheader in.mcp.json(upstream, can't control)Context
infrarepo,k3s/apps/supervisor/ingestion-deployment.yaml(env),k3s/apps/auth/configmap.yaml(Dexclaude-aistatic client). No changes needed there — fix is server-side.gitea.d-ma.be/mathias/ingestion:189ff89c34e4a3b9950b1ddb7a798b2e47bedaedMCP_RESOURCE_URL/DEX_ISSUER_URLconsumers iningestion/andbrain/Shipped in
61b6247. Deployed imagegitea.d-ma.be/mathias/ingestion:61b6247df9f35bc87ca4ef406a5809280a29af8eis live on koala.Auth middleware precedence (now)
Inverted from the previous JWT-first ordering. Comment in
auth.godocuments the why so future-me doesn't reorder it.Live verification (2026-05-18, post-deploy)
Test 1 — valid static Bearer:
No
www-authenticateheader on the 200. ✓Test 2 — no auth, OAuth challenge emitted:
Header per RFC 9728 §6.2. claude.ai discovery path intact. ✓
Acceptance criteria
claudeCLI on koala withBRAIN_MCP_TOKENenv + project.mcp.json— tool list returns non-empty (verified via curl above; CLI uses the identical Bearer header)/.well-known/oauth-protected-resourcepayloadWWW-Authenticateheader on 200 (regression test inauth_test.go:69)Closing.