HTTP API: AI assistants (MCP)
OAuth discovery, client registration and revocation for MCP clients, the console's consent endpoints, your connected AI assistants and the organization switch.
The OAuth endpoints AI assistants use to connect to Sovaria's hosted MCP server (https://api.sovaria.in/mcp), and the endpoints behind Account › Security › AI assistants and Organization settings › Security. MCP clients do the OAuth part themselves; the guide is Connect AI assistants (MCP): Claude, ChatGPT, Cursor and VS Code.
API=https://api.sovaria.in
ACCESS_TOKEN=<your console session, or a token from `sov login`>
ORG_ID=<organization id>| Base URL | https://<project-ref>.sovarias.in — your project URL from Connect. |
| Keys | Send the project's key as apikey. Calls made for a signed-in user also send Authorization: Bearer <access token>. |
| Errors | A status code and {"detail": …} — a sentence, or {code, message} for errors your code may branch on (HTTP API). |
OAuth for MCP clients
Discovery, registration and revocation. Authorization and the token exchange are the shared [[rest-auth#oauth-server|OAuth endpoints]]: an mcp_… or https client_id gets the MCP flow (consent in the console, sov_mcp_ tokens, refresh rotation).
GET/.well-known/oauth-authorization-server
Authorization server metadata (RFC 8414) for MCP clients.
MCP clients find it from the MCP server's 401: WWW-Authenticate names https://api.sovaria.in/.well-known/oauth-protected-resource/mcp, whose authorization_servers is https://api.sovaria.in. Cached for an hour.
- Who
- Anyone: no key or session is needed.
Responses
- 200Success.
curl -s "$API/.well-known/oauth-authorization-server"Response
{
"issuer": "https://api.sovaria.in",
"authorization_endpoint": "https://api.sovaria.in/oauth/authorize",
"token_endpoint": "https://api.sovaria.in/oauth/token",
"registration_endpoint": "https://api.sovaria.in/oauth/register",
"revocation_endpoint": "https://api.sovaria.in/oauth/revoke",
"scopes_supported": [
"mcp",
"mcp:read"
],
"response_types_supported": [
"code"
],
"response_modes_supported": [
"query"
],
"grant_types_supported": [
"authorization_code",
"refresh_token"
],
"code_challenge_methods_supported": [
"S256"
],
"token_endpoint_auth_methods_supported": [
"none",
"client_secret_post",
"client_secret_basic"
],
"revocation_endpoint_auth_methods_supported": [
"none",
"client_secret_post",
"client_secret_basic"
],
"client_id_metadata_document_supported": true,
"authorization_response_iss_parameter_supported": false,
"service_documentation": "https://sovaria.in/docs/mcp"
}POST/oauth/register
Register an MCP client (dynamic client registration, RFC 7591).
Returns a client_id for the authorization request. Registrations that are never used are removed after 30 days; register again whenever you need to. Instead of registering, a client can use an https URL that serves its own metadata document as its client_id.
- Who
- Anyone: no key or session is needed.
Body
redirect_urisstring[]required- 1–10 URIs, matched exactly (a loopback
http://127.0.0.1URI matches on any port). https anywhere; http only to127.0.0.1,localhostor[::1]; or an app's own scheme (cursor://…). No fragments. client_namestring- Shown on the consent page and in the member's connections (up to 128 characters).
token_endpoint_auth_methodstringnone(default: a public client using PKCE),client_secret_postorclient_secret_basic(you get aclient_secret).grant_typesstring[]authorization_codeand optionallyrefresh_token(the default is both).response_typesstring[]- Only
["code"]. client_uristring- Your app's https home page.
software_idstring- Optional identifier of your client software.
software_versionstring- Optional version.
Returns
client_idstringmcp_and 24 characters.client_secretstring- Only for
client_secret_post/client_secret_basic:sov_mcps_…, shown once. client_secret_expires_atinteger0(doesn't expire), with a secret.client_id_issued_atinteger- Unix time.
scopestring- The scopes a client can ask for:
mcp,mcp:read.
Responses
- 201Created.
- 400
invalid_redirect_uri(an unsafe or malformed URI) orinvalid_client_metadata(grant types, response types, auth method), in OAuth's{error, error_description}form. - 429
temporarily_unavailable— more than 20 registrations from one address in an hour. - 503
temporarily_unavailable— connecting AI assistants is turned off right now.
curl -s -X POST "$API/oauth/register" \
-H 'Content-Type: application/json' \
-d '{"client_name": "Claude", "redirect_uris": ["https://claude.ai/api/mcp/auth_callback"], "token_endpoint_auth_method": "none"}'Response
{
"client_id": "mcp_Xv3kP9qLm2Rt8sWy1aBc4d",
"client_id_issued_at": 1792396800,
"client_name": "Claude",
"redirect_uris": [
"https://claude.ai/api/mcp/auth_callback"
],
"grant_types": [
"authorization_code",
"refresh_token"
],
"response_types": [
"code"
],
"token_endpoint_auth_method": "none",
"scope": "mcp mcp:read"
}POST/oauth/revoke
Revoke a token (RFC 7009).
Form-encoded. A refresh token (sov_mcpr_…) ends the whole connection; an access token (sov_mcp_…) stops working. Unknown or already revoked tokens are not an error.
- Who
- Anyone: no key or session is needed.
Body
tokenstringrequired- The access or refresh token.
token_type_hintstring- Ignored: the prefix says which kind it is.
client_idstringrequired- The client the token was issued to (or in HTTP Basic auth).
client_secretstring- For confidential clients (or in HTTP Basic auth).
Responses
- 200Success.
- 401
invalid_client— client authentication failed (a confidential client without its secret).
curl -s -X POST "$API/oauth/revoke" \
-d token=$REFRESH_TOKEN -d client_id=mcp_Xv3kP9qLm2Rt8sWy1aBc4dResponse
{}Consent
What the console's approval page calls. Only a console member's own session can approve: not app users, staff impersonation, simulations or another assistant.
GET/api/v1/auth/mcp/authorize-info
What the consent page shows about an authorization request: the app, where it returns, what it asked for.
Takes the query string /oauth/authorize received. Checks it the same way (client, exact redirect URI, PKCE S256, resource), so the page never shows or answers a request Sovaria wouldn't accept.
- Who
- A project team member's session (the console, or a token from
sov login) asAuthorization: Bearer <token>. The member's role must allow the action. - Also at
/auth/v1/mcp/authorize-info
Query parameters
client_idstringrequiredmcp_…or a metadata-document URL.redirect_uristringrequired- Must be one the client registered.
response_typestringrequiredcode.code_challengestringrequired- PKCE S256 challenge (43–128 characters).
code_challenge_methodstringS256(the default).statestring- Returned unchanged to the client.
resourcestringhttps://api.sovaria.in/mcp, optionally with?project=and?read_only=true.scopestringmcpormcp:read.
Returns
clientobjectclient_id,name,client_uri, andverified_host: the host of a metadata-document client's URL (nullfor a registered client, which only named itself).redirectobjecturiand a shortlabel(claude.ai,an app on this computer,the cursor app).resourcestring- The MCP server the token will be for.
read_only_requestedboolean- The client asked for
mcp:reador?read_only=true. project_hintstring | null?project=from the resource URL, to preselect.
Responses
- 200Success.
- 400
{code, message}:invalid_client,invalid_redirect_uri,invalid_request(PKCE),invalid_target(another resource). Scopes other thanmcpandmcp:readare ignored. - 403
access_denied— not a console member's own session. - 401No valid session or key.
curl -s "$API/api/v1/auth/mcp/authorize-info?response_type=code&client_id=mcp_Xv3kP9qLm2Rt8sWy1aBc4d&redirect_uri=https%3A%2F%2Fclaude.ai%2Fapi%2Fmcp%2Fauth_callback&code_challenge=$CHALLENGE&code_challenge_method=S256&resource=https%3A%2F%2Fapi.sovaria.in%2Fmcp" \
-H "Authorization: Bearer $ACCESS_TOKEN"Response
{
"client": {
"client_id": "mcp_Xv3kP9qLm2Rt8sWy1aBc4d",
"name": "Claude",
"client_uri": null,
"verified_host": null
},
"redirect": {
"uri": "https://claude.ai/api/mcp/auth_callback",
"label": "claude.ai"
},
"resource": "https://api.sovaria.in/mcp",
"read_only_requested": false,
"project_hint": null
}POST/api/v1/auth/mcp/authorize
Approve: one organization, optionally one project, read-only or read and write. Returns where to send the browser.
Send the authorization request's parameters with your choice. The code in redirect_url works once, for 5 minutes, only with the PKCE verifier. Recorded in your security activity and emailed to you.
- Who
- A project team member's session (the console, or a token from
sov login) asAuthorization: Bearer <token>. The member's role must allow the action. - Also at
/auth/v1/mcp/authorize
Body
client_idstringrequired- From the authorization request.
- 1–512 characters
redirect_uristringrequired- From the authorization request.
- 1–2,000 characters
response_typestringoptionaldefaultcodecode.code_challengestringrequired- From the authorization request.
- 43–128 characters
code_challenge_methodstringoptionaldefaultS256S256.statestringoptional- From the authorization request; returned in
redirect_url. - 0–2,000 characters
resourcestringoptional- From the authorization request.
- 0–2,000 characters
scopestringoptional- From the authorization request;
mcp:readforces read-only. - 0–200 characters
org_idstringrequired- The organization the assistant works in. You must be a member, and the organization must allow assistants.
project_idstring | nulloptional- One project of that organization you can open;
nullfor every project (the assistant then picks one with?project=). read_onlybooleanoptionaldefaultfalsetrue: read tools only, and the API refuses changes.
Returns
redirect_urlstring- The client's redirect URI with
code(andstate).
Responses
- 200Success.
- 403
access_denied: not a member of that organization, the project isn't in it or you can't open it, the organization turned assistants off, or not your own console session. - 400The request itself is invalid (see
authorize-info). - 503Connecting AI assistants is turned off right now.
- 401No valid session or key.
- 422The body is missing a required field or has a field of the wrong type.
curl -s -X POST "$URL/api/v1/auth/mcp/authorize" \
-H "apikey: $ANON_KEY" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"client_id": "mcp_Xv3kP9qLm2Rt8sWy1aBc4d", "redirect_uri": "https://claude.ai/api/mcp/auth_callback", "response_type": "code", "code_challenge": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", "code_challenge_method": "S256", "state": "st-91f2", "resource": "https://api.sovaria.in/mcp", "scope": "mcp", "org_id": "8a1d2c3b-4e5f-4a6b-8c7d-9e0f1a2b3c4d", "project_id": "5f2c7a91-3b4d-4e6f-8a1b-2c3d4e5f6a7b", "read_only": false}'Response
{
"redirect_url": "https://claude.ai/api/mcp/auth_callback?code=Hk3v…&state=st-91f2"
}Your connections
GET/api/v1/auth/mcp/connections
AI assistants you connected.
Your live connections, newest first. An assistant's own token can't call this.
- Who
- A project team member's session (the console, or a token from
sov login) asAuthorization: Bearer <token>. The member's role must allow the action. - Also at
/auth/v1/mcp/connections
Returns
connectionsobject[]- One per connection:
9 fields
idstring- The connection's id (for disconnecting).
client_namestring- The name the app gave itself when it registered (or from its metadata document).
client_idstringmcp_…for a registered app, or the https URL of its client metadata document.orgobject- The organization it works in:
{id, name}. projectobject | null- The one project it may use (
{id, name}), ornullwhen you allowed every project in the organization. read_onlybooleantrue: it only reads.created_atstring- When you approved it (ISO 8601).
last_used_atstring | null- When it last called Sovaria (updated at most every 5 minutes).
devicestring | null- The browser you approved it from.
Responses
- 200Success.
- 401No valid session or key.
curl -s "$API/api/v1/auth/mcp/connections" \
-H "Authorization: Bearer $ACCESS_TOKEN"Response
{
"connections": [
{
"id": "1b9f6c2e-4d3a-4e8b-9a71-5c0d2e3f4a5b",
"client_name": "Claude",
"client_id": "mcp_Xv3kP9qLm2Rt8sWy1aBc4d",
"org": {
"id": "8a1d2c3b-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
"name": "Acme"
},
"project": {
"id": "5f2c7a91-3b4d-4e6f-8a1b-2c3d4e5f6a7b",
"name": "Storefront"
},
"read_only": false,
"created_at": "2026-10-19T08:14:02.118Z",
"last_used_at": "2026-10-19T09:02:47.530Z",
"device": "Chrome on macOS"
}
]
}DELETE/api/v1/auth/mcp/connections/{grant_id}
Disconnect one of your AI assistants.
Its access and refresh tokens stop working at once (a session it already holds ends within seconds).
- Who
- A project team member's session (the console, or a token from
sov login) asAuthorization: Bearer <token>. The member's role must allow the action. - Also at
/auth/v1/mcp/connections/{grant_id}
Path parameters
grant_idstringrequired- From
GET /api/v1/auth/mcp/connections.
Responses
- 200Success.
- 404No live connection of yours with that id.
- 401No valid session or key.
curl -s -X DELETE "$API/api/v1/auth/mcp/connections/1b9f6c2e-4d3a-4e8b-9a71-5c0d2e3f4a5b" \
-H "Authorization: Bearer $ACCESS_TOKEN"Response
{
"status": "revoked"
}Organization
Owners and admins decide whether members may connect assistants, and can disconnect any member's.
GET/api/v1/org/{org_id}/mcp
Whether the organization's members may connect AI assistants, and (owners and admins) their connections.
- Who
- A project team member's session (the console, or a token from
sov login) asAuthorization: Bearer <token>. The member's role must allow the action.
Path parameters
org_idstringrequired- The organization.
Returns
allowedboolean- Members may connect assistants.
resourcestring- The MCP server URL.
can_manageboolean- You're an owner or admin.
connectionsobject[]- Owners and admins only: members' connections, as in
GET /api/v1/auth/mcp/connectionsplusmember_email.
Responses
- 200Success.
- 404Not a member of that organization.
- 401No valid session or key.
curl -s "$API/api/v1/org/$ORG_ID/mcp" \
-H "Authorization: Bearer $ACCESS_TOKEN"Response
{
"allowed": true,
"resource": "https://api.sovaria.in/mcp",
"can_manage": true,
"connections": [
{
"id": "1b9f6c2e-4d3a-4e8b-9a71-5c0d2e3f4a5b",
"client_name": "Claude",
"client_id": "mcp_Xv3kP9qLm2Rt8sWy1aBc4d",
"org": {
"id": "8a1d2c3b-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
"name": "Acme"
},
"project": {
"id": "5f2c7a91-3b4d-4e6f-8a1b-2c3d4e5f6a7b",
"name": "Storefront"
},
"read_only": false,
"created_at": "2026-10-19T08:14:02.118Z",
"last_used_at": "2026-10-19T09:02:47.530Z",
"device": "Chrome on macOS",
"member_email": "ada@acme.dev"
}
]
}PUT/api/v1/org/{org_id}/mcp
Allow or turn off AI assistants for the organization.
Owners and admins. Turning it off ends every member's connection at once and refuses new ones.
- Who
- A project team member's session (the console, or a token from
sov login) asAuthorization: Bearer <token>. The member's role must allow the action.
Path parameters
org_idstringrequired- The organization.
Body
allowedbooleanrequiredfalseto turn assistants off.
Responses
- 200Success.
- 403Not an owner or admin, or called with an assistant's token.
- 404Not a member of that organization.
- 401No valid session or key.
- 422The body is missing a required field or has a field of the wrong type.
curl -s -X PUT "$API/api/v1/org/$ORG_ID/mcp" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'Content-Type: application/json' -d '{"allowed": false}'Response
{
"allowed": false
}DELETE/api/v1/org/{org_id}/mcp/connections/{grant_id}
Disconnect a member's AI assistant (owners and admins).
- Who
- A project team member's session (the console, or a token from
sov login) asAuthorization: Bearer <token>. The member's role must allow the action.
Path parameters
org_idstringrequired- The organization.
grant_idstringrequired- From
GET /api/v1/org/{org_id}/mcp.
Responses
- 200Success.
- 403Not an owner or admin.
- 404No live connection with that id in the organization.
- 401No valid session or key.
curl -s -X DELETE "$API/api/v1/org/$ORG_ID/mcp/connections/1b9f6c2e-4d3a-4e8b-9a71-5c0d2e3f4a5b" \
-H "Authorization: Bearer $ACCESS_TOKEN"Response
{
"status": "revoked"
}