Back to Mcpproxy Go

Tray Debugging Guide

docs/tray-debug.md

0.38.16.9 KB
Original Source

Tray Debugging Guide

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.

Quick Reference

VariableScopeDefaultPurpose
MCPPROXY_TRAY_SKIP_CORETrayunsetPrevents the tray from launching the core binary.
MCPPROXY_CORE_URLTrayhttp://localhost:8080Overrides the core API endpoint the tray connects to.
MCPPROXY_DISABLE_OAUTHCoreunsetDisables OAuth popups and tray-driven login prompts.

Use Cases

Debugging the Core and Tray Separately

When you want to attach two debuggers (one for the core binary, another for the tray) or restart the core without bouncing the tray:

bash
# 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

  • The tray icon appears immediately and connects to :8085 once the core is ready.
  • Because 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.

VS Code Compound Debugging

Add the following launch configurations to .vscode/launch.json (already included in the repo’s example setup):

jsonc
{
  "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:

  1. Start the core under the debugger without tray UI.
  2. Attach the tray to the already debugging core.

Automated UI Testing

For Playwright, scripted tray checks, or MCP automation harnesses:

bash
# 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.

Tips & Troubleshooting

  • If the tray still spawns a core instance, confirm MCPPROXY_TRAY_SKIP_CORE is set to 1 or true in the tray process environment.
  • The core URL must include the protocol (e.g. http://); otherwise the Go HTTP client rejects it.
  • Combine MCPPROXY_DISABLE_OAUTH with test configs to avoid OAuth popups in CI or when running unit tests.
  • When running against non-default ports, update your MCP clients (Cursor, VS Code, etc.) to use the same port.

Resolving Port Conflicts

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:

  • Retry the existing port once you have freed it.
  • Automatically switch to the next available port (the tray persists the new value and restarts the core for you).
  • Copy the MCP connection URL to the clipboard for quick use in clients.
  • Jump straight to the configuration directory if you prefer manual edits.

For scripted verification on macOS you can drive the new menu via osascript:

applescript
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.

Launcher Configuration

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.

Building a DMG with Both Binaries

Use the updated packaging script to bundle the tray and core into a single notarizable DMG:

bash
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.

Further Reading