Skip to main content
Four steps: generate a verifier, open a deep link, catch the one-time code, trade it for a token over localhost. TablePro releases the token only to the caller that can produce the verifier, so an app that intercepts the redirect holds a code it cannot spend. Nothing about this is Raycast-specific. Any client that can receive a callback, through its own URL scheme or a loopback HTTP listener, can pair.

Sequence

The name in the heading is whatever client asked to be called, so read the line under it: it names the address the code is delivered to. Approve only when that address belongs to the app you started the pairing from.
Approval sheet headed Allow Raycast to access TablePro, with the delivery address, a permissions picker, a connection list and an expiry pickerApproval sheet headed Allow Raycast to access TablePro, with the delivery address, a permissions picker, a connection list and an expiry picker

Scope, connections and expiry are all editable before Approve

The whole client, in one file

Three constraints decide whether TablePro accepts the request at all:
  • The verifier is 43 to 128 characters from A-Z a-z 0-9 - . _ ~. 32 random bytes in base64url is 43.
  • The challenge is its base64url SHA-256, so exactly 43 base64url characters.
  • The redirect is a loopback http or https URL (127.0.0.1, localhost, ::1), or a private-use scheme an installed app has registered. Anything else, or one carrying credentials, is refused with “The redirect address is not a local callback, so pairing was refused.”
One delivery detail: a raycast:// redirect gets the code wrapped as ?context={"code":"<uuid>"}, Raycast’s launch-context convention, and every other scheme gets a flat ?code=<uuid>. The exchange endpoint takes no bearer token: the single-use code plus the verifier is the credential. It and /mcp are the only paths the server serves, and both accept POST only.

What the user approves

The sheet names the client and counts down the five minutes the request is good for, dimming Approve when it runs out. Three controls sit under that: Permission Level (starting at what the link asked for, movable in either direction), Allowed Connections (all, or a checked subset), and Expiration (never, 1, 7, 30 or 90 days). The link’s parameters are a request, not a grant. Approving mints the token, holds the plaintext against a one-time code for 5 minutes, and opens the redirect. Pairing again mints a second token rather than replacing the first, so an extension that re-pairs should stop using its old one. Revoke either under Settings > Integrations > Authentication.

Security properties

Errors

A failed verification burns the code, so retrying with a guessed verifier is not an option: start a new pair request. Every failed exchange lands in the activity log under the auth category with outcome denied. Clicking Deny opens the redirect with error=denied and error_description=user_denied, wrapped in the context JSON for raycast:// and appended as flat parameters otherwise.