M2M tokens
Authenticate an agent to its Virtual MCP with short-lived access tokens minted through the OAuth 2.0 client-credentials grant. Machine-to-machine (M2M) tokens are the more secure alternative to a bearer key: the agent holds a client ID and secret, exchanges them for a short-lived access token, and sends that token, not the long-lived secret, on each MCP request.
M2M tokens apply to agent identities only. A Virtual MCP offered to human members uses interactive OAuth; a Virtual MCP scoped to an agent can authenticate with a bearer key, an M2M token, or both.
How it works
MintMCP provisions a client for the agent's Virtual MCP, and the agent runs a standard client-credentials exchange:
- An admin opens the agent Virtual MCP's settings and clicks Create M2M client. MintMCP mints a client ID and a client secret scoped to that one Virtual MCP and shows the secret once.
- The runtime stores the client ID and secret in its secret manager.
- On startup, and whenever its token is close to expiry, the agent POSTs the credentials to the Virtual MCP's token endpoint and receives a short-lived access token.
- The agent sends the access token as
Authorization: Bearer <access_token>on every request to the MCP endpoint. When the token expires, it exchanges the credentials again.
Both endpoints come from the Virtual MCP's settings, and the exchange looks like this:
# Exchange client credentials for an access token
curl -X POST "https://app.mintmcp.com/o/<org-slug>/s/<server-slug>/m2m/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data "grant_type=client_credentials&client_id=<client_id>&client_secret=<client_secret>"
# => {"access_token": "...", "expires_in": 3600, "token_type": "bearer"}
# Call the agent's MCP with the returned token
# Authorization: Bearer <access_token>
# https://app.mintmcp.com/o/<org-slug>/s/<server-slug>/mcp
The token endpoint comes in two shapes, .../o/<org-slug>/s/<server-slug>/m2m/token and .../s/<gateway-id>/m2m/token, so copy the exact one shown alongside the client credentials in the admin UI rather than deriving it from the MCP URL. The runtime reuses the access token until the expires_in window the endpoint returns elapses (the sample above shows 3600 seconds), then exchanges the credentials again. Only grant_type=client_credentials is accepted, and the presented client_id must match the one bound to the Virtual MCP, so a token minted for one agent can't be replayed against another.
Why M2M over a bearer key
Both mechanisms land on the same gateway as the same agent principal and inherit the same tool curation and rules. They differ in what the agent holds and what travels on each request:
| Property | Bearer key | M2M token |
|---|---|---|
| What each request carries | The long-lived secret itself | A short-lived access token; the client secret only appears during the token exchange |
| Default lifetime | No expiry unless you set one (up to 1 year) | Access tokens are short-lived and re-minted on demand |
| Rotation | Mint a new key, then delete the old one | Add a new client secret while the old one still works, then revoke the old one |
| Containment | Revoke the key | The token expires on its own, or revoke a client secret |
| Per-use audit | Create and delete events, plus a last-used timestamp | Every token exchange is a discrete audit event |
The credential the agent handles on each request is disposable: the access token turns over on its own, and the client secret stays out of the request path, appearing only during the token exchange. You rotate the client secret on your own schedule.
Client secrets and rotation
Each agent Virtual MCP has one M2M client and can hold more than one client secret at a time:
- One-time display. MintMCP shows a new secret once, in a copy panel, and stores only an encrypted form. Copy it into your secret manager then, or mint a new one.
- Overlapping secrets. Click Create new secret to add a second live secret. Creating a secret does not revoke existing secrets or already-issued access tokens, so you can roll the runtime onto the new secret with no cutover window.
- Independent revocation. Revoke secret invalidates one secret. New token exchanges with it fail immediately; tokens already issued from it keep working until they expire.
Scoping
M2M tokens carry the same tight scope as any agent credential:
| Scope | What it means |
|---|---|
| VMCP-bound | The token authenticates against one Virtual MCP; the gateway checks both the client ID and the organization on every request |
| Principal-bound | The token acts as exactly one agent identity, never a human user |
| Policy-bound | The token inherits the Virtual MCP's tool curation, rules, and middleware; it authenticates the agent, it doesn't elevate it |
| Org-scoped | Tokens never cross organizations |
For how access policies shape what any agent credential can reach, see role-based access control.
Audit
M2M authentication is auditable at a finer grain than a static key, because the token exchange itself is logged:
- Token exchange writes a discrete event on every attempt:
agent_m2m_token_issuedon success,agent_m2m_token_deniedwhen the client ID is wrong or the grant is unsupported, andagent_m2m_token_failedon an upstream error. Each records the client ID, response status, request ID, and user agent. - Lifecycle events cover the client and its secrets:
agent_m2m_client_created,agent_m2m_secret_created, andagent_m2m_secret_revoked. - Tool calls made with the token attribute to the agent identity, the same as any other agent traffic, so agent activity stays distinguishable from human activity in the gateway audit log.
Together these show not just what the agent did, but when and how it authenticated. A bearer key records its creation and deletion and a last-used timestamp, so you can see that a key exists and when it was last used, but not each time it authenticated.
When to use M2M tokens
- Sandboxed and ephemeral runs are the strongest fit. When an agent calls MCP from a throwaway sandbox (a background coding agent, a CI job, a scheduled run on compute that's torn down afterward), the credential on each request is a short-lived token the run mints at startup, not a durable key baked into the environment. The token expires on its own, so the sandbox never has to hold a long-lived key that outlives the run.
- Reach for M2M tokens whenever the runtime can run a client-credentials exchange (most CI platforms and standing services can) and you want short-lived credentials with per-exchange audit. This is the more secure option for agents that can support it.
- Use a bearer key when the client can only send a static
Authorization: Bearerheader and can't manage a token exchange, or when you want the simplest possible setup.
Related
- Bearer keys: the long-lived alternative, and how the two compare
- Agent identities: the non-human principal an M2M client is scoped to
- Role-based access control: how access policies shape what an agent credential can reach
- Audit and observability: where token exchanges and agent tool calls land