Skip to content

MCP server: auth-required + freshly-generated token deadlocks first authenticated request (v0.38.0) #1093

@globalsecurepayments

Description

@globalsecurepayments

Summary

After enabling the built-in MCP server, toggling Require authentication ON, and generating a token via the Generate Token dialog, the very next authenticated POST to /mcp silently deadlocks the server's auth path. The TCP socket accepts the connection, but the server never sends an HTTP response — curl returns (52) Empty reply from server after the timeout window. Quitting and relaunching TablePro does NOT recover the wedged state.

A workaround exists (see below), but the first-time setup experience is currently broken.

Environment

  • TablePro v0.38.0 (brew install --cask tablepro, signed/notarized: Developer ID Dat Ngo Quoc (D7HJ5TFYCU))
  • macOS 14 / Apple Silicon
  • MCP server bound at *:23508 (IPv6 wildcard, accepting from 127.0.0.1)
  • Client: Claude Code with claude mcp add tablepro --transport http http://127.0.0.1:23508/mcp --header "Authorization: Bearer <token>"

Reproduction

  1. TablePro → Preferences → MCP Server → toggle MCP server ON.
  2. Toggle Require authentication ON.
  3. Click Generate Token, fill in Token Name, choose any access level (Read Only / Read & Write / Full Access), Connections=All, Expiration=None, click Generate. Copy the resulting token (tp_...).
  4. From a terminal:
    curl -i -m 5 -X POST http://127.0.0.1:23508/mcp \
      -H 'Content-Type: application/json' \
      -H 'Accept: application/json, text/event-stream' \
      -H 'Authorization: Bearer tp_...' \
      -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"smoketest","version":"1.0"}}}'
  5. Observe: curl: (52) Empty reply from server after the timeout. No HTTP status, no body, no logs in log show --predicate 'processImagePath CONTAINS "TablePro"'.

Expected

A successful MCP initialize response: HTTP/1.1 200 OK with Mcp-Session-Id header and a JSON-RPC body containing serverInfo, capabilities, and protocolVersion.

Actual

  • TCP connection accepted (port 23508 listener confirmed via lsof -nP -iTCP:23508 -sTCP:LISTEN)
  • HTTP request sent successfully (request body 100% received per curl -v)
  • Server hangs without sending response bytes; eventually drops the socket
  • claude mcp list reports ✗ Failed to connect

Quitting TablePro fully (⌘Q, process gone, port released) and relaunching does NOT recover. The first generated token appears to be permanently stale even across app restarts.

Workaround (canonical)

  1. Toggle Require authentication OFF.
    • Probe (no token): HTTP/1.1 401 Unauthorized with WWW-Authenticate: Bearer realm="TablePro MCP" (returns immediately — server is healthy).
    • Probe (with original token): HTTP/1.1 401 Unauthorized with WWW-Authenticate: Bearer error=\"invalid_token\" (token confirmed stale).
  2. Toggle Require authentication back ON.
  3. Generate a new token (the original token is permanently invalidated; do not reuse).
  4. Authenticated MCP requests now succeed: HTTP/1.1 200 OK, full tools/list returns 19 tools, connect / list_tables / execute_query all work end-to-end.

Hypothesis

The token generated when Require authentication is flipped ON for the first time (in the same UI session as the toggle change) does not get registered with the auth subsystem before the MCP server begins challenging. The auth middleware enters a state where it accepts TCP, fails to validate the token (because it's not in the token store yet), but also fails to return a 401 — possibly because the auth check itself awaits a token-store read that never completes.

Once auth is toggled OFF then back ON in a separate UI action, the auth subsystem re-initializes its token store correctly and subsequent token generations land cleanly.

Secondary issue: Token Name field focus bug

In the same Generate Token dialog, the Token Name input field cannot be focused via mouse click on first open — clicking the field has no effect, and the Generate button stays grayed out. Pressing Tab (from any other dialog control) successfully transfers focus into the field. This appears to be a SwiftUI focus-state initialization bug in the dialog. Workaround: press Tab on dialog open before clicking.

Impact

First-time MCP setup with auth requires either reading documentation that doesn't exist yet (the workaround is undiscoverable without bash-level diagnostics) or stumbling onto the toggle-cycle by accident. End-to-end MCP wiring works perfectly once the token is regenerated post-toggle-cycle — TablePro's MCP server itself is solid (19 well-designed tools, clean authorization separation, fast response times).

Verification of working state (after workaround)

$ tablepro__execute_query connection_id=AF6016... query="SELECT name FROM sqlite_master"
→ correct row payload returned in 0.9 ms

Happy to provide additional diagnostic output (verbose curl logs, lsof state at each step, auth-state probe sequence) if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions