Executor version
1.6.2 observed; the composition is unchanged at v1.6.8 (see below).
How do you run Executor?
CLI (executor daemon run)
Operating system
macOS (Apple Silicon)
Integration involved
MCP server (browser elicitation / elicitation_mode=browser)
What happened
When a gated tool call pauses, the daemon returns structuredContent.approvalUrl anchored at the request's own origin. In packages/hosts/mcp/src/browser-approval.ts, approvalUrlForRequest does new URL(path, request.url), so the authority reflects the Host header but the scheme is whatever the daemon's own listener spoke — always http:// behind a TLS-terminating proxy, because the proxy talks plain HTTP to loopback:
http://<host>:<port>/resume/<execution-id>?mcp_session_id=<session-id>
The host and port are taken from the request, so a reverse proxy or a Tailscale serve/funnel route is reflected correctly — but the scheme is not. On a TLS-only listener the emitted link is unreachable exactly as given: the client must rewrite http to https before a human can open it.
Any deployment that reaches the daemon over TLS (which is the only sane way to expose it beyond loopback) must post-process the approval link before presenting it. Agent clients that render the URL verbatim — which the resumePrompt text explicitly instructs them to do — hand the user a dead link.
What you expected
The scheme to be derived the same way the authority already is — from the forwarded request — or to be overridable.
Proposed fix: compose the scheme from the same forwarded request context as the authority: honour X-Forwarded-Proto when the daemon is configured to trust its proxy, and otherwise let an explicit base URL (EXECUTOR_WEB_BASE_URL or a config key) win over both. Falling back to http:// only when neither is available keeps the loopback default unchanged.
Steps to reproduce
- Run
executor daemon run --foreground --port <port> (binds loopback).
- Put any TLS-terminating reverse proxy in front of it on
https://<host>:<tls-port>.
POST https://<host>:<tls-port>/mcp?elicitation_mode=browser with the bearer, initialize, then call execute with a program that invokes a tool covered by a require_approval policy.
- The paused result's
approvalUrl is http://<host>:<tls-port>/resume/<execution-id>?mcp_session_id=<session-id> — the authority is right, the scheme is not, and the URL does not resolve.
What I tried:
| Attempt |
Result |
EXECUTOR_WEB_BASE_URL=https://… |
ignored; link still http:// |
X-Forwarded-Proto: https |
ignored; link still http:// |
X-Forwarded-Host |
authority honoured, scheme not |
| TLS-terminating proxy in front |
link still http:// |
Diagnostics / logs
Observed on executor 1.6.2 (darwin-arm64, npm), macOS 26.4, TLS terminated by a reverse proxy on the same host. Checked against the v1.6.8 tag before filing: approvalUrlForRequest in packages/hosts/mcp/src/browser-approval.ts still builds from request.url, and no 1.6.3–1.6.8 release note touches approval-URL composition. No logs attached — the behaviour is fully described by the returned approvalUrl above; all identifiers are placeholders.
Before you submit
Executor version
1.6.2 observed; the composition is unchanged at v1.6.8 (see below).
How do you run Executor?
CLI (
executor daemon run)Operating system
macOS (Apple Silicon)
Integration involved
MCP server (browser elicitation /
elicitation_mode=browser)What happened
When a gated tool call pauses, the daemon returns
structuredContent.approvalUrlanchored at the request's own origin. Inpackages/hosts/mcp/src/browser-approval.ts,approvalUrlForRequestdoesnew URL(path, request.url), so the authority reflects theHostheader but the scheme is whatever the daemon's own listener spoke — alwayshttp://behind a TLS-terminating proxy, because the proxy talks plain HTTP to loopback:The host and port are taken from the request, so a reverse proxy or a Tailscale
serve/funnelroute is reflected correctly — but the scheme is not. On a TLS-only listener the emitted link is unreachable exactly as given: the client must rewritehttptohttpsbefore a human can open it.Any deployment that reaches the daemon over TLS (which is the only sane way to expose it beyond loopback) must post-process the approval link before presenting it. Agent clients that render the URL verbatim — which the
resumePrompttext explicitly instructs them to do — hand the user a dead link.What you expected
The scheme to be derived the same way the authority already is — from the forwarded request — or to be overridable.
Proposed fix: compose the scheme from the same forwarded request context as the authority: honour
X-Forwarded-Protowhen the daemon is configured to trust its proxy, and otherwise let an explicit base URL (EXECUTOR_WEB_BASE_URLor a config key) win over both. Falling back tohttp://only when neither is available keeps the loopback default unchanged.Steps to reproduce
executor daemon run --foreground --port <port>(binds loopback).https://<host>:<tls-port>.POST https://<host>:<tls-port>/mcp?elicitation_mode=browserwith the bearer,initialize, then callexecutewith a program that invokes a tool covered by arequire_approvalpolicy.approvalUrlishttp://<host>:<tls-port>/resume/<execution-id>?mcp_session_id=<session-id>— the authority is right, the scheme is not, and the URL does not resolve.What I tried:
EXECUTOR_WEB_BASE_URL=https://…http://X-Forwarded-Proto: httpshttp://X-Forwarded-Hosthttp://Diagnostics / logs
Observed on executor 1.6.2 (darwin-arm64, npm), macOS 26.4, TLS terminated by a reverse proxy on the same host. Checked against the v1.6.8 tag before filing:
approvalUrlForRequestinpackages/hosts/mcp/src/browser-approval.tsstill builds fromrequest.url, and no 1.6.3–1.6.8 release note touches approval-URL composition. No logs attached — the behaviour is fully described by the returnedapprovalUrlabove; all identifiers are placeholders.Before you submit