docs/tray-debug.md
This guide explains how to control the mcpproxy tray during development and automated testing using environment variables. The variables below let you attach the tray to a pre-launched core, skip automatic OAuth helpers, and keep instrumentation deterministic.
| Variable | Scope | Default | Purpose |
|---|---|---|---|
MCPPROXY_TRAY_SKIP_CORE | Tray | unset | Prevents the tray from launching the core binary. |
MCPPROXY_CORE_URL | Tray | http://localhost:8080 | Overrides the core API endpoint the tray connects to. |
MCPPROXY_DISABLE_OAUTH | Core | unset | Disables OAuth popups and tray-driven login prompts. |
When you want to attach two debuggers (one for the core binary, another for the tray) or restart the core without bouncing the tray:
# terminal 1: start the core with verbose logging
MCPPROXY_DISABLE_OAUTH=true \
go run ./cmd/mcpproxy serve --listen :8085 --tray=false --log-level=debug
# terminal 2: build + run the tray without auto-spawning the core
MCPPROXY_TRAY_SKIP_CORE=1 \
MCPPROXY_CORE_URL=http://localhost:8085 \
go run ./cmd/mcpproxy-tray
What happens
:8085 once the core is ready.MCPPROXY_TRAY_SKIP_CORE is set, the tray never forks a new mcpproxy process. This lets you rebuild or restart the core freely.MCPPROXY_DISABLE_OAUTH=true ensures no OAuth browser windows are spawned during debugging.Add the following launch configurations to .vscode/launch.json (already included in the repo’s example setup):
{
"name": "Debug mcpproxy (.tree/next)",
"type": "go",
"request": "launch",
"mode": "exec",
"program": "${workspaceFolder}/.tree/next/mcpproxy",
"args": ["serve", "--listen", ":8085", "--tray", "false"],
"env": {
"CGO_ENABLED": "1",
"MCPPROXY_DISABLE_OAUTH": "true"
}
},
{
"name": "Debug mcpproxy-tray (.tree/next)",
"type": "go",
"request": "launch",
"mode": "exec",
"program": "${workspaceFolder}/.tree/next/mcpproxy-tray",
"env": {
"CGO_ENABLED": "1",
"MCPPROXY_TRAY_SKIP_CORE": "1",
"MCPPROXY_CORE_URL": "http://localhost:8085"
}
}
With a compound configuration that launches both entries, pressing F5 will:
For Playwright, scripted tray checks, or MCP automation harnesses:
# Start the core in headless mode
MCPPROXY_DISABLE_OAUTH=true \
MCPPROXY_CORE_URL=http://localhost:18080 \
mcpproxy serve --listen :18080 --tray=false &
# Launch the tray with instrumentation enabled
MCPPROXY_TRAY_SKIP_CORE=true \
MCPPROXY_CORE_URL=http://localhost:18080 \
MCPPROXY_TRAY_INSPECT_ADDR=127.0.0.1:8765 \
go run -tags traydebug ./cmd/mcpproxy-tray
The traydebug build tag exposes an HTTP inspector (see /state, /action) so automated tests can query the tray menu without needing Accessibility permissions.
MCPPROXY_TRAY_SKIP_CORE is set to 1 or true in the tray process environment.http://); otherwise the Go HTTP client rejects it.MCPPROXY_DISABLE_OAUTH with test configs to avoid OAuth popups in CI or when running unit tests.If another process already uses the configured listen port, the tray now surfaces a Resolve port conflict sub-menu directly beneath the status indicator. From there you can:
For scripted verification on macOS you can drive the new menu via osascript:
osascript <<'EOF'
tell application "System Events"
tell process "mcpproxy-tray"
click menu bar item 1 of menu bar 1
click menu item "Resolve port conflict" of menu 1 of menu bar item 1 of menu bar 1
delay 0.2
click menu item "Use available port" of menu 1 of menu item "Resolve port conflict" of menu bar item 1 of menu bar 1
end tell
end tell
EOF
Adjust the inner menu titles if you localise the app; the defaults above match the English build.
The tray launches mcpproxy serve when it detects that no core is running. You can steer that subprocess with the following environment variables before starting the tray:
MCPPROXY_CORE_URL – full base URL the tray should connect to (e.g. http://localhost:8085). This also controls the health checks.MCPPROXY_CORE_PATH – custom path to the mcpproxy core binary (defaults to bundled binary or PATH lookup).MCPPROXY_TRAY_LISTEN / MCPPROXY_TRAY_PORT – override the port passed to --listen when the tray launches the core (formats accepted: :8085 or 8085).MCPPROXY_TRAY_CONFIG_PATH – absolute path to the mcp_config.json the tray should hand to the core via --config.MCPPROXY_TRAY_EXTRA_ARGS – optional additional CLI arguments (whitespace separated) appended after serve.MCPPROXY_TRAY_SKIP_CORE – set to 1 to prevent the tray from launching the core automatically (useful when attaching to an external instance).MCPPROXY_TRAY_CORE_TIMEOUT – timeout in seconds for core server startup (default: 30).MCPPROXY_TRAY_RETRY_DELAY – retry delay in milliseconds for core server connection (default: 1000).MCPPROXY_TRAY_STATE_DEBUG – set to 1 to enable state machine debug logging.The tray’s status tooltip reflects the active listen address; when you change any of the variables above, restart the tray so it relaunches the core with the new settings.
Use the updated packaging script to bundle the tray and core into a single notarizable DMG:
GOOS=darwin GOARCH=arm64 go build -o dist/mcpproxy-tray ./cmd/mcpproxy-tray
GOOS=darwin GOARCH=arm64 go build -o dist/mcpproxy ./cmd/mcpproxy
./scripts/create-dmg.sh dist/mcpproxy-tray dist/mcpproxy v1.0.0 arm64
The resulting mcpproxy.app contains:
Contents/MacOS/mcpproxy – the tray executable.Contents/Resources/bin/mcpproxy – the CLI core binary that the tray stages at runtime.When the DMG is mounted the user only needs to drag the app bundle to /Applications; the tray will manage the core automatically from that embedded location.