Quickstart
Connect an AI agent to Atribu over OAuth — no account required before you start
The Atribu MCP server speaks OAuth 2.1 with Dynamic Client Registration, so an agent can connect a human who does not yet have an Atribu account. The agent registers a client, builds an authorization URL and hands it over; the human signs up or signs in inside that hand-off and approves.
Claude Desktop, Claude.ai, ChatGPT and Cursor do all of this for you
Add https://mcp.atribu.app/mcp as a custom connector and the client runs
the whole flow below on its own — the exact steps per client are on
Connect an AI client.
The steps here are for an agent, script or CI job driving the flow itself.
Register a client and build the authorize URL
curl -sX POST https://www.atribu.app/oauth/mcp/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "My Agent",
"redirect_uris": ["http://127.0.0.1:7788/callback"],
"token_endpoint_auth_method": "none",
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"scope": "mcp:read"
}'{ "client_id": "mcpc_9f2c…", "redirect_uris": ["http://127.0.0.1:7788/callback"], "scope": "mcp:read" }Three scopes exist and each is a separate checkbox on the consent screen. Ask for the least you need:
| scope | what it grants |
|---|---|
mcp:read | analytics — campaigns, creatives, ROAS. PII masked. |
mcp:read_pii | the same, with customer names/emails/phones, where the workspace allows it |
mcp:write | conversion write-back to Meta CAPI, on workspaces where the user is an admin with write-back on |
import base64, hashlib, os, urllib.parse
verifier = base64.urlsafe_b64encode(os.urandom(32)).rstrip(b"=").decode()
challenge = base64.urlsafe_b64encode(
hashlib.sha256(verifier.encode()).digest()
).rstrip(b"=").decode()
state = base64.urlsafe_b64encode(os.urandom(16)).rstrip(b"=").decode()
url = "https://www.atribu.app/oauth/mcp/authorize?" + urllib.parse.urlencode({
"response_type": "code",
"client_id": "mcpc_9f2c…",
"redirect_uri": "http://127.0.0.1:7788/callback",
"scope": "mcp:read",
"state": state,
"code_challenge": challenge,
"code_challenge_method": "S256",
"resource": "https://mcp.atribu.app/mcp",
})Endpoints are discoverable at
https://www.atribu.app/.well-known/oauth-authorization-server, and the
resource server advertises itself at
https://mcp.atribu.app/.well-known/oauth-protected-resource.
Your agent gives the human a URL. They open it, create their account, approve. They come back.
Print it and wait — this is the human's only step.
- No Atribu account yet? The page sends them to sign in; an unrecognised email goes straight to signup with the pending authorization preserved. After they confirm their email, the confirmation link returns them to your consent screen, not to a dashboard.
- The consent screen shows your
client_name, one checkbox per scope you asked for, and the host behind yourredirect_uri(loopback is labelled as a local application). A user with no workspace yet reads "You have no workspace yet — your agent will create one after you approve." - Approve redirects to your
redirect_uriwithcodeand yourstate. Comparestatebefore you usecode.
Deny, and an expired or malformed request, come back to the same
redirect_uri with error + error_description rather than stranding either
of you on a page — see the error table in the API quickstart.
Exchange the code for a token
curl -sX POST https://www.atribu.app/oauth/mcp/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d grant_type=authorization_code \
-d code=THE_CODE_FROM_THE_CALLBACK \
-d redirect_uri=http://127.0.0.1:7788/callback \
-d client_id=mcpc_9f2c… \
-d code_verifier=THE_VERIFIER_FROM_STEP_1{
"access_token": "atb_user_…",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "atb_refresh_…",
"scope": "mcp:read"
}Access token: 1 hour. Refresh token: 60 days, rotating — persist the new one on every refresh, because replaying an old one revokes the family.
Make your first call
curl -sX POST https://mcp.atribu.app/mcp \
-H "Authorization: Bearer atb_user_…" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"whoami","arguments":{}}}'It costs zero units and returns the workspaces and profiles the caller can
reach, the default currency, the PII mode and whether write-back is enabled — so
the model stops guessing. The same token also works on /api/v1 since #1084 —
GET /api/v1/workspaces is its REST equivalent, and profile-scoped routes want an
explicit ?profile_id=. See API authentication.
Using a static MCP token instead
For a script or CI job that belongs to one person and needs no consent screen, mint a token by hand:
- Log in to Atribu
- Go to Developer in the sidebar
- Switch to the MCP Tokens tab
- Click New token, select at least Read analytics, and copy it
Save your token
The token starts with atb_user_ and is shown only once. Store it somewhere secure.
claude mcp add atribu --transport http \
https://mcp.atribu.app/mcp \
--header "Authorization: Bearer atb_user_YOUR_TOKEN"Add to your Cursor MCP settings (.cursor/mcp.json):
{
"mcpServers": {
"atribu": {
"url": "https://mcp.atribu.app/mcp",
"headers": {
"Authorization": "Bearer atb_user_YOUR_TOKEN"
}
}
}
}Recommended: one-click connector
For Claude Desktop, the simplest path is the OAuth custom connector — no token needed. See Connect an AI client. The manual config below is an alternative if you prefer a static token.
Add to your Claude Desktop config (claude_desktop_config.json):
{
"mcpServers": {
"atribu": {
"url": "https://mcp.atribu.app/mcp",
"headers": {
"Authorization": "Bearer atb_user_YOUR_TOKEN"
}
}
}
}Any MCP client that supports Streamable HTTP transport works. Configure it with:
- URL:
https://mcp.atribu.app/mcp - Transport: Streamable HTTP
- Auth header:
Authorization: Bearer atb_user_YOUR_TOKEN
Verify the connection
curl https://mcp.atribu.app/health/live{ "status": "ok" }Example conversation
Here's what a typical interaction looks like in Claude Code:
You: What happened to our ad performance last week vs the week before?
Claude: I'll compare the two periods for you.
[calls compare_periods with last week vs prior week]
Your ad performance improved last week:
- Spend: $2,450 → $2,680 (+9.4%)
- Revenue: $8,200 → $11,400 (+39.0%)
- ROAS: 3.35 → 4.25 (+26.9%)
- Conversions: 42 → 58 (+38.1%)
The jump in revenue is driven primarily by "Summer Sale - Retargeting"
which generated $4,200 in attributed revenue at a 6.8x ROAS.
Data as of: 2026-04-14T18:30:00Z | Model: last_touch | Currency: USDWorkspace resolution
If you belong to multiple workspaces, the AI tool will ask you to specify which one. If you have only one workspace with one profile, it's selected automatically.