Security
How the MCP connection is secured: OAuth with PKCE, scoped keys, short-lived tokens, and the boundaries that never move.
Before you connect an AI model to your revenue data, you'll want to know the worst it could do. This page answers that.
The connection
The server speaks streamable HTTP at https://mcp.affiliaterail.com/mcp. It authenticates with OAuth 2.1, the standard way one app gets limited access to another without your password.
- Client registration: assistants register themselves. They use a Client ID Metadata Document (a small public file that names the app) or dynamic registration. Redirect addresses must be
https, or loopback (localhost,127.0.0.1,[::1]) on any port. Loopback is how command-line and desktop clients receive their callback. - PKCE required, S256 only. PKCE ties a sign-in code to the app that asked for it, so an intercepted authorization code is useless.
- Short lifetimes: authorization codes live five minutes. Access tokens live one hour, and refresh tokens thirty days. Codes and refresh tokens work once only, and a replay is refused.
- Audience binding (RFC 8707): a client can name
https://mcp.affiliaterail.com/mcpas itsresource. Its tokens are then bound to that address and refused anywhere else. - Discovery at
/.well-known/oauth-authorization-serverand/.well-known/oauth-protected-resource. Every 401 carries aWWW-Authenticatechallenge withresource_metadata. So any client that implements the MCP authorization spec finds the authorization flow on its own. Every advertised endpoint stays onmcp.affiliaterail.com.
Some tools can send a header directly, like Claude Code and command-line clients. They can skip OAuth and present an API key you made yourself as a bearer token. Same scopes, same revocation.
You never handle a key
The Allow screen runs on our own dashboard, outside the assistant, behind your normal sign-in, second factor and all. It shows who's asking. It also shows which organization they'd see and what they could do. Nothing is created until you press Allow.
We then make the credential on our side and never show it to you, so there's no key you could leave sitting in a chat window by mistake. The assistant receives a short-lived token that's bound to that credential and dies with it.
The connection appears under Settings, API keys, Connected agents, named after the assistant. Press Disconnect and every token made from it stops working on its very next request, whichever app is holding it. The others carry on. Connecting the same assistant again reuses that row, so the list shows exactly what's connected.
If a browser can't hold a session, https://mcp.affiliaterail.com/oauth/authorize/key still takes a pasted key and reaches the same place. It's the fallback.
Scopes decide what the assistant may do
A scope is a permission that comes with a connection. There are two.
mcp:read: the 22 read tools, including the setup guide and the install check. Nothing can change. A read-scoped API key of your own makes a connection with only this scope.mcp:write: the 22 write tools as well. They cover program settings, branding, commission rules, groups, partner links, coupons and clearing the demo data. They also cover the commission and application review queues, partner invites, risk flags and the resources shelf, and they can send a campaign or pause a sequence.
The write scope is checked when tools are registered, and again on every call. Every write tool returns a confirmation card first, with names resolved, a field-level diff and nothing applied. The change only applies when the tool is called again with confirm: true and the preview_token from that card.
Pressing Allow on the consent screen grants both scopes. So a connected assistant works without a second trip through the browser. Keep the write tools on "needs approval" in the assistant's own permissions, so a human confirms every change. For an assistant that may only ever read, connect it with a read-scoped API key from Settings instead.
Five write tools carry a destructive hint, so clients ask before running them:
- rejecting a commission
- rejecting an application
- deleting a resource
- clearing the demo data
- sending a campaign
A sent campaign puts real mail in real inboxes and can't be recalled.
What's never exposed
Some capability is left out of the MCP server on purpose, whatever you allowed:
- Moving money: executing payout batches, editing payout connections, changing anyone's bank details.
- Minting credentials: creating or revoking API keys, webhook secrets, domain settings. A compromised assistant must not be able to issue itself a better key.
- Destruction: deleting partners, customers or programs.
- Billing and team: plan changes, and adding or removing team members. Roles are set in the dashboard; see who on your team can do what.
Only the dashboard, with a signed-in person, can do those. This is the standing shape of the server, and there's no setting you could relax by accident.
Everything is attributed
Every write an assistant performs goes through the same ledger paths as a dashboard click. It's written to the audit log as the assistant's connection, with the state before and after. A commission approved over MCP shows in its history like any other approval, marked as coming from MCP. There's no anonymous path.
Rate limits
Each connection can make 25 tool calls a second, the same as an API key. Past that, the assistant gets an error that says "Too many requests" and should wait a moment. Signing in and refreshing a token are limited to 30 tries in ten minutes per network address. That's far more than a normal assistant uses.
Practical hygiene
- Use one connection per assistant, which is what Allow gives you. Then you can revoke exactly one. Name any key you make by hand the same way.
- Removing a connector inside the assistant ends it here too, when the client tells the server (it calls
/oauth/revoke). If you're not sure yours does, press Disconnect under Connected agents as well. - Treat prompt content as untrusted when the assistant also browses the web. The write tools' approval prompts are your backstop, so read them before confirming.