Discovery flow
- The client POSTs to
/api/mcpwithout a token and receives401 Unauthorizedwith aWWW-Authenticateheader pointing athttps://mcp.mavel.ai/.well-known/oauth-protected-resource(RFC 9728). - That document names the authorization server; the client then fetches
/.well-known/oauth-authorization-server(RFC 8414) for the endpoints: authorize, token, register, revoke. The flow is authorization-code + PKCE (S256), public clients only. - The client self-registers (RFC 7591 dynamic client registration), runs the
PKCE flow (you approve on the Mavel consent page), and retries with
Authorization: Bearer <token>.
Scopes
Registering a client (RFC 7591)
redirect_uris: 1-10 URIs;https://only, excepthttp://localhost/http://127.0.0.1. URIs are matched on scheme + host + path (query and fragment ignored).- Only public clients are issued. There is no
client_secret, andtoken_endpoint_auth_methodis alwaysnone.
Tokens
- Access tokens expire after 1 hour (
expires_in: 3600). - Refresh tokens live 90 days; each refresh rotates the pair (the consumed refresh token is revoked and a fresh access + refresh pair is issued).
- Revoke with
POST /api/mcp/oauth/revokeusing either token: both belong to the same grant, so revoking one kills both (RFC 7009 semantics: HTTP 200 regardless).
Protocol surface
POST /api/mcp accepts JSON-RPC 2.0 (single object or batch array). Supported
methods: initialize, tools/list, prompts/list, tools/call,
prompts/get. Add Accept: text/event-stream only if your client requires
SSE responses.
Rate limit: 200 requests / minute / authenticated user; exceeding it
returns HTTP 429 with X-RateLimit-* headers.
Error codes
Strict argument validation
tools/call checks the top-level keys of arguments against the tool’s
declared schema before running it. An argument key the schema doesn’t list
(a typo like offfset for offset, for example) used to be silently
dropped; it now fails the whole call with -32602. The error message
itself names every offending key and the tool’s legal argument names, for
example unknown argument "offfset" for tool "list_chats" (valid: project_id, date_from, ...), so the guidance holds even on clients that
strip error.data. The same list is also available structured, as
error.data.valid_keys.
If a key you’re sure is spelled correctly still gets rejected, your cached
tool schema may be stale: re-fetch tools/list (see Server version below
for how to detect a stale cache automatically).
Server version
serverInfo.version, returned at initialize and by an unauthenticated
GET request to the server URL, is the deployed commit SHA. Every
non-array tools/call result also carries that same value as a plain
server_version field in its response body, alongside
_meta.server_version (the MCP-spec channel): the body field is the
reliable one, since some MCP clients strip _meta (and error.data)
before the model ever sees them. -32601 / -32602 errors carry
server_version in error.data too, for clients that do deliver it, so
even a failed call can tell you which deploy answered it.
Compare the stamp across calls in the same session: if it changes, the
server redeployed and the tool catalog may have changed underneath you, so
re-fetch tools/list. There’s no push notification for this
(capabilities.tools.listChanged is false by design, since this is a
stateless request/response transport with no server-to-client channel to
push on), so comparing the version yourself is the only way to detect a
mid-session deploy.