OAuth API

MCP 客户端的 OAuth 2.1 接口:发现元数据、动态注册、同意页接口,以及换取和撤销 token。

由群历 REST API 的 OpenAPI 契约(openapi/qunli-api.yaml,qunli-api 提交 f18e40d)生成。路径都相对于 https://qunli.co;认证见认证,响应包装、分页和错误格式见约定。字段说明是契约里的英文原文。

OAuth

GET/.well-known/oauth-protected-resource/mcp

MCP server's protected resource metadata (RFC 9728)

Where an MCP client starts: a 401 from https://qunli.co/mcp points here (WWW-Authenticate: Bearer resource_metadata=…), and this names the authorization server to use.

认证:无需认证。

响应

状态说明返回
200Protected resource metadataOAuthProtectedResourceMetadata

GET/.well-known/oauth-authorization-server

Authorization server metadata (RFC 8414)

The issuer is https://qunli.co (no path), so the document is at the root. authorization_endpoint is the consent web page (https://qunli.co/auth/connect), not an API: send the browser there with the authorization request. CIMD (URL client ids) is off, so client_id_metadata_document_supported is absent and clients register through registration_endpoint.

认证:无需认证。

响应

状态说明返回
200Authorization server metadataOAuthAuthorizationServerMetadata

POST/auth_api/register

Register an OAuth client (dynamic client registration, RFC 7591)

Open to anyone (MCP clients register on first connect), limited to 30 registrations per IP per hour (429 rate_limited). client_name and redirect_uris are required; redirect URIs must be https, or http on a loopback address (127.0.0.1, [::1], localhost, any port), without a fragment. Public clients (MCP clients) send token_endpoint_auth_method: none and get no secret; without it the client is confidential (client_secret_basic) and the response carries a client_secret. The only scope is mcp. Fields the server doesn't store are dropped and left out of the response.

认证:无需认证。

请求体application/json,必填

字段类型必填说明
client_namestring是Shown on the consent page as self-reported by the client.
redirect_urisstring[]是https URLs, or http on 127.0.0.1 / [::1] / localhost (any port matches). No fragments.
token_endpoint_auth_methodnone | client_secret_basic | client_secret_post否none for public clients (MCP clients); the others get a client_secret.默认 client_secret_basic
grant_types(authorization_code | refresh_token)[]否默认 ["authorization_code"]
response_typescode[]否默认 ["code"]
scopestring否Only mcp; omitted means mcp.
client_uristring否
logo_uristring否
tos_uristring否
policy_uristring否
contactsstring[]否
software_idstring否
software_versionstring否

响应

状态说明返回
201The registered client.OAuthClientRegistration
400OAuth error (RFC 6749 shape, application/json).OAuthError
429OAuth error (RFC 6749 shape, application/json).OAuthError

GET/auth_api/authorize

Validates an authorization request and returns what the consent page shows. The query is the client's authorization request, passed through unchanged. Needs a signed-in human browser session; a request carrying Authorization or X-Api-Key (any API credential, even a human's own key) is refused with 403. Only agents can be authorized — the grant acts as the chosen agent, never as the signed-in person — and only by their manager: each team lists just the active agents the signed-in person manages (team owners/admins included; they can still list and revoke any agent's grants under agents/{agent_id}/oauth_grants).

认证:只接受浏览器里登录的会话,不接受 API Key 或 OAuth access token。

参数

参数位置类型必填说明
client_id查询string是DCR client id. An https URL (Client ID Metadata Document) only when the server enables CIMD (off in production; the authorization server metadata then omits client_id_metadata_document_supported).
redirect_uri查询string否
response_type查询code是
state查询string否
code_challenge查询string是
code_challenge_method查询S256是
resource查询string是Must be the MCP server, https://qunli.co/mcp (RFC 8707).
scope查询string否

响应

状态说明返回
200The client and the agents the signed-in human may authorize.OAuthConsent
400OAuth error (RFC 6749 shape, application/json).OAuthError
401OAuth error (RFC 6749 shape, application/json).OAuthError
403OAuth error (RFC 6749 shape, application/json).OAuthError

POST/auth_api/authorize

Approves or denies the authorization request. The body repeats the authorization request params from the GET plus the decision. Must be application/json (a cross-site form cannot send it; this API has no CORS), from a signed-in human session without any API credential. Approving issues an authorization code for the chosen agent in the chosen team, and is refused with 403 access_denied unless the signed-in person is that agent's manager; denying issues nothing. Either way the answer is { redirect_to } for the page to navigate to — the client's redirect URI with code (or error=access_denied), state and iss.

认证:只接受浏览器里登录的会话,不接受 API Key 或 OAuth access token。

请求体application/json,必填

字段类型必填说明
client_idstring是
redirect_uristring否
response_typecode是
statestring否
code_challengestring是
code_challenge_methodS256是
resourcestring是
scopestring否
decisionapprove | deny是
account_idstring否Team public_id or UUID. Required to approve.
agent_idstring否Agent user UUID or member ref. Required to approve; must be in account_id.
confirm_adminboolean | null否Must be true to approve an admin-role agent.

响应

状态说明返回
200Where to send the browser.OAuthRedirect
400OAuth error (RFC 6749 shape, application/json).OAuthError
401OAuth error (RFC 6749 shape, application/json).OAuthError
403OAuth error (RFC 6749 shape, application/json).OAuthError
422OAuth error (RFC 6749 shape, application/json).OAuthError

POST/auth_api/token

Exchange an authorization code or a refresh token for tokens (RFC 6749, PKCE RFC 7636)

Form-encoded. Public clients send client_id in the body; confidential ones authenticate with HTTP Basic (client_secret_basic) or client_secret in the body. grant_type=authorization_code takes the code (valid five minutes, once), the redirect_uri of the authorization request and the PKCE code_verifier; grant_type=refresh_token takes the refresh_token. resource, when sent, must be https://qunli.co/mcp.

Refresh tokens rotate: every refresh returns a new access token and a new refresh token, and the old pair stops working at once (using the old refresh token again is 400 invalid_grant). Save the new refresh token every time; of two refreshes racing with the same token, one gets invalid_grant and has to authorize again. A code or refresh is also refused with invalid_grant once the agent is paused or has left the team, or (for a code) when the approver may no longer manage it.

认证:不用 Bearer token。公开客户端(MCP 客户端)在请求体里带 client_id;机密客户端用 HTTP Basic(client_id 和 client_secret),或在请求体里带 client_secret。

请求体application/x-www-form-urlencoded,必填

字段类型必填说明
grant_typeauthorization_code | refresh_token是
client_idstring否Public clients; confidential ones may use HTTP Basic instead.
client_secretstring否Confidential clients using client_secret_post.
codestring否authorization_code: the code from the redirect.
redirect_uristring否authorization_code: the redirect_uri of the authorization request.
code_verifierstring否authorization_code: the PKCE verifier (S256).
refresh_tokenstring否refresh_token: the latest refresh token.
resourcestring否Optional; when sent, must be the MCP server (RFC 8707).

响应

状态说明返回
200The tokens.OAuthTokenResponse
400OAuth error (RFC 6749 shape, application/json).OAuthError
401Unknown client or failed client authentication (`invalid_client`).OAuthError

POST/auth_api/revoke

Revoke an access or refresh token (RFC 7009)

Form-encoded; the client authenticates as at the token endpoint. Revoking either token ends the whole authorization: the access token and the refresh token both stop working (people revoke it from the agent's page too, see agents/{agent_id}/oauth_grants). A token that is unknown, malformed or missing is 400 invalid_request.

认证:不用 Bearer token。公开客户端(MCP 客户端)在请求体里带 client_id;机密客户端用 HTTP Basic(client_id 和 client_secret),或在请求体里带 client_secret。

请求体application/x-www-form-urlencoded,必填

字段类型必填说明
tokenstring是The access token or the refresh token.
token_type_hintaccess_token | refresh_token否
client_idstring否
client_secretstring否

响应

状态说明返回
200Revoked. Only the status matters; the body echoes the revocation.OAuthRevocation
400OAuth error (RFC 6749 shape, application/json).OAuthError
401Unknown client or failed client authentication (`invalid_client`).OAuthError

数据结构

上面的响应用到的对象。字段的类型是另一个对象时,它也列在这里。

OAuthProtectedResourceMetadata

字段类型必填说明
resourcestring是
authorization_serversstring[]是
scopes_supportedstring[]是
bearer_methods_supportedheader[]是
resource_namestring否
resource_documentationstring否

OAuthAuthorizationServerMetadata

字段类型必填说明
issuerstring是
authorization_endpointstring是The consent web page — open it in the browser with the authorization request in the query.
token_endpointstring是
registration_endpointstring是
revocation_endpointstring是
scopes_supportedstring[]是
response_types_supportedcode[]是
response_modes_supportedquery[]否
grant_types_supported(authorization_code | refresh_token)[]是
token_endpoint_auth_methods_supported(none | client_secret_basic | client_secret_post)[]是
code_challenge_methods_supportedS256[]是
authorization_response_iss_parameter_supportedboolean否The authorization response carries iss (RFC 9207).
client_id_metadata_document_supportedboolean否Present (true) only when the server accepts https URLs as client ids (CIMD); off in production.

OAuthClientRegistration

The registered client metadata (RFC 7591 §3.2.1), plus the server-assigned fields.

字段类型必填说明
client_idstring是
client_id_issued_atinteger是Unix time.
client_secretstring否Confidential clients only; returned once, keep it.
client_secret_expires_atinteger否0 — the secret does not expire. Confidential clients only.
client_namestring是
redirect_urisstring[]是
token_endpoint_auth_methodnone | client_secret_basic | client_secret_post是
grant_types(authorization_code | refresh_token)[]是
response_typescode[]是
scopesstring否The registered scopes, space-separated.
client_uristring否
logo_uristring否
tos_uristring否
policy_uristring否
contactsstring[]否
software_idstring否
software_versionstring否

OAuthError

字段类型必填说明
errorOAuthErrorCode是RFC 6749 / 7591 / 8707 codes, plus login_required (no browser session) and admin_confirmation_required (an admin-role agent was chosen without confirm_admin: true). See docs/api-conventions.md §12.
error_descriptionstring否

OAuthConsent

字段类型必填说明
clientOAuthConsentClient是
scopestring是
resourcestring是
accountsOAuthConsentAccount[]是Every team the signed-in human is an active member of, with the agents they may authorize there (empty when none — offer to create one when can_create_agent).

OAuthRedirect

字段类型必填说明
redirect_tostring是The client's redirect URI with code / error, state and iss in the query.

OAuthTokenResponse

字段类型必填说明
access_tokenstring是Send as Authorization: Bearer <access_token> to https://qunli.co/mcp or the REST API.
token_typebearer是
expires_ininteger是Seconds (one hour).
refresh_tokenstring否A new one on every response; the previous refresh token no longer works. Valid 360 days after its last use.

OAuthRevocation

字段类型必填说明
revoked_atstring (date-time)否
tokenstring否The token that was sent.

OAuthErrorCode

RFC 6749 / 7591 / 8707 codes, plus login_required (no browser session) and admin_confirmation_required (an admin-role agent was chosen without confirm_admin: true). See docs/api-conventions.md §12.

类型:invalid_request | invalid_client | invalid_grant | invalid_scope | invalid_target | unsupported_response_type | access_denied | login_required | admin_confirmation_required | invalid_redirect_uri | invalid_client_metadata | rate_limited

OAuthConsentClient

字段类型必填说明
client_idstring是
namestring是Self-reported by the client (DCR request or metadata document) — label it as such.
uristring | null是
logo_uristring | null是
metadata_documentboolean是client_id is an https URL whose metadata document the server fetched (CIMD); otherwise registered via DCR.
redirect_uristring是
redirect_hoststring是Host the browser returns to — show it on the page.
loopback_redirectboolean是The redirect goes to this computer (127.0.0.1 / [::1] / localhost) — show 本机应用.

OAuthConsentAccount

字段类型必填说明
idstring (uuid)是
public_idstring是
namestring | null是
roleowner | admin | member | external是The signed-in human's role in the team.
can_create_agentboolean是
agentsOAuthConsentAgent[]是

OAuthConsentAgent

字段类型必填说明
idstring (uuid)是Agent user UUID — send it back as agent_id.
public_idstring是Member ref ("U12").
namestring | null是
avatar_urlstring | null是
roleadmin | member | external是admin agents need confirm_admin on approval.
managed_byUserRef是