mirror of
https://github.com/penpot/penpot.git
synced 2026-08-10 14:59:08 +00:00
🐛 Fix stale DNS caching in frontend nginx MCP proxy (#10947)
The generated /etc/nginx/overrides/server.d/mcp-locations.conf used a plain proxy_pass target (e.g. `proxy_pass http://penpot-mcp:4402;`) where $PENPOT_MCP_URI/$PENPOT_MCP_URI_WS are shell variables substituted once by envsubst in nginx-entrypoint.sh at container startup, not nginx variables. nginx resolves a literal proxy_pass hostname once when the config loads and never re-checks it, so the existing `resolver 127.0.0.11 valid=10s;` directive in overrides/http.d/resolvers.conf has no effect on these three locations - it only applies to nginx variables evaluated per-request. In multi-container deployments where the penpot-mcp container restarts or is recreated independently of penpot-frontend (image update, OOM, orchestrator reschedule), it gets a new IP from Docker's/the orchestrator's DNS, and the frontend's nginx keeps forwarding to the old, now-dead address until penpot-frontend itself is restarted. This surfaces to users as `wss://<host>/mcp/ws` failing to connect from the browser after enabling the MCP plugin, with `connect() failed (111: Connection refused)` in the frontend's nginx logs. Route each location through a `set $var ...; proxy_pass $var;` pair so proxy_pass evaluates a real nginx variable, letting the pre-existing resolver directive re-resolve penpot-mcp within its 10s TTL instead of caching the address for the container's lifetime. For /mcp/stream and /mcp/sse, the set value also appends $is_args$args explicitly: when proxy_pass targets a variable AND that variable's value includes a URI/path component, nginx does not automatically forward the original request's query string the way it does for a static proxy_pass target - it must be appended by hand, or the userToken query parameter used for multi-user authentication is silently dropped before reaching the MCP server. /mcp/ws has no path component in its target so it isn't affected by this and needed no such change. Verified locally: force-recreated the penpot-mcp container onto a different IP while leaving penpot-frontend untouched; the /mcp/ws WebSocket upgrade kept returning 101 Switching Protocols throughout, both immediately and after the resolver's TTL window. Separately verified /mcp/stream: a POST with ?userToken=... now shows up server-side as userTokenFp=<redacted first 8 chars> instead of <none>, and an actual MCP client (Claude Code) using this proxy can now call authenticated tools like execute_code successfully. Signed-off-by: Jules LaPrairie <jules@lucidbox.ca>
This commit is contained in:
parent
b9c92496f1
commit
d63d6370c0
@ -1,16 +1,19 @@
|
||||
location /mcp/ws {
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_pass $PENPOT_MCP_URI_WS;
|
||||
set $mcp_ws_backend $PENPOT_MCP_URI_WS;
|
||||
proxy_pass $mcp_ws_backend;
|
||||
proxy_http_version 1.1;
|
||||
}
|
||||
|
||||
location /mcp/stream {
|
||||
proxy_pass $PENPOT_MCP_URI/mcp;
|
||||
set $mcp_stream_backend $PENPOT_MCP_URI/mcp$is_args$args;
|
||||
proxy_pass $mcp_stream_backend;
|
||||
proxy_http_version 1.1;
|
||||
}
|
||||
|
||||
location /mcp/sse {
|
||||
proxy_pass $PENPOT_MCP_URI/sse;
|
||||
set $mcp_sse_backend $PENPOT_MCP_URI/sse$is_args$args;
|
||||
proxy_pass $mcp_sse_backend;
|
||||
proxy_http_version 1.1;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user