Documentation

How to Connect

Authentication reference for the ZopNight MCP server — OAuth 2.1 with Dynamic Client Registration, Protected Resource Metadata, refresh-token rotation with reuse detection, and Personal Access Tokens for non-browser clients.

The ZopNight MCP server accepts two auth methods. Both arrive as Authorization: Bearer <token> on every request; the gateway resolves identity, role, and RBAC per call.

  • OAuth 2.1 (recommended) for interactive MCP clients that can drive a browser consent flow — Cursor, Claude Desktop, Claude Code, ChatGPT, VS Code, and the rest.
  • Personal Access Token for CI, scripts, and non-browser clients that need a long-lived credential.

Client-side setup (config paths, install links, screenshots) lives in the product docs at zop.dev/docs/zopnight/integrations/mcp and zop.dev/docs/zopday/integrations/mcp. This page is the protocol reference: what the server implements and what a client sees on the wire.

Enable MCP Server (admin, one-time)

MCP is enabled per organisation. An admin needs to turn it on first.

  1. Open organisation settings

    Log in to ZopNight, open Settings → Organisation, and scroll to the MCP Server section in the Organisation details card.

  2. Toggle MCP Server on

    The status badge flips from "Inactive" to "Active". The Server URL becomes visible, and the Write access picker unlocks.

  3. Choose a Write access level

    Read-only ships. Raise it only after reading the security disclosure below.

OAuth 2.1

OAuth 2.1 is the recommended path for any MCP client that can open a browser. It builds on RFC 6749 (authorization code + refresh), RFC 6750 (Bearer usage), RFC 7591 (Dynamic Client Registration), RFC 9728 (Protected Resource Metadata), RFC 8414 (Authorization Server Metadata), and RFC 9700 §4.14.2 (refresh-token reuse detection). PKCE is required.

End-to-end flow

  1. Client discovers the resource

    Client sends any request to the MCP URL without a token. The server responds 401 Unauthorized with a WWW-Authenticate: Bearer challenge naming the resource. The client fetches the resource's metadata at /.well-known/oauth-protected-resource (RFC 9728) and reads authorization_servers from it.

  2. Client discovers the authorization server

    Client fetches /.well-known/oauth-authorization-server (RFC 8414) — and, if the AS lives under a path prefix, the path-suffixed form /.well-known/oauth-authorization-server/oauth. The metadata document lists authorization_endpoint, token_endpoint, registration_endpoint, revocation_endpoint, supported grant types, PKCE methods, and scopes.

  3. Client registers dynamically (RFC 7591)

    Client POSTs to /oauth/register with a name and redirect URIs. Registration is open by design — no bearer or admin approval — because MCP clients discover the resource without prior arrangement. The response returns client_id and a single-use client_secret the client persists locally. Re-registration mints a new pair.

  4. User authorises

    Client sends the user's browser to authorization_endpoint with response_type=code, client_id, redirect_uri, code_challenge, code_challenge_method=S256, and the requested scope. The user consents in a ZopNight page listing the scopes and the exact write tools each scope admits, then the browser returns to redirect_uri with code.

  5. Client exchanges code for tokens

    Client POSTs to token_endpoint with grant_type=authorization_code, code, code_verifier, client_id, and client_secret. Response: a short-lived JWT access_token (identity + granted scopes), a rotating refresh_token, and a Connected App row visible to the user in ZopNight.

  6. Client calls the MCP server

    Every JSON-RPC request carries Authorization: Bearer <access_token>. The gateway validates the JWT, resolves the caller's live role in the target org, and checks the tool against the org's Write access setting and the token's granted scopes.

  7. Client refreshes

    When the access token nears expiry, the client POSTs to token_endpoint with grant_type=refresh_token. The server issues a new access token and rotates the refresh token — the old refresh token remains valid for a 60-second in-row grace window to absorb races, then it is retired. Presenting a retired refresh token triggers family kill on reuse (RFC 9700 §4.14.2): every refresh token in that lineage is revoked, and the user must re-consent.

Discovery endpoints

text
/.well-known/oauth-protected-resource                    # RFC 9728
/.well-known/oauth-authorization-server                   # RFC 8414
/.well-known/oauth-authorization-server/oauth             # RFC 8414 (path-suffixed)
/oauth/register                                           # RFC 7591 DCR
/oauth/authorize
/oauth/token
/oauth/revoke                                             # RFC 7009

Revocation and lifecycle

  • Access tokens are short-lived JWTs; identity/role/scope are re-resolved on every call, so removing a user from an org or dropping their role takes effect on the agent's next request.
  • Refresh tokens rotate on every use, with a 60-second in-row grace window; reuse of a retired refresh token kills the entire token family (RFC 9700 §4.14.2).
  • Instant revoke via Connected Apps in ZopNight — a user can revoke any connected client at any time, which invalidates its access and refresh tokens immediately.
  • Client secrets issued by DCR are single-use per registration; there is no rotation API. Re-register to mint a new pair.

Personal Access Token

For CI, scripts, or any client that cannot drive a browser, mint a PAT.

  1. Open Developer Settings

    Click your profile avatar (bottom-left of the sidebar), then Developer Settings ↗.

  2. Create a token

    Click Create Token, enter a name (e.g. "CI runner"), choose an expiry, tick the Write capabilities you want it to carry, and click Create Token.

  3. Copy the token immediately

    The token is shown once. Store it in your CI's secret store. The Server URL is on the same page.

  4. Send it as Bearer

    On every request:

    text
    Authorization: Bearer <pat>

What your PAT can do

A PAT carries your identity. It is not a separate, narrower account:

  • Every call resolves your live role at the moment it is made. Change your role, or get removed from the organisation, and the token's reach changes with it — immediately, with no re-mint.
  • The token cannot do anything you cannot do in the UI. It also cannot do less, except for the write capabilities you tick at mint time (see below).
  • Tokens are tied to your email, not to one organisation, and they work across every organisation you belong to.
  • Tokens have an expiry. When it lapses, create a new one.

Mint the token as the person with the narrowest role that does the job, and keep Write access as low as the job allows.

Write access

The Write access setting (Settings → Organisation) decides how much an agent may change, regardless of auth method.

SettingWhat it admits
Read-onlyNothing. Read tools only.
Metadata onlyChanges to ZopNight's own records: budgets, recommendation status, smart-tag acceptance. No cloud state, no cost.
Reversible changesBounded blast radius, may affect cost: schedules, overrides, tagging policies, Service deploys, rollbacks, config.
Irreversible changesNo undo, or incurs cost: starting/stopping resources, Provisioning jobs, Kubernetes writes, deletes.

The settings are cumulative: Reversible admits Metadata-only too. The picker in Settings enumerates the exact tools each level turns on, generated from the same manifest the gateway authorizes against.

Some surfaces are excluded at every level and nothing adds them: roles and permissions, user management, organisation deletion, cloud-account deletion, credentials, and bulk actions.