MCP & agents
Okapi mounts an MCP server over HTTP at /mcp, inside the same running instance as everything else — there's no separate process to start and no stdio transport. One transport, identical on self-host and cloud: point an agent at your instance's /mcp endpoint and it can read issue context, logs and traces, discover issue deltas, triage issues and performance issues, manage alert rules and notification channels, and provision projects and credentials through the tools below, scoped to what its credential is allowed to see. Agents are never metered and never gated to a paid tier — the whole MCP surface ships in every edition, every plan.
Two ways to authenticate
/mcp accepts two credentials, and they are equivalent once resolved: the same tools, the same scoping, the same roles, the same errors. They differ in how you obtain one, who can revoke it, and what the audit log records next to the actions it takes. There is one exception: personal alert rules belong to a person, so an agent token is always refused on personal-rule calls (see Personal alert rules).
OAuth (okapi_oauth_…) |
Agent token (okapi_agent_…) |
|
|---|---|---|
| How you get it | Sign in and approve in your browser | An owner mints it in the dashboard or on the CLI |
| Best for | A person working interactively at a laptop | CI, headless agents, shared automation |
| Lifetime | 30 days, then re-approve | Until revoked |
| Revoked by | You (Connected apps), an organization owner, or a password reset/change | An organization owner |
| Audit rows | Name the human who approved it | Name the token, no human |
| Configuration | None — no header, no secret in a file | The token lives in the client's config |
whoami reports which door a call arrived on (door), and — for OAuth only — whose approval it is acting under (user).
OAuth: the interactive path
With OAuth enabled on the instance and a client that supports its authorization flow, add the server with no header at all:
claude mcp add --scope user --transport http okapi https://your-okapi-host/mcp
claude mcp login okapi
--scope user makes the server available in every project on this machine. claude mcp login okapi opens the consent screen in your browser straight away; without it, Claude Code waits until you run /mcp in a session and choose to authenticate. Codex (codex mcp add okapi --url …) always saves to ~/.codex/config.toml, which is user-wide too, and signs in as part of add; for Cursor, use the global ~/.cursor/mcp.json.
| Client | Sign in | Sign in again (renew, or approve new permissions) |
|---|---|---|
| Claude Code | claude mcp login okapi |
claude mcp logout okapi, then claude mcp login okapi |
| Codex | part of codex mcp add; otherwise codex mcp login okapi |
codex mcp logout okapi, then codex mcp login okapi |
| Cursor | Cursor Settings → Tools & MCPs, next to okapi; or cursor-agent mcp login okapi |
the same |
| OpenCode | opencode mcp auth okapi |
opencode mcp logout okapi, then opencode mcp auth okapi |
These sign-in commands are for you to run in your own terminal, not for an agent: in Claude Code, signing in first revokes the current approval, and without an interactive terminal the new sign-in can't finish. Sign in from outside any repository that has its own local-scope okapi entry, because the login picks the server by name and a local entry wins. An OAuth approval lasts 30 days; sign in again to renew it. Reconnecting a client can reuse the old approval, so to grant something new — such as access to your personal alert rules, which connections approved before that feature existed don't have — sign out and sign in again so the consent screen appears.
Cloud is not open for signup yet. When it opens, the same command works with the cloud instance's /mcp URL.
The first call gets a 401 pointing at your instance's own authorization server — Okapi is both the authorization server and the protected resource, so authorization never leaves your instance — and the client opens a consent screen in your browser (Claude Code instead marks the server as needing authentication and waits for claude mcp login okapi or /mcp). Sign in if you aren't already; the screen names the client asking, and you choose the organization, the projects, and the role it will get. Approve and the client completes the exchange itself; there is nothing to paste. Deny and no grant exists.
Clients identify themselves by a Client ID Metadata Document — an https:// URL Okapi fetches to learn the client's name and redirect URIs. A client that can't do that may register itself dynamically (RFC 7591) instead; Okapi verifies nothing about such a client, so the consent screen presents it as an unverified application named only by what it calls itself. Registration confers no access on its own and is rate limited.
A grant lives 30 days, and its power is whatever you chose on the consent screen — not something the client asked for. When it expires, sign in again (the table above) to renew it.
Validated OAuth clients: claude.ai and Claude Desktop were walked interactively against a staging instance on 2026-08-24, including browser approval, using a Client ID Metadata Document (CIMD). Add the instance's /mcp URL as a custom connector and leave the Client ID and Client Secret fields blank. Both connect from Anthropic's cloud, so the instance must be publicly resolvable.
Cursor and OpenCode were walked against a staging instance on 2026-08-25, including browser approval, using the registration fallback at POST /oauth/register. Point either client at the instance's /mcp URL; the consent screen labels it an unverified application. These are dated validation records, not a guarantee for every client version.
Agent tokens: the CI and headless path
An agent token is a Bearer credential you paste into a client's configuration — the right shape when there is no human and no browser:
claude mcp add --scope local --transport http okapi https://your-okapi-host/mcp \
--header "Authorization: Bearer okapi_agent_…"
Run it in the repository's directory. For an organization-wide token, use --scope user instead.
Self-host and cloud differ only in hostname — same transport, same tools, same token format. The documented default is a project-scoped credential per repository, so each codebase talks to exactly its own Okapi project; mint a broader organization-wide token only when an agent genuinely needs to work across projects (e.g. provisioning a new one). Both scopes keep the token out of git, in your Claude Code config (~/.claude.json). A project-scoped token belongs in --scope local, which applies to this repository only, so every repository can hold its own okapi entry. --scope user applies to every project on the machine and holds a single okapi entry: use it for OAuth and organization-wide tokens. Adding a second user-scope okapi entry fails with "already exists"; a local-scope okapi can sit next to the user-scope one and takes precedence in its repository. This Claude Code installation scope is separate from the Okapi credential's project scope. A committed .mcp.json references it through an environment variable instead, as "Authorization": "Bearer ${OKAPI_TOKEN}".
Managing OAuth connections
- Yours: Account settings → Connected apps lists your still-active grants, across every organization, with what it can reach and when it was last used. Disconnecting takes effect on the next call — the token is checked against the grant every time, never cached.
- Your organization's: organization Settings → Connected apps (owner-only) lists every member's still-active grants into that organization and lets an owner revoke any of them.
- A password reset or password change revokes every one of that user's grants, in the same transaction as the credential rotation.
For self-host operators
OAuth on /mcp is on by default and needs no configuration beyond OKAPI_BASE_URL being an absolute https:// origin (or http:// on a loopback host) without userinfo, a non-root path, query, or fragment (it's the OAuth issuer). When it isn't, OAuth stays off, /mcp keeps working on agent tokens alone, and okapi serve logs one WARN naming the problem — it never refuses to boot over it. To turn OAuth off deliberately, set OKAPI_MCP_OAUTH=0 (on self-host, in an override file — see Setting variables the compose file does not list); that closes the door completely and immediately invalidates any tokens it issued. Agent tokens are untouched by the switch.
Approving a new connection does not revoke the old grant; disconnect it in Connected apps.
With the cloud billing capability enabled, /mcp answers 402 subscription_required when the organization has never held a subscription and is not otherwise collecting. A live grant, an unapplied-payment marker, or a quarantine vouch opens that wall. OAuth consent can precede checkout; access follows stored billing state.
Minting an agent token
Dashboard: Settings → Agent tokens (organization settings, owner-only) → New token. Give it a name, a role, and — optionally — narrow it to one project. The plaintext is shown once, alongside a ready-to-paste claude mcp add command with the token already filled in.
CLI, for headless self-host operators or CI:
okapi createtoken --role triage
okapi createtoken --role read --project web-app --name ci
--project <slug> scopes the token to one project; omitted, the token is organization-wide. On a self-host instance (exactly one organization) the organization resolves automatically; on an instance with more than one organization, pass --organization <slug> or the command errors with that instruction. The plaintext and the inline claude mcp add command print once — there's no second chance to retrieve either.
Roles
Three capability tiers, checked on every tool call — the same three whether the credential is an agent token or an OAuth grant (an OAuth grant's role is chosen on the consent screen and can never exceed the approving user's own role in that organization):
| Role | Can do |
|---|---|
read |
List projects and issues, read issue context, search logs, search traces, list expensive queries, read performance issues, discover deltas, check resolutions and installations, and read team alert rules, alert views, channel metadata (never URLs or secrets) and channel alert history; whoami. |
triage |
Everything read can, plus resolve, unresolve, ignore, and snooze issues and performance issues. Over OAuth only, it also manages the approving user's own personal alert rules and personal alert history (see Personal alert rules). The default role for new tokens. |
admin |
Everything triage can, plus delete issues, bulk deletes, provisioning (create projects with a default DSN key, read and list DSN keys, mint additional DSN keys and source-map upload tokens), and creating, updating, deleting, resetting and testing team alert rules. An organization-wide admin credential can also create, update, delete and test notification channels. |
Deletes, alert rule and channel changes, real test sends, minting additional DSN keys and upload tokens, and performance-issue triage need a named confirmation flag in the call (confirmDelete, confirmBulk, confirmChange, confirmSend, confirmCreate or confirm, depending on the tool). create_project needs no flag, although it also mints the project's default DSN key. Changes made over MCP are recorded in the audit log. DSN keys and upload tokens that an agent mints keep working after the agent's credential is revoked; delete them in the project's settings.
An agent token can be revoked at any time from Settings → Agent tokens; revocation is immediate and irreversible (mint a new one to replace it). OAuth grants are revoked from Connected apps, as above.
Tools
The minimum role for each tool is in brackets. A running instance lists the same tools, generated from the same registry, in GET /api/v1/agents.
Identity and issues
whoami[read] — the credential's own name, which door it came through (agent_tokenoroauth, plus the approving user for OAuth), scope, organization, role, rate-limit state, and a deep link. For narrowed credentials, it lists surviving projects; one survivor yieldsscope:"project", whilescope:"projects"can mean several or zero. Its projects field is omitted with zero survivors or organization-wide scope. Dynamically registered application names carry(unverified). It does not list tools.list_projects[read] — every project the credential can see (all of them for an organization-wide one; surviving granted projects for a narrowed one). Deleted projects are skipped; default limit 100, maximum 200.list_issues[read] — list issues in a project; filter bystatus(unresolveddefault,all,resolved,ignored,snoozed),level, or a case-insensitive literalqueryacross title, culprit, short issue ID, and error type, with-termexclusions.whats_new[read] — what changed since a release or a recent time window: new issues, regressions, and spiking issues. Pass a knownsinceReleaseor positivesinceMinutes; the release wins if both are set. New issues include first-release attribution; spikes compare the trailing two daily-count buckets against the preceding daily rate, independently of the boundary. With no project, one survivor yields detail, several yield summary (up to 50 projects, three headlines per bucket);releaseNotSeenflags unknown releases andcountsCappedmarks floors.nextStepnames a slug when a project saw the boundary, otherwise suggests a time window.get_issue_context[read] — the one-shot fix bundle for an issue by short id: a symbolicated stacktrace, breadcrumbs, trace-id-correlated logs, release info, a daily frequency series, affected-user count and sample contexts, similar issues, and the raw event payload.titledescribes the issue's first event;latestEventTitledescribes the latest stored event.check_resolution[read] — did a previous single-issue resolve hold? Compares server receive times against the marker stamped on everyupdate_issueresolve, even without commit/release. Bulk actions preserve earlier markers. With a known resolving release, older releases are excluded and release-less events still count; otherwise all later events count.hasMarker:falsemeans no marker exists.update_issue[triage] — triage by short id:resolve,unresolve,ignore,snooze(withsnoozeMinutes), ordelete(requires theadminrole and aconfirmDelete: trueflag).resolvealso accepts an optional commit/release and note.bulk_update_issues[triage] — apply one triage action to a bounded selection matching a filter (status/level/query), up to 100 matches per call —resolve,unresolve,ignore,snooze, ordelete(requiresadminandconfirmBulk: true). It reports selected IDs, write count, pre-write matching count, andhasMore. Repeat the filter only if the action removes selected issues from it. Bulk resolve records no provenance or fresh marker; earlier single-issue markers remain.search_logs[read] — search a project's logs bylevel, messagequery,traceId,attributes, or asinceMinutestime window.
Traces and performance (see Performance monitoring)
search_traces[read] — search trace segments by project, time (default the last 24 hours), duration, error status, release, or environment.get_trace[read] — one trace by its 32-hex trace id: spans, database queries, correlated errors and logs, and performance issues. An organization-wide credential sees every project's segments of the trace.found:falsemeans the trace expired or never existed.list_expensive_queries[read] — normalized database queries ranked by total, count, mean, p95 or maximum duration, or by latest seen, with a sample reference.search_performance_issues[read] — search slow-query and N+1 performance issues by project, detector, status (unresolveddefault), or text.get_performance_issue[read] — one performance issue by id, with up to 50 recent occurrences.sampleAvailable:falsemeans the raw trace has expired while the issue remains.update_performance_issue[triage] —resolve,unresolve,ignore, orsnoozeone performance issue, withconfirm: true. A snooze needs anuntiltime no more than 30 days ahead.
Provisioning and credentials
create_project[admin, organization-wide] — provision a brand-new project and mint its default DSN key in one call.get_dsn[admin] — read a project's DSN (the SDK init string), the same sensitivity as the dashboard's owner-only DSN Keys page. It only reads existing keys;found:falsemeans no key exists. It prefers an enabled key, falling back to a disabled one that must be enabled before ingest works.get_install_guide[admin] — read a platform's install guide with the project's real DSN filled in. Pass a supportedplatform; the ordinary project scope applies. It selects keys likeget_dsn, so checklist_dsn_keysfor enabled state before sending events. With no key,found:falsestill returns the full guide with a recovery placeholder.list_dsn_keys[admin] — every DSN key of a project, disabled keys included, each with its DSN.create_dsn_key[admin] — mint an additional DSN key, withconfirmCreate: true. Each call creates a new key, so do not retry blindly.create_upload_token[admin] — mint a source-map upload token, withconfirmCreate: true. The token is shown only in this response; put it in your CI secret store.verify_installation[read] — is Okapi actually receiving events for this project? Reports any DSN key (including disabled keys), stored events, source maps, and configured alert rules.statusisno_key,waiting_for_first_event,test_event_onlyorinstalled:test_event_onlymeans every stored event has first exception typeTest event;installedmeans one lacks that marker. This does not independently prove SDK origin or a genuine failure.alertsLivesays whether any team or personal rule could deliver an alert, andactiveAlertRuleslists the team rules; neither proves that a delivery will happen. Correlate the result with your SDK test; diagnosis and reported SDK/environment/release fields are fenced captured data.
Alert rules and history
list_alert_rules[read] — team alert rules of a project or view, enabled and disabled, with channel names and kinds.scope:"personal"lists your own personal rules instead (OAuth,triageoradmin).list_alert_views[read] — the views this credential covers completely, which a rule or digest can target.get_alert_rule[read] — one rule's configuration.scope:"personal"reads your own personal rule (OAuth,triageoradmin).list_notification_channels[read] — channel names, kinds, status and project scope. Channel URLs and secrets are never returned.list_alert_history[read] — what Okapi delivered to channels for a project or view (a view needs an organization-wide credential).scope:"personal"adds your own personal deliveries (OAuth,triageoradmin).create_alert_rule,update_alert_rule,delete_alert_rule[triage; team rules need admin] — manage a rule withconfirmChange: true(create, update) orconfirmDelete: true(delete). Deleting a rule deletes its alert history.test_alert_rule[triage; team rules need admin] — send real test messages to the rule's destinations, withconfirmSend: true. MCP tests are limited to one per target per 60 seconds and ten per organization per hour.reset_alert_rules[triage; team rules need admin] — restore a project's default rules, withconfirmDelete: true. A team reset keeps your custom team rules. A personal reset deletes your custom personal rules in that project.consolidate_personal_digests[triage, OAuth only] — delete your own seeded per-project digests for the projects in one view, withconfirmDelete: true. Create the view digest first withcreate_alert_rule.
Notification channels (organization-wide admin only)
create_notification_channel,update_notification_channel,delete_notification_channel— manage Slack and webhook channels, withconfirmChange: trueorconfirmDelete: true. The channel URL is write-only. Revealing it, rotating the signing secret and Add to Slack stay in the dashboard.test_notification_channel— send one real test delivery, withconfirmSend: true. A successful test clears the channel's failure state.
Ordinary project tools take an optional project slug. Omit it only when exactly one granted project still exists: the tool then uses that project, the same one whoami reports. An organization-wide credential, or a grant with several surviving projects, must pass a slug from list_projects. If no granted project survives, mint a new agent token or reconnect the application.
Personal alert rules
The personal-rule branches (scope:"personal" and consolidate_personal_digests) act for the person who approved an OAuth connection, on that person's own rules only. They need a grant at triage or admin. An agent token is always refused, because it has no person behind it. No tool shows another member's personal rules, not even to an owner.
A connection approved before personal alert rules existed does not have access to them. Sign out of that connection and sign in again (see OAuth: the interactive path), so the consent screen appears and records your consent. Reconnecting is not enough, because it can reuse the old approval.
Every tool call is scoped to the credential's organization (and projects, if it's project-scoped) — a credential can never reach another organization's data, and read-role tools expose no product mutations (usage/rate-limit bookkeeping still occurs). Event- and log-derived text (messages, stack frames, breadcrumbs, correlated logs, release names, the raw payload) returned by these tools is wrapped in an explicit untrusted-data envelope before it reaches an agent: it's captured application data, never instructions.
Agent bootstrap
An agent arriving at a running instance with no prior context starts at GET /api/v1/agents: a public, ungated JSON manifest describing how to authenticate (OAuth in the browser or a pasted agent token — no session, no credential granted), the three roles and what each grants, recommended first calls (whoami → list_projects → whats_new / verify_installation), and doc links. Its tool catalog is generated from the same registry the instance's live /mcp endpoint runs, so it can't drift from what's actually callable.
For a signed-in dashboard session, GET /api/v1/organizations/{organizationSlug}/projects/{projectSlug}/install/{node|python|browser|laravel|php|nextjs|go|rails|spring|aspnetcore|rust|phoenix|vue|nuxt|sveltekit}.md returns a DSN-personalized guide for an organization member. This HTTP route requires dashboard session authentication. With an admin MCP credential, prefer get_install_guide(project, platform) for the same guide with the real DSN filled in. On older instances without this tool, use get_dsn with an admin credential and the matching public guide at https://useokapi.app/docs/install/{id}.md. For non-admin credentials, ask an organization owner for the DSN and follow that public guide; get_dsn requires admin too. Send a test event, then call verify_installation to confirm delivery.
Agent Kit ships two skills, served as plain markdown from the instance — GET /api/v1/agents/skills/okapi-setup.md and GET /api/v1/agents/skills/okapi-fix-issues.md — encoding the setup loop (detect stack → install → initialize → test event → verify_installation, then mint a per-repo triage token) and the debugging loop (whats_new → bulk-suppress noise → get_issue_context per issue → fix → check_resolution → resolve-in-commit). There's no installer: an agent fetches the markdown and places it itself — .claude/skills/<name>/SKILL.md for Claude Code, the Cursor rules directory, or a fragment appended to AGENTS.md for Codex.
For AI agents
If you're an agent reading this because a human pointed you at an Okapi instance: don't scrape the dashboard UI or ask for a pasted stack trace. Connect over MCP and work from the real data instead —
whoamito confirm your own scope and role, thenlist_projects.whats_neworlist_issues(defaultunresolved) to see what's actually open.get_issue_contextfor the full fix bundle on anything you're investigating.search_logsto pull structured logs correlated by trace id or time window around an error.search_traces,get_traceorsearch_performance_issueswhen the problem is slowness rather than an error.update_issueorbulk_update_issuesto resolve, snooze, or ignore once you've verified a fix — don't leave triage to the human if you've already confirmed the fix landed.
A machine-readable summary of Okapi — what it is, pricing, and where these docs live — is published at /llms.txt.
Next: Telemetry for what an Okapi instance sends home on its own, independent of anything you do with MCP.