feat: support legacy streamable-http config entries

This commit is contained in:
gpt-5-codex 2025-10-10 23:55:27 +02:00 committed by Andre Heinecke
parent 18d94d8913
commit 2708a6ff5f
3 changed files with 108 additions and 31 deletions

View File

@ -1,11 +1,14 @@
# Example NGINX front-end for MCP Browser # Example NGINX front-end for MCP Browser
# #
# Replace the placeholder values (domain, certificate paths, slug, upstream # This template follows the MCP specification (2025-03-26) section 2.4 on dynamic
# ports) with values that match your deployment. This configuration exposes: # client registration:
# * OAuth metadata + token endpoints implemented via an njs helper # https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization#2-4-dynamic-client-registration
# * An authenticated Streamable HTTP MCP endpoint #
# * An unauthenticated debug endpoint (optional) that forwards to the # Replace the placeholder values (domain, certificate paths, hash slug, upstream
# mcp-browser SSE bridge # ports) with values that match your deployment. The configuration provides:
# * OAuth metadata + register/token endpoints backed by an njs helper
# * A protected Streamable HTTP MCP endpoint (auth_request + OAuth)
# * An optional unauthenticated SSE bridge for local debugging
# #
# The example assumes that: # The example assumes that:
# * An njs script lives at /etc/nginx/njs/mcp_oauth.js providing handlers # * An njs script lives at /etc/nginx/njs/mcp_oauth.js providing handlers
@ -45,14 +48,17 @@ server {
add_header X-Robots-Tag "noindex, nofollow, noarchive" always; add_header X-Robots-Tag "noindex, nofollow, noarchive" always;
# --- OAuth discovery + token endpoints (served by njs helper) ------------- # Resource identifiers MUST match what your metadata document returns.
# Replace __MCP_SLUG__ with the slug you publish in OAuth metadata. # Use the same hash/digest in mcp_oauth.metadata().
location = /.well-known/oauth-authorization-server/__MCP_SLUG__/mcp { set $mcp_resource "__MCP_RESOURCE_HASH__";
# --- OAuth discovery + token/register endpoints per §2.4 ------------------
location = /.well-known/oauth-authorization-server/$mcp_resource/mcp {
default_type application/json; default_type application/json;
js_content mcp_oauth.metadata; js_content mcp_oauth.metadata;
} }
location = /.well-known/oauth-protected-resource/__MCP_SLUG__/mcp { location = /.well-known/oauth-protected-resource/$mcp_resource/mcp {
default_type application/json; default_type application/json;
js_content mcp_oauth.metadata; js_content mcp_oauth.metadata;
} }
@ -77,6 +83,16 @@ server {
js_content mcp_oauth.token; js_content mcp_oauth.token;
} }
# The metadata served above should advertise this /authorize endpoint.
# Update the upstream target to whatever Authorization Server you use.
location = /authorize {
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass https://idp.example.com/oauth2/authorize;
}
# Internal auth_request hook used by the protected MCP endpoint. # Internal auth_request hook used by the protected MCP endpoint.
location = /_oauth_check { location = /_oauth_check {
internal; internal;
@ -85,7 +101,7 @@ server {
# --- Protected Streamable HTTP MCP endpoint -------------------------------- # --- Protected Streamable HTTP MCP endpoint --------------------------------
# Requires successful OAuth check before proxying to the upstream server. # Requires successful OAuth check before proxying to the upstream server.
location ^~ /__MCP_SLUG__/mcp/ { location ^~ /$mcp_resource/mcp/ {
auth_request /_oauth_check; auth_request /_oauth_check;
proxy_http_version 1.1; proxy_http_version 1.1;
@ -109,7 +125,7 @@ server {
} }
# --- Optional unauthenticated SSE bridge for local debugging --------------- # --- Optional unauthenticated SSE bridge for local debugging ---------------
location ^~ /__MCP_SLUG__/mcp-browser/ { location ^~ /$mcp_resource/mcp-browser/ {
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Connection ""; proxy_set_header Connection "";
proxy_set_header Host $host; proxy_set_header Host $host;

View File

@ -109,6 +109,23 @@ class ConfigLoader:
servers = {} servers = {}
for name, server_config in config_data.get("servers", {}).items(): for name, server_config in config_data.get("servers", {}).items():
transport_data = server_config.get("transport", {}) or {} transport_data = server_config.get("transport", {}) or {}
# Merge transport metadata from legacy locations
raw_transport_type = (
transport_data.get("type")
or server_config.get("transportType")
or server_config.get("type")
)
normalized_type = "stdio"
if isinstance(raw_transport_type, str):
lowered = raw_transport_type.replace("_", "-").lower()
if lowered in {"streamable-http", "streamablehttp"}:
normalized_type = "streamable-http"
elif lowered in {"stdio", "standard"}:
normalized_type = "stdio"
else:
normalized_type = lowered
oauth_data = transport_data.get("oauth") or {} oauth_data = transport_data.get("oauth") or {}
oauth_config = None oauth_config = None
if oauth_data: if oauth_data:
@ -121,12 +138,43 @@ class ConfigLoader:
extra_params=oauth_data.get("extra_params") or oauth_data.get("extraParams", {}), extra_params=oauth_data.get("extra_params") or oauth_data.get("extraParams", {}),
access_token=oauth_data.get("access_token") or oauth_data.get("accessToken"), access_token=oauth_data.get("access_token") or oauth_data.get("accessToken"),
) )
transport_url = transport_data.get("url") or server_config.get("url")
headers = transport_data.get("headers") or server_config.get("headers") or {}
timeout = (
transport_data.get("timeout")
or server_config.get("timeout")
or 30.0
)
sse_timeout = (
transport_data.get("sse_timeout")
or transport_data.get("sseTimeout")
or server_config.get("sse_timeout")
or server_config.get("sseTimeout")
or 300.0
)
# Legacy args: ["url", "http://..."] or ["--url=http://..."]
args = server_config.get("args", [])
if not transport_url and isinstance(args, list):
for idx, arg in enumerate(args):
if not isinstance(arg, str):
continue
stripped = arg.strip()
if stripped in {"url", "--url", "--transport-url"}:
if idx + 1 < len(args):
transport_url = args[idx + 1]
break
elif stripped.startswith(("url=", "--url=", "--transport-url=")):
transport_url = stripped.split("=", 1)[1]
break
transport_config = TransportConfig( transport_config = TransportConfig(
type=transport_data.get("type", "stdio"), type=normalized_type,
url=transport_data.get("url"), url=transport_url,
headers=transport_data.get("headers", {}), headers=headers,
timeout=transport_data.get("timeout", 30.0), timeout=timeout,
sse_timeout=transport_data.get("sse_timeout") or transport_data.get("sseTimeout", 300.0), sse_timeout=sse_timeout,
oauth=oauth_config oauth=oauth_config
) )
servers[name] = MCPServerConfig( servers[name] = MCPServerConfig(

View File

@ -1,5 +1,5 @@
[Unit] [Unit]
Description=Expose MCP Browser via HTTP bridge Description=Run mcp-browser in MCP server mode
Documentation=https://github.com/Xilope0/mcp-browser Documentation=https://github.com/Xilope0/mcp-browser
After=network-online.target After=network-online.target
Wants=network-online.target Wants=network-online.target
@ -7,20 +7,33 @@ Wants=network-online.target
[Service] [Service]
Type=simple Type=simple
Environment="PYTHONUNBUFFERED=1" Environment="PYTHONUNBUFFERED=1"
Environment="MCP_PROXY_BIN=%h/.local/bin/mcp-proxy" Environment="MCP_BROWSER_BIN=%h/.local/bin/mcp-browser"
Environment="MCP_PROXY_ALLOW_ORIGIN=https://platform.openai.com" Environment="MCP_BROWSER_CONFIG=%h/.config/mcp-browser/config.yaml"
Environment="MCP_PROXY_PORT=14001" Environment="MCP_BROWSER_SERVER=default"
Environment="MCP_PROXY_CONFIG=%h/.config/mcp-browser/proxy.yaml" Environment="MCP_BROWSER_MODE=server"
Environment="MCP_PROXY_EXTRA_ARGS=" Environment="MCP_BROWSER_NO_BUILTIN=false"
EnvironmentFile=-%h/.config/mcp-browser/proxy.env Environment="MCP_BROWSER_TRANSPORT="
Environment="MCP_BROWSER_TRANSPORT_URL="
Environment="MCP_BROWSER_EXTRA_ARGS="
EnvironmentFile=-%h/.config/mcp-browser/browser.env
ExecStart=/usr/bin/env sh -c '\ ExecStart=/usr/bin/env bash -lc 'set -eu
set -eu; \ ARGS=()
exec "$MCP_PROXY_BIN" \ if [[ "$MCP_BROWSER_NO_BUILTIN" == "true" ]]; then
--allow-origin "$MCP_PROXY_ALLOW_ORIGIN" \ ARGS+=(--no-builtin)
--port "$MCP_PROXY_PORT" \ fi
--named-server-config "$MCP_PROXY_CONFIG" \ if [[ -n "$MCP_BROWSER_TRANSPORT" ]]; then
$MCP_PROXY_EXTRA_ARGS' ARGS+=(--transport "$MCP_BROWSER_TRANSPORT")
fi
if [[ -n "$MCP_BROWSER_TRANSPORT_URL" ]]; then
ARGS+=(--transport-url "$MCP_BROWSER_TRANSPORT_URL")
fi
exec "$MCP_BROWSER_BIN" \
--mode "$MCP_BROWSER_MODE" \
--config "$MCP_BROWSER_CONFIG" \
--server "$MCP_BROWSER_SERVER" \
"${ARGS[@]}" \
$MCP_BROWSER_EXTRA_ARGS'
Restart=on-failure Restart=on-failure
RestartSec=5 RestartSec=5