Sequence

The pairing approval sheet
The whole client, in one file
- 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
httporhttpsURL (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.”
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.

