Skip to content

Top-level Remote mcp: failing to load static mcp-* credentials in Apple container #335

@PatrickSpieker

Description

@PatrickSpieker

Hi folks! Struggling to use any remote mcp server with moat; wondering if I'm somehow missing something really simple or if there's a broader issue here (apologies if it's the former!).

The failure is seems to be host-side, before any upstream MCP server is contacted. It is not specific to a given MCP server: I reproduced the same failure with mcp-linear as I did with mcp-render.

Observed behavior

Trying to set up the Render MCP as in:

mcp:
  - name: render
    url: https://mcp.render.com/mcp
    auth:
      grant: mcp-render
      header: Authorization

in the moat.yaml file.

Moat generates a relay entry in /home/moatuser/.claude.json like:

{
  "type": "http",
  "url": "http://192.168.64.1:19080/mcp/<token>/render",
  "headers": {
    "Authorization": "moat-stub-mcp-render"
  }
}

But probing that relay from inside the running container returns:

HTTP/1.1 500 Internal Server Error
MOAT: Failed to load credential for 'render'. Grant: mcp-render. Run: moat grant mcp render

Same thing happens with Linear:

HTTP/1.1 500 Internal Server Error
MOAT: Failed to load credential for 'linear'. Grant: mcp-linear. Run: moat grant mcp linear

Why this looks like a Moat relay bug

These all checked out:

  1. moat grant show mcp-render and moat grant show mcp-linear succeeded on the host.
    ~/.moat/credentials/mcp-render.enc and mcp-linear.enc existed.
  2. The host Keychain encryption key was readable.
  3. The encrypted credential files were decryptable on the host.
  4. The container got the correct relay URL and stub header.
  5. ~/.moat/proxy/runs.json registered the remote MCP server under mcp_servers.
  6. There were no outbound upstream requests to mcp.render.com when the relay failed.
  7. There were no normal credential injected log lines for mcp-render / mcp-linear.

Working paths on the same machine:

  1. github and claude grants worked through normal proxy credential injection.
  2. sandbox-local stdio MCPs worked (playwright is the working example for me)
    So the break appears isolated to Moat’s top-level remote mcp: relay path for static mcp-* credentials.

Steps to Repro

Store a static MCP credential:

moat grant mcp render
moat grant mcp linear

Add a remote MCP to moat.yaml:

mcp:
  - name: linear
    url: https://mcp.linear.app/mcp
    auth:
      grant: mcp-linear
      header: Authorization

Start a fresh Claude run: moat claude ./

Inside the container, inspect the generated relay:

python3 - <<'PY'
import json
cfg = json.load(open("/home/moatuser/.claude.json"))
print(json.dumps(cfg["mcpServers"], indent=2))
PY

Probe the relay directly:

python3 - <<'PY'
import json, subprocess
cfg = json.load(open("/home/moatuser/.claude.json"))
server = cfg["mcpServers"]["linear"]
url = server["url"]
header = server["headers"]["Authorization"]
res = subprocess.run(
    ["curl", "-sS", "-D", "-", "-o", "/tmp/body.txt", url, "-H", f"Authorization: {header}"],
    text=True,
    capture_output=True,
)
print(res.stdout)
print(open("/tmp/body.txt").read())
PY

Actual result:

HTTP/1.1 500 Internal Server Error
MOAT: Failed to load credential for 'linear'. Grant: mcp-linear. Run: moat grant mcp linear

Expected result
Moat should load the stored mcp-* credential and forward the request upstream through the relay, just as documented for top-level remote mcp: servers.

Other things I tried

  1. Re-granting the credential did not help.
  2. Restarting the proxy daemon did not help.
  3. Changing the stored Render token from Bearer ... to raw token did not help.
  4. This is why I switched the repo to a workaround: run render-mcp-server locally inside the container as a sandbox-local stdio MCP instead of using the remote relay.

Let me know if there's any other info I can provide! Thanks.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions