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 is refused with “The redirect address is not a local callback, so pairing was refused.” That covers a redirect carrying credentials, tablepro:// and the database URL schemes TablePro opens, a scheme a web browser handles (such as google-chrome:), and network schemes whose handler fetches from the host they name, such as smb://, ssh:// and webcal://.
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.

Response modes

response_mode decides how the answer reaches redirect. Send query. Use context when your host passes an extension a single launch parameter named context, as Raycast does. The context value is JSON with sorted keys, percent-encoded like any other query value. Any other response_mode drops the link: no sheet opens and nothing reaches redirect. Query items already on redirect stay as they were, with these appended after them.

State

state is optional and opaque: up to 1,024 UTF-8 bytes, returned unchanged on approve and on deny, in every mode. A longer one drops the link the same way. Send a fresh random value with each request and drop any callback whose state does not match the request you started.

Deprecated: no response_mode

A link without response_mode still gets the delivery in the last row of the table, and is deprecated. A later release will read a missing response_mode as query, so send it explicitly. Versioning has the policy.

Scopes

scopes holds one permission level (readOnly, readWrite or fullAccess) plus, for a launcher, connections:display, separated by spaces or commas. Unknown entries are skipped. When the list names more than one level, the sheet starts at the lowest. connections:display lets the token call list_connections with purpose: "display", which adds connections hidden from AI and each connection’s user name. Ask for it only to show the user their connections, never to feed a model. The sheet offers it only while Allow apps to list connections hidden from AI is on under Settings > MCP, and its checkbox starts off. Otherwise the token is issued without it. The exchange response names what was granted:
scope lists every granted scope, sorted and space separated. Read it instead of assuming the request went through: the user can lower the permission level or leave the checkbox off.

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). A link that asks for connections:display adds List connections hidden from AI, off until the user ticks it. 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 > MCP > 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 the deny response for the request’s mode.