Skip to content

AI assistants (MCP)

Repro can act as an OAuth 2.1 authorization server and an MCP resource server, letting AI assistants (Claude Desktop, Cursor, ChatGPT, …) connect to your instance through a per-user OAuth flow. This page covers the operator-side concerns: enabling it, the database migration, and the security model.

Enabling MCP

Set MCP_ENABLED=true in your environment. From v0.6.0 this is the default; in v0.5.x it required an explicit opt-in.

bash
MCP_ENABLED=true

When the flag is off, the /api/mcp endpoint returns 404, the /.well-known/oauth-authorization-server/api/auth discovery endpoint returns 404, and the better-auth oauth-provider plugin is not loaded. Existing dashboard auth (magic link, GitHub OAuth) is unaffected.

Tunables

VariableDefaultPurpose
MCP_ENABLEDtrueMaster toggle. Set to false to disable MCP entirely.
MCP_ACCESS_TOKEN_TTL_SECONDS3600 (1h)How long an access token is valid before refresh. Shorter is safer; longer reduces token-refresh chatter.
MCP_RATE_LIMIT_PER_USER_PER_MINUTE600Per-user rate cap on /api/mcp. AI clients are chatty — leave this generous unless you see abuse.

BETTER_AUTH_URL and BETTER_AUTH_SECRET are reused; no new auth secrets to manage.

Database changes

Migration 0017_cute_toxin adds two columns; oauth-provider plugin tables (jwks, oauth_client, oauth_access_token, oauth_refresh_token, oauth_consent) were added earlier in 0016. The two new columns from 0017:

  • report_events.actor_client_id text — populated with the OAuth client_id when a write came through MCP, NULL when it came through the dashboard UI.
  • report_comments.actor_client_id text — same.

Apply migrations the standard way:

bash
bun run db:migrate

(or db:push for dev iteration).

The migrations are purely additive — no existing tables are altered or dropped.

How the OAuth flow looks

  1. AI client fetches https://<your-repro>/.well-known/oauth-authorization-server/api/auth for endpoint discovery (RFC 8414).
  2. Client registers via POST /api/auth/oauth2/register (RFC 7591 — anonymous; this only registers a client, not a grant).
  3. Client opens the user's browser to /api/auth/oauth2/authorize?... with PKCE.
  4. The user signs in (existing flow) and lands on /oauth/consent — they click Allow.
  5. Client exchanges the code for a JWT access token at /api/auth/oauth2/token with the resource indicator set to <your-repro>/api/mcp.
  6. All subsequent MCP calls go to /api/mcp with Authorization: Bearer <jwt>.

Audit trail

Every change made through MCP is recorded with actor_client_id set to the OAuth client_id. To find every change made by an MCP client on a particular ticket:

sql
SELECT kind, payload, created_at, actor_client_id
FROM report_events
WHERE report_id = '<ticket-id>' AND actor_client_id IS NOT NULL
ORDER BY created_at DESC;

A future dashboard release will surface this as a badge on the activity feed; for now it's queryable directly.

Security model

  • Per-user OAuth, no service tokens. An assistant runs as a specific user with that user's project memberships and roles.
  • No exfiltration tools. Repro's MCP surface has no fetch_url, post_to_webhook, or run_code tool. An AI ingesting attacker-controlled content (prompt-injected replay, console log) cannot exfiltrate data through the Repro surface.
  • No destructive writes. No delete_* tool exists. The worst an injected prompt can do is mistakenly change a status or post a comment — both reversible in the UI in seconds.
  • Bounded blast radius. Access tokens expire after 1 hour. Users can revoke any time at /settings/mcp.
  • Cookies are opt-in. repro_get_ticket deliberately omits captured cookies. An AI must call repro_get_ticket_cookies explicitly. The tool description warns about session-token risk.

For more depth see the spec at docs/superpowers/specs/2026-05-06-mcp-oauth-server-design.md.

Disabling

Set MCP_ENABLED=false and restart. The OAuth tables stay in the database (so existing consents are preserved if you re-enable later) but are unreachable. To clean up entirely, also revoke any consents from the dashboard UI before disabling.