Build your own client

Most MCP hosts handle all of this for you; just give them the URL. This page is for anyone writing a client from scratch. The connector follows the MCP authorization spec: OAuth 2.1 with PKCE, dynamic client registration and protected-resource metadata.

Transport

Endpointhttps://api.outlai.io/mcp
TransportStreamable HTTP, stateless. Every response is plain JSON (never an SSE stream), and there's no session to keep.
Protocol versionsNegotiated at initialize: 2024-11-05 through 2026-07-28. Tested most with 2025-06-18.
CapabilitiesTools only.
AuthAuthorization: Bearer olc_… header on every request.
Request limitsBody up to 64 KiB; 15-second timeout.

Discovery

A request without a token gets 401 with a pointer to the metadata:

WWW-Authenticate: Bearer resource_metadata="https://api.outlai.io/.well-known/oauth-protected-resource"
DocumentURL
Protected resource (RFC 9728)/.well-known/oauth-protected-resource (also served under …/mcp)
Authorization server (RFC 8414)/.well-known/oauth-authorization-server

The authorization server advertises:

Client registration

Register once with dynamic client registration (RFC 7591):

POST https://api.outlai.io/oauth/register
Content-Type: application/json

{
  "client_name": "My finance agent",
  "redirect_uris": ["http://127.0.0.1:8765/callback"],
  "grant_types": ["authorization_code", "refresh_token"]
}

The response (201) includes your client_id. Rules for redirect URIs:

Authorization code + PKCE

  1. Create a PKCE verifier (43–128 characters) and its S256 challenge (43 characters). PKCE is required.
  2. Send the user's browser to:
    https://api.outlai.io/oauth/authorize
      ?response_type=code
      &client_id=…
      &redirect_uri=http://127.0.0.1:8765/callback
      &scope=finance:read finance:write
      &state=…
      &code_challenge=…
      &code_challenge_method=S256
      &resource=https://api.outlai.io/mcp
    resource is optional, but if you send it, it must be exactly https://api.outlai.io/mcp.
  3. The user signs in on app.outlai.io, picks what to allow and approves. They have 10 minutes.
  4. Outlai redirects back with code, state and iss. The code is valid for 5 minutes and works once.
  5. Exchange it. Parameters go in the form body, never the query string:
    POST https://api.outlai.io/oauth/token
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=authorization_code&code=…&redirect_uri=…&client_id=…&code_verifier=…

Device flow

For clients that can't open a browser (RFC 8628). Register with the device-code grant in grant_types, then:

POST https://api.outlai.io/oauth/device_authorization
Content-Type: application/x-www-form-urlencoded

client_id=…&scope=finance:read

The response contains:

Show the user the code and the link, then poll /oauth/token every interval seconds with grant_type=urn:ietf:params:oauth:grant-type:device_code, device_code and client_id. While you wait you get authorization_pending; slow down by 5 seconds on slow_down. Stop on access_denied, expired_token or invalid_grant.

Tokens and refresh

{
  "access_token": "olc_…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "olc_…",
  "scope": "finance:read finance:write"
}
ItemLifetime
Access token1 hour
Refresh token30 days, replaced on every use
Connection90 days, then the user must approve again

Scopes

ScopeGrants
finance:readThe 7 read tools
finance:writeThe 7 write tools

Asking for no scope gets finance:read; write is never implied. The user can approve less than you asked for, so read scope in the token response, or call tools/list to see what you got.

Errors

ResponseMeaning
401 {"error":"invalid_token"}Missing, expired or revoked token. Refresh, or start the OAuth flow again.
403 {"error":"origin_not_allowed"}Browser requests from another website are refused. Call the connector from a server or native app.
429 {"error":"rate_limited"}Wait for Retry-After seconds.
429 {"error":"too_many_failed_attempts"}Too many malformed or unknown tokens from your IP in a minute.
503 {"error":"connector_disabled"}The connector is paused. OAuth endpoints answer 503 {"error":"temporarily_unavailable"} at the same time.
Tool result with isError: trueThe tool refused the input or failed. The message says why.

OAuth errors use the standard {"error": "…"} codes. Registration errors add error_description.

Rate limits

WhatLimit
/mcp60 requests/min per connection, 120/min per user
Writes100 per connection per UTC day
/oauth/register10/min per IP
/oauth/authorize30/min per IP
/oauth/token60/min per IP
/oauth/revoke30/min per IP
/oauth/device_authorization10/min per IP

OAuth request bodies are limited to 16 KiB.