M2M Authentication
Obtain a Teads MCP access token with OAuth2 client credentials on the public API token route.
Use M2M when the caller is a service: an in-house agent, a scheduled job, or a partner platform.
There is no browser. You mint an access token on the API using the OAuth 2.0 Client Credentials grant, then send it as Authorization: Bearer on MCP.
Your agent or service
→ POST https://public-api.teads.tv/token (client_id + client_secret)
→ access_token
→ POST https://public-api.teads.tv/mcp/v1/agency
Authorization: Bearer <access_token>1. Client id and secret
Teads doesn't provide a self-served way to create a client id and secret for MCP yet. You need to contact your Teads representative to create one.
2. Request an access token
Token URL:
POST https://public-api.teads.tv/tokenSend application/x-www-form-urlencoded (not JSON):
| Field | Value |
|---|---|
grant_type | client_credentials |
client_id | Your M2M client id |
client_secret | Your M2M client secret |
scope | mcp:agency |
POST /token HTTP/1.1
Host: public-api.teads.tv
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&scope=mcp:agencycurl -sS -X POST 'https://public-api.teads.tv/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials' \
-d 'client_id=CLIENT_ID' \
-d 'client_secret=CLIENT_SECRET' \
-d 'scope=mcp:agency'Successful response (200):
{
"access_token": "<jwt>",
"expires_in": 3600,
"token_type": "Bearer"
}access_token is a JSON Web Token (JWT). There is no refresh_token. When expires_in elapses, repeat this POST with the same client id and secret.
Wrong credentials return 401.
3. Call MCP with the token
MCP URL:
POST https://public-api.teads.tv/mcp/v1/agencyEvery MCP request (initialize, tools/list, tool call, resource read) must send:
Authorization: Bearer <access_token>
Accept: application/json, text/event-stream
Content-Type: application/json| Header | Required | Value |
|---|---|---|
Authorization | Yes | Bearer plus the access_token from step 2 |
Accept | Yes | application/json, text/event-stream (both media types) |
Content-Type | Yes | application/json |
Optional if your client uses them: Mcp-Session-Id, MCP-Protocol-Version.
TOKEN='<access_token>'
curl -sS -X POST 'https://public-api.teads.tv/mcp/v1/agency' \
-H "Authorization: Bearer $TOKEN" \
-H 'Accept: application/json, text/event-stream' \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Expected: HTTP 200 and a JSON-RPC result listing tools.
4. Keep the token fresh
- Cache
access_tokenandexpires_in. - Request a new token before expiry (same
client_credentialsPOST). - Replace
Authorizationon subsequent MCP calls.
MCP clients that accept headers
If the client supports custom headers on a remote Streamable HTTP server, pass the current Bearer token (refresh it as in step 4). These interactive setups are useful for local testing; production agents should mint and rotate tokens in code rather than paste them into a config file.
Pick your client:
Create .cursor/mcp.json in your project (or ~/.cursor/mcp.json globally):
{
"mcpServers": {
"teads-agency": {
"url": "https://public-api.teads.tv/mcp/v1/agency",
"headers": {
"Authorization": "Bearer <access_token>"
}
}
}
}Or reference an environment variable:
{
"mcpServers": {
"teads-agency": {
"url": "https://public-api.teads.tv/mcp/v1/agency",
"headers": {
"Authorization": "Bearer ${env:TEADS_MCP_TOKEN}"
}
}
}
}Replace <access_token> / refresh TEADS_MCP_TOKEN when you mint a new token. Cursor does not refresh client-credentials tokens for you.
Errors
| HTTP status | Typical cause |
|---|---|
401 on /token | Wrong client_id or client_secret |
403 on /mcp | Missing Authorization, expired token, scope is not mcp:agency, or the client is not enabled for Teads MCP |
401 / 403 on /oauth2/token with client_credentials | Wrong endpoint — use /token |