MCP Security

How OAuth connections and personal access tokens are scoped, revoked and audited, and what to do the moment a credential leaks.

Threat model

The MCP server accepts two credentials over HTTPS, an OAuth 2.1 access token you granted by signing in or a personal access token, and runs the request inside the same tenancy boundary as the REST API. The most common risks are:

  1. A token leaking into LLM conversation logs. Claude, Cursor, and ChatGPT Desktop all log the session. A developer sharing a transcript can leak an Authorization header if the client logs it.
  2. Over-privileged credentials. Minting a personal access token with every ability ticked, or connecting over OAuth from an account with more reach than the task needs, makes a leak costly.
  3. Accidental writes. An eager agent executing a tool that turns out to change state.

The server is designed for the first two and treats the third as a prompt-engineering concern, marking every tool that changes data as destructive so a client asks before running it. Below are the controls.

OAuth connections

  • Consent, not a pasted secret. You sign in to the application, with two-factor authentication and passkeys applying as usual, and read a consent screen that names the client, the signed-in account, the reach of the connection and where you will be sent back to. Nothing is copied to disk.
  • Short-lived access. An access token lasts one hour. The refresh token lasts 30 days from its last use and is rotated on every renewal; presenting a spent one is refused, so a copied refresh token dies the moment the client renews.
  • Public clients with PKCE. Every client registers dynamically as a public client and must present an S256 code challenge; there is no client secret to leak.
  • One scope, your own reach. The connection acts as you on the team you were working in, with every ability your role grants there, inside the same tenant boundary as the application. A connection that should be narrower is a personal access token instead.
  • Disconnect ends it. Disconnecting the server inside the client discards its tokens; the access token expires within the hour and the refresh token cannot be renewed once discarded.

Personal access tokens

Prefix

Every token starts with zg_, which makes one easy to find in a repository, a transcript or a log with a search; add the prefix to your own secret-scanning rules so a pushed token is caught before it is used.

Mandatory team scope

Every token carries scope:team:<uuid> frozen at mint time. A leaked token is constrained to exactly one team and can never pivot. Project-level scope:project:<uuid> tuples tighten this further, and a guest's token is always project-scoped.

Minimum-privilege defaults

Tick only the abilities the task needs; most tokens need two or three. An agent that reads the project record and the activity log needs project:view-any, project:view and activity:read, nothing more.

Expiry

Personal access tokens do not expire automatically. Long-lived tokens (CI, dashboards, a standing agent) should be rotated on your own cadence: revoke the old token, mint a fresh one with the same abilities, and update the client config.

Revocation

Revoke a token under Account Settings โ†’ API Tokens, or with revoke_token from another token. Revocation is immediate; any client still holding the token gets AUTHENTICATION_REQUIRED on its next call.

Audit trail

Every change a tool makes is recorded in the activity log with you as the causer, the subject it changed and an actor_kind of mcp, which distinguishes agent-made changes from the application's and the REST API's. Review what an agent did on the activity log page, or read the log over the API with the get_activity_log tool or the GET /v1/activity REST endpoint.

Leaked-credential playbook

For a personal access token:

  1. Revoke the token under Account Settings โ†’ API Tokens.
  2. Review the activity log for the last 24 hours for changes made through the API or MCP by your account.
  3. If writes happened you did not authorise, tell your workspace administrator, naming the token's id (never its value).
  4. Mint a replacement token with the same abilities.
  5. Update the client config (Claude Desktop, Cursor, and so on) with the new token.

For an OAuth connection whose client or device you no longer trust: disconnect the server inside the client, change your Zuse Bifrost password (which signs out every browser session), and review the activity log the same way. Access the connection still held expires within the hour.

How is this guide?

Last updated on

On this page

What changed recentlyEvery release of Zuse Bifrost, with the pages it touched.

For AI agents: llms.txt ยท llms-full.txt