Build & push patched image / build (push) Failing after 1m13s
The wrapper sed-patches the two host-wide OAuth 2.1 discovery route registrations in MetaMCP's compiled backend bundle so spec-compliant MCP clients do not force an interactive login flow on public endpoints. See README for full rationale (RFC 9728, MCP Authorization spec 2026-03-26 revision) and the diagnostic evidence. Guarded with grep pre/post-conditions so a future upstream release that renames or restructures these routes fails the build loudly rather than silently shipping an unpatched image.
100 lines
3.6 KiB
Markdown
100 lines
3.6 KiB
Markdown
# metamcp-patched
|
|
|
|
A thin wrapper around [`ghcr.io/metatool-ai/metamcp`](https://github.com/metatool-ai/metamcp)
|
|
that hides the host-wide OAuth 2.1 discovery endpoints so spec-compliant MCP
|
|
clients don't force an interactive login on public endpoints.
|
|
|
|
## The problem this fixes
|
|
|
|
MetaMCP unconditionally publishes RFC 9728 protected-resource metadata and
|
|
RFC 8414 authorization-server metadata at:
|
|
|
|
- `/.well-known/oauth-protected-resource`
|
|
- `/.well-known/oauth-authorization-server`
|
|
|
|
Both documents describe the whole MetaMCP host as OAuth-protected — even
|
|
if every configured endpoint is public. Per the MCP Authorization spec
|
|
(2026-03-26 revision), a spec-compliant client — Claude.ai's remote-MCP
|
|
integration, Claude Desktop, and the reference SDKs — probes these paths
|
|
on connect. When it sees them, it kicks off the OAuth flow (with dynamic
|
|
client registration and PKCE) **before** calling any tool. Users get an
|
|
interactive login page even when trying to connect to an endpoint that
|
|
doesn't require auth.
|
|
|
|
The route mounts live in a compiled bundle with no env-var gate — the
|
|
line `app.use(oauth_default)` in `apps/backend/dist/index.js` pulls the
|
|
metadata router in unconditionally. There is no admin toggle for it in
|
|
the shipping build.
|
|
|
|
This repo produces a wrapper image that sed-patches the two discovery
|
|
route registrations to bogus paths, so Express falls through to its
|
|
default 404 for those requests. Nothing else in MetaMCP changes.
|
|
|
|
Endpoints that DO enforce auth still 401 correctly; they just can't be
|
|
consumed via OAuth dynamic-discovery clients any more (consume them via
|
|
a bearer token in the MCP client's config instead).
|
|
|
|
## Using the pre-built image
|
|
|
|
```yaml
|
|
# docker-compose.yml
|
|
services:
|
|
metamcp:
|
|
image: git.jpaul.io/justin/metamcp-patched:latest
|
|
pull_policy: always
|
|
# ... rest of your existing metamcp service config unchanged
|
|
```
|
|
|
|
## Or build from source locally
|
|
|
|
```yaml
|
|
services:
|
|
metamcp:
|
|
image: metamcp-patched:local
|
|
pull_policy: never
|
|
build:
|
|
context: https://git.jpaul.io/justin/metamcp-patched.git
|
|
# or a local clone / vendored path
|
|
```
|
|
|
|
Bump upstream at any time with:
|
|
|
|
```bash
|
|
docker compose build --pull metamcp && docker compose up -d metamcp
|
|
```
|
|
|
|
The Dockerfile has `grep` guards that fail the build loudly if a future
|
|
upstream release renames or restructures the OAuth routes, so a bad bump
|
|
never silently ships an unpatched image.
|
|
|
|
## Verify it worked
|
|
|
|
```bash
|
|
UA="Mozilla/5.0 (X11; Linux x86_64) Firefox/120.0"
|
|
|
|
# want 404, was 200 pre-patch
|
|
curl -sSo /dev/null -w "%{http_code}\n" -H "User-Agent: $UA" \
|
|
"https://<your-metamcp-host>/.well-known/oauth-protected-resource"
|
|
curl -sSo /dev/null -w "%{http_code}\n" -H "User-Agent: $UA" \
|
|
"https://<your-metamcp-host>/.well-known/oauth-authorization-server"
|
|
|
|
# want 200 (unchanged) — proves you didn't collaterally break the endpoint
|
|
curl -sSo /dev/null -w "%{http_code}\n" -H "User-Agent: $UA" \
|
|
-H "Accept: application/json, text/event-stream" \
|
|
-X POST -H "Content-Type: application/json" \
|
|
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"diag","version":"1.0"}}}' \
|
|
"https://<your-metamcp-host>/metamcp/<a-public-endpoint>/mcp"
|
|
```
|
|
|
|
## Delete this repo the day upstream ships a toggle
|
|
|
|
The right fix belongs in MetaMCP itself — either an env var like
|
|
`DISABLE_OAUTH_DISCOVERY=true`, or per-endpoint RFC 9728 metadata
|
|
scoping so public endpoints don't advertise. Track upstream at
|
|
<https://github.com/metatool-ai/metamcp>.
|
|
|
|
## License
|
|
|
|
MIT for the wrapper contents (Dockerfile, workflow, docs). The base
|
|
image and everything it contains remain under their upstream licenses.
|