OAuth
probr is its own OAuth 2.1 authorization server. If you are connecting a standard MCP client you do not need this page — MCP is enough, and the client does all of it for you. This is for writing one.
What kind of server this is
Public clients with PKCE, and nothing else. There are no client secrets, no
implicit grant, and no client_credentials — every token is issued on behalf of a
person who approved it in a browser.
| Setting | Value |
|---|---|
| Issuer | https://api.probr.ai |
| Metadata | https://api.probr.ai/.well-known/oauth-authorization-server |
| Grants | authorization_code, refresh_token |
| PKCE | Required, S256 only |
| Client auth | none — public clients |
| Scope | probr.read |
| Access token | 1 hour |
| Refresh token | 30 days |
Start from the metadata document rather than from this table. It is the same information, it is machine-readable, and it stays correct if anything moves.
1 · Register
Dynamic client registration, no credentials required. redirect_uris is the only
required field.
curl -X POST https://api.probr.ai/oauth/register \
-H 'Content-Type: application/json' \
-d '{
"client_name": "My assistant",
"redirect_uris": ["http://127.0.0.1:8976/callback"]
}'
You get back a client_id. There is no secret to store — hold the id and register
again if you lose it.
2 · Authorize
Send the user's browser to /oauth/authorize:
https://api.probr.ai/oauth/authorize
?response_type=code
&client_id=<client_id>
&redirect_uri=http://127.0.0.1:8976/callback
&code_challenge=<S256 of your verifier>
&code_challenge_method=S256
&scope=probr.read
&state=<random>
&resource=https://mcp.probr.ai/mcp
resource names what the token is for, and the token's audience is bound to it.
For MCP, pass the MCP endpoint.
probr redirects to a consent screen showing which client is asking, which probr
account is granting, and what probr.read covers. The user signs in if they are
not already, then approves or declines.
On approval you are redirected back with code and your state. The code is
single-use and expires in five minutes.
Two ways this fails before consent, and they behave differently on purpose. An
unknown client_id or a redirect_uri that does not match a registered one
returns an error directly — there is nowhere safe to redirect to. Everything else
— a missing PKCE challenge, a scope that does not exist, a response type other than
code — comes back to your redirect_uri as ?error=…, which is where a client
can actually handle it.
3 · Token
curl -X POST https://api.probr.ai/oauth/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=authorization_code' \
-d 'code=<code>' \
-d 'client_id=<client_id>' \
-d 'redirect_uri=http://127.0.0.1:8976/callback' \
-d 'code_verifier=<the verifier>' \
-d 'resource=https://mcp.probr.ai/mcp'
Returns an access token, a refresh token, and expires_in. Refresh the same way
with grant_type=refresh_token.
4 · Call
Authorization: Bearer <access_token>
The MCP server checks three things on every request, not just the first: the signature, that the audience matches its own address, and that the workspace is on a paid plan. A token minted for a different resource is rejected even if it is otherwise valid.
Errors you will meet
401 with a WWW-Authenticate header. The token is missing, expired, or has
the wrong audience. The header points at the protected-resource metadata, which is
how a client rediscovers where to authenticate.
403 MCP access is not included in your current plan. The token is fine and the
workspace has no live plan — the trial or a subscription ended. This also fails
earlier, at consent — the approval step refuses before a token is ever issued, so
the user finds out before connecting rather than after.
400 at the token endpoint. Almost always the PKCE verifier not matching the
challenge, or a code that has already been used. Codes are single-use; a retried
request is a second use.
Scopes
Two, and asking for the first is the normal case:
| Scope | Grants |
|---|---|
probr.read | every competitor, every signal, every brief in the workspace |
probr.write | additionally: add, pause, resume and stop watching a competitor |
There is nothing narrower than probr.read — it is the whole workspace or nothing.
probr.write is separate because reading is reversible and writing spends a plan
slot or stops delivery. Ask for it only if your client needs it: the MCP server does
not advertise its write tools to a token without it, so a read-only client is never
offered something it cannot do.
A caution about what you build
A token grants read access to the whole workspace. An agent holding one can read all of it, and anything the agent sends onward goes with it. Be as deliberate about where that output lands as you would be about a report link.