/v1, and a bot token can’t call /mcp.
Hosts
Each environment has one host. It is the OAuth issuer, the authorization server and the resource server.
The examples below use Production.
Discovery
A request to/mcp without a token gets 401 and a challenge that names
the resource metadata:
GET /.well-known/oauth-protected-resource/mcp:
/mcp/readonly, resource is https://mcp.workchats.com/mcp/readonly,
scopes_supported is ["workchats:read"], and the 401 challenge names
that metadata URL and scope="workchats:read".
GET /.well-known/oauth-authorization-server:
Register a client
A client identifies itself in one of two ways. Both are open: any client may register, and a company admin can block a client by its block key.Client ID Metadata Document (preferred)
Use anhttps:// URL as your client_id. At /authorize, Workchats
fetches the document at that URL. The fetch:
- uses HTTPS only, follows no redirects, waits at most 3 seconds and reads at most 10 KB
- refuses private and reserved addresses
- needs the document’s
client_idto equal its URL, and itsredirect_uristo contain the requestedredirect_uri - needs a URL with a path (not just the host) of at most 2048 bytes
- needs a public client:
token_endpoint_auth_methodabsent ornone. Any other value getsinvalid_clientat/authorize.
Cache-Control max-age, kept between 5
minutes and 24 hours. With no max-age, or with no-store, it is cached
for 5 minutes. A failed fetch is not cached. One document URL is fetched at
most 5 times in 10 minutes.
Dynamic client registration
POST /register, with no authentication, as JSON or a form:
client_nameis 1 to 100 printable characters. It can’t contain the words Workchats, Octogle, admin, system or support.redirect_urislists 1 to 5 URIs. Each is one of:https://on any host- loopback
http://on127.0.0.1,localhostor[::1], any port - a private-use scheme that contains a period, for example
com.example.app:/callback, or one of the editor schemescursor,vscode,vscode-insiders,windsurfandzed
- No URI may have a fragment or a user name.
- The URIs of one registration are all
httpson one host, or all on one private-use scheme, or loopback with or withouthttpson any hosts. Any other mix is refused. VS Code’s fallback registration listshttps://insiders.vscode.dev/redirect,https://vscode.dev/redirect,http://127.0.0.1/andhttp://127.0.0.1:33418/in one request. That mix is accepted. Its block key islocal:, the lower-caseclient_nameand@web, so it never shares a key or a fixed display name with an all-loopback app. Only anhttps-only registration gets a host key, because anyone can list anhttpshost they do not own. grant_typesmay hold onlyauthorization_codeandrefresh_token.response_typesmust be["code"].token_endpoint_auth_methodisnone,client_secret_basicorclient_secret_post. If you leave it out, it isclient_secret_basic. The two secret methods get aclient_secret, shown once.- Extra metadata (
logo_uri,client_uri,tos_uri,policy_uri,contacts,scope,software_id,software_version) is stored, at most 4 KB, but never fetched or shown.
201 with a client_id that starts with mcpc_, and the
registered metadata. A registered client that never connects anyone is
deleted after 30 days.
Private-use schemes
The MCP authorization spec allows onlyhttps and localhost redirect URIs.
Workchats also accepts private-use schemes (RFC 8252 section 7.1), because
desktop apps and editors such as Cursor and VS Code use them. The consent
screen warns the employee that the code goes to an app on their computer,
as it does for a loopback redirect.
Block key and display name
Every client gets a block key, the part of its identity Workchats can check. A company admin blocks a client by this key, so a block survives a new registration.
Well-known keys get a fixed display name:
claude.ai is “Claude”,
chatgpt.com and chat.openai.com are “ChatGPT”, local:claude code is
“Claude Code”, cursor is “Cursor” and vscode is “VS Code”. A local:
key of a well-known host gets that host’s name. Any other client shows its
client_name, cut to 40 characters. The display name is what the consent
screen shows and what the “via” label of a sent
message says.
Authorize
Send the employee’s browser to/authorize with PKCE:
code_challenge_methodmust beS256.plainis refused. The challenge is 43 characters of base64url.redirect_urimust match a registered URI exactly. A loopback URI may differ only in its port.resource(RFC 8707) ishttps://mcp.workchats.com/mcporhttps://mcp.workchats.com/mcp/readonly. Left out, it is/mcp.scopeis a subset of the resource’s scopes. Left out, it is all of them.
redirect_uri with error and your state: unsupported_response_type,
invalid_request, invalid_target or invalid_scope.
On success, the browser goes to the Workchats consent screen. The employee
signs in, picks one company and approves. Workchats then redirects to:
iss is the issuer you started with (RFC 9207). If the employee
denies, the redirect carries error=access_denied instead of code.
The request is valid for 10 minutes, and the code for 10 minutes after
approval. Only employees with an active account can approve, and only in a
company that has AI assistants on and has not blocked your client.
Exchange the code
POST /token, as a form or JSON:
client_id
and client_secret in the body. HTTP Basic wins if both are sent.
Cache-Control: no-store. Outside Production the
tokens contain _test_ in place of _live_.
A code works once. Presenting a used code again returns invalid_grant
and revokes the connection that code created, because a replayed code
is more likely theft than a retry.
A new connection replaces an older one of the same employee, with the same
client_id and the same redirect URI. Loopback redirect URIs count as the
same whatever their port. So Claude on the web and a local app that uses
Claude’s client_id keep separate connections.
Call the MCP endpoint
Send the access token in theAuthorization header on every request:
- A token in the query string is refused with
400. - A token for
/mcpalso works on/mcp/readonly, where it lists only read tools. A token for/mcp/readonlyworks only there. - A token without
workchats:writelists only read tools. Calling a write tool with it gets403withWWW-Authenticate: Bearer error="insufficient_scope", so you can ask the employee for more. - The server speaks MCP protocol versions
2026-07-28,2025-11-25and2025-06-18over Streamable HTTP. initializereturns anMcp-Session-Id. Send it on every later request of the session. A request without it gets400, and an id from another connection or an ended session gets404. A session ends onDELETEor after 10 idle minutes. A stateless2026-07-28request needs no session.- A browser request must send an
Originthat is onehttpsorigin or a loopbackhttporigin. Any otherOrigingets403. - A request body is at most 1 MB, and must be JSON or a form. A larger body
gets
413with{"error": "request_too_large"}.
401 with
error="invalid_token" in the challenge.
Scopes
There is no
offline_access scope. Every grant gets a refresh token.
Tokens and lifetimes
Tokens are opaque, not JWTs. Workchats looks each one up on every request,
so a revocation takes effect at once.
Refresh.
POST /token with grant_type=refresh_token and
refresh_token, authenticated as for the code grant. The answer has a new
access token and a new refresh token. The old refresh token is spent,
and the old access token stops working.
- A refresh token works only for the client it was issued to.
scope, if sent, can’t be wider than the grant:invalid_scope.resource, if sent, must be the grant’s:invalid_target.- Reuse ends the connection. A spent refresh token presented again
revokes the whole connection and returns
invalid_grant. There is no grace period. A client that loses a refresh response must connect again. - A connection ends when no refresh happens for 30 days, or when the company’s maximum session lifetime passes.
Revocation
POST /revoke (RFC 7009) with token, an access or a refresh token,
authenticated as for /token, revokes the connection of that token. It
answers 200 with {}, also for an unknown token, a revoked token or a
token of another client. Those it leaves alone.
Workchats also ends a connection when:
After revocation, the next MCP request gets
401 with
error="invalid_token". Show your own “reconnect” prompt. Turning AI
assistants on again or unblocking a client brings no connection back.
Rate limits
OAuth endpoints, per IP address and per client:
Over a limit,
/token, /revoke and /register answer 429 with
{"error": "slow_down"} and Retry-After. /authorize answers 429 with
a plain text page.
The MCP endpoint has per-employee limits, shared by all of an employee’s
connections. See Limits.
Errors
OAuth endpoints answer in the RFC 6749 shape, not the public API’s error envelope:
Tool errors inside a successful MCP response are described in
Tools.