agentic-community / agentic-community/mcp-gateway-registry

MCP proxy hop does not apply the server `auth_scheme` credential or `custom_headers` (FAQ says it does); no operator-side way to set User-Agent on egress

Đang mở
#1,745 0 bình luận 0 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
Python
Star
912
Fork
234
Merge trung bình
1 ngày 11 giờ
Pull request đã merge (30 ngày)
62

Mô tả

## Summary

On the MCP proxy hop (`auth_server` `mcp_proxy`), a registered server's **Backend Authentication** credential (`auth_scheme` + `auth_credential`) and its **custom headers** (`custom_headers`, #1028) are **not** applied to the upstream request. They are only used by the registry for health checks and tool discovery (`registry/core/mcp_client.py`, `registry/health/service.py`).

Three consequences for operators fronting a third-party MCP server that expects a shared, operator-owned credential or a fixed header:

1. The FAQ `docs/faq/registering-auth-protected-servers.md` still states "**Request proxying** -- When clients connect through the gateway, the credential is injected into proxied requests". That is not what the code does, so operators configure `auth_scheme=bearer`, see the server turn healthy, and then every proxied call fails at the upstream.
2. `custom_headers` were proposed in #1028 with "Also injected outbound when the gateway proxies to the backend MCP server" and with "Required `User-Agent` … values some backends enforce" as a motivating case. PR #1041 implemented registration, encryption, health/discovery and the Connect JSON, but touched no `auth_server` file, so the outbound part never landed.
3. The 1.30.0 generic HTTP proxy (#1628) introduced a **shared operator key** credential model with operator-injected headers (`_vend_generic_upstream_headers` / `_merge_generic_upstream_headers`). The MCP hop has no equivalent, so the same upstream behaves differently depending on which hop fronts it.

I understand from #1265 that `auth_scheme` was redefined as "the gateway acting as itself" (health checks) and that per-user egress is the preferred model (`egress_auth_mode` pat / oauth_user / obo_exchange). This issue is not asking to weaken that: it asks for (a) the documented behaviour to match the code, and (b) an explicit, opt-in way to apply operator-owned headers on the MCP hop, as the generic hop already allows.

## Environment

- MCP Gateway Registry 1.29.x, Helm (EKS-style deployment), `AUTH_PROVIDER=entra`, `EGRESS_AUTH_ENABLED` unset (default off).
- Upstream: a vendor MCP server that requires two fixed headers (an API key and a protocol-signal header), hosted behind Cloudflare with Browser Integrity Check enabled.

## Observed

- Registry health check / tool fetch to the upstream: sends `Authorization: Bearer ` (from `auth_scheme`), gets the expected app-level answer.
- Proxied client call to the same upstream (`POST /mcp-proxy//mcp`): `_forward_headers()` copies the client's headers minus hop-by-hop, cookie and auth; nothing from the server record is added. The upstream receives the **client's `User-Agent`** verbatim. In our case the client is a hosted coding agent whose default User-Agent trips Cloudflare error 1010, so every proxied call returns 403 while the registry's own health check (httpx default UA, same egress IP) passes. There is no per-server setting that lets the operator override or normalize `User-Agent` on the MCP hop.
- With `egress_auth_mode` unset and `EGRESS_AUTH_ENABLED=false`, no credential is injected at all on the MCP hop, so the upstream additionally answers 401 once the UA problem is worked around.

Relevant code (main at time of writing):

- `auth_server/server.py`: `_forward_headers()` (strips ingress auth, forwards everything else), `mcp_proxy()` (injects only vaulted egress tokens under `settings.egress_auth_enabled`).
- `registry/core/mcp_client.py`: merges `headers`, `custom_headers_encrypted` and `auth_scheme` credential for health/discovery only.
- `registry/api/egress_auth_routes.py`: `/_egress_internal/egress-token` never returns the server's static `auth_credential`.

## Proposal

1. **Docs (immediate):** fix the FAQ bullet to say the credential is used for health checks and tool discovery, and that proxied requests get upstream credentials only through `egress_auth_mode` (link `docs/egress-credential-vault.md`). I can send this PR right away.
2. **`custom_headers` on the MCP hop:** apply the server's decrypted `custom_headers` on egress in `mcp_proxy`, operator value winning over any caller-supplied header of the same name (same semantics as `_merge_generic_upstream_headers` on the generic hop, reusing `_pop_header_ci`). This alone covers the `User-Agent` / protocol-signal-header cases without touching credentials.
3. **Opt-in shared operator credential on the MCP hop:** an explicit per-server mode (for example `egress_auth_mode: operator_credential`) that injects the stored `auth_scheme` credential on egress, gated behind `EGRESS_AUTH_ENABLED` like the other modes, so the default stays "strip and inject nothing". This mirrors the generic hop's "shared operator key" model and keeps #1265's default intact.

## Acceptance criteria

- [ ] FAQ no longer claims proxied requests receive the `auth_scheme` credential.
- [ ] A server with `custom_headers` set has those headers on the upstream request made by `mcp_proxy`, operator value winning on name collision.
- [ ] A server without the opt-in mode still gets no credential on egress (unchanged default).
- [ ] With the opt-in mode, the stored credential is injected with the header derived from `auth_scheme` / `auth_header_name`, and never logged.
- [ ] Unit tests for `_forward_headers` + merge behaviour.

Related: #1237 (closed, same symptom), #1265, #1028 / #1041, #966, #1628.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Hướng nghiên cứu

Read `auth_server/server.py` first (`mcp_proxy()` and `_forward_headers()`), since MCP egress headers are built there. Compare with `registry/core/mcp_client.py` and `registry/api/egress_auth_routes.py` to align MCP-hop behavior with existing registry/egress semantics. Update `docs/faq/registering-auth-protected-servers.md` (linking `docs/egress-credential-vault.md`) and add/adjust tests for `_forward_headers` merge precedence and opt-in operator credential behavior. Done when targeted pytest coverage for proxy/header paths passes and MCP proxy sends operator headers/credentials only when enabled.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
python
Lĩnh vực
api, backend
Loại issue
Tính năng
Độ khó
3/5
Thời gian dự kiến
1-2 ngày
Mức độ hoạt động
Sôi nổi
Độ rõ ràng
Đặc tả rõ ràng
Mức phù hợp với người mới
70/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.