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
#
# Replace the placeholder values (domain, certificate paths, slug, upstream
# ports) with values that match your deployment. This configuration exposes:
# * OAuth metadata + token endpoints implemented via an njs helper
# * An authenticated Streamable HTTP MCP endpoint
# * An unauthenticated debug endpoint (optional) that forwards to the
# mcp-browser SSE bridge
# This template follows the MCP specification (2025-03-26) section 2.4 on dynamic
# client registration:
# https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization#2-4-dynamic-client-registration
#
# Replace the placeholder values (domain, certificate paths, hash slug, upstream
# 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:
# * 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;
# --- OAuth discovery + token endpoints (served by njs helper) -------------
# Replace __MCP_SLUG__ with the slug you publish in OAuth metadata.
location = /.well-known/oauth-authorization-server/__MCP_SLUG__/mcp {
# Resource identifiers MUST match what your metadata document returns.
# Use the same hash/digest in mcp_oauth.metadata().
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;
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;
js_content mcp_oauth.metadata;
}
@ -77,6 +83,16 @@ server {
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.
location = /_oauth_check {
internal;
@ -85,7 +101,7 @@ server {
# --- Protected Streamable HTTP MCP endpoint --------------------------------
# Requires successful OAuth check before proxying to the upstream server.
location ^~ /__MCP_SLUG__/mcp/ {
location ^~ /$mcp_resource/mcp/ {
auth_request /_oauth_check;
proxy_http_version 1.1;
@ -109,7 +125,7 @@ server {
}
# --- Optional unauthenticated SSE bridge for local debugging ---------------
location ^~ /__MCP_SLUG__/mcp-browser/ {
location ^~ /$mcp_resource/mcp-browser/ {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;

View File

@ -109,6 +109,23 @@ class ConfigLoader:
servers = {}
for name, server_config in config_data.get("servers", {}).items():
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_config = None
if oauth_data:
@ -121,12 +138,43 @@ class ConfigLoader:
extra_params=oauth_data.get("extra_params") or oauth_data.get("extraParams", {}),
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(
type=transport_data.get("type", "stdio"),
url=transport_data.get("url"),
headers=transport_data.get("headers", {}),
timeout=transport_data.get("timeout", 30.0),
sse_timeout=transport_data.get("sse_timeout") or transport_data.get("sseTimeout", 300.0),
type=normalized_type,
url=transport_url,
headers=headers,
timeout=timeout,
sse_timeout=sse_timeout,
oauth=oauth_config
)
servers[name] = MCPServerConfig(

View File

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