I needed an mcp for lexidraw.app

feat(login): paste-the-callback fallback for headless/SSH logins #14

Closed scarnecchia.net
targeting main from fix/oauth-security-high-medium
Closed opened by and assigned to scarnecchia.net
targeting main from fix/oauth-security-high-medium

What #

node dist/index.js --login <handle> can now be completed on a headless machine over SSH. After printing the authorize URL, the flow also accepts the OAuth callback URL pasted into the terminal (copied from the browser's address bar on whatever machine ran the browser), racing the existing loopback listener — whichever arrives first wins; the flow settles exactly once.

  • Auto-enabled on interactive terminals (TTY), local or SSH
  • Forced with --paste or LEXIDRAW_MCP_LOGIN_PASTE=1|true|yes (scripted drivers over open pipes; host-spawned pipes without the flag keep byte-identical behavior)
  • Pasted state is checked against FileStateStore before acceptance, mirroring the loopback handler's fail-fast; client.callback remains the security authority for the exchange
  • EOF ("stdin closed") degrades gracefully back to loopback + timeout
  • LEXIDRAW_MCP_LOGIN_TIMEOUT_MS overrides the 5-minute wait (positive integer, ≤ 2147483647); five invalid pastes abort the flow
  • Denial redirects (error=access_denied, no code) surface a clear "Authorization failed" error

Why #

Over SSH the browser runs on the user's laptop; the authorization server redirects the laptop's browser to the laptop's 127.0.0.1:<port>, where nothing listens. The user sees "connection refused" — but the address bar holds the full callback URL. The pasted code is exchanged server-to-server from the headless machine, so no local browser is needed.

Tests #

26 new tests in tests/cli.test.ts: race in both directions, per-branch hints and re-prompting, invalid-paste cap, denial, EOF fallback, timeout override + ConfigError paths, parsePastedCallback units, and makeStdinPasteReader lifecycle over a PassThrough. Full suite 138 passed; tsc --noEmit clean; npm run build succeeds.

Docs #

README (headless/SSH paragraph + env table rows) and CLAUDE.md (loopback auth bullet) updated. README hunks also fold in the pending Lexidraw rename.

Commits 0