Table of Contents
Getting Started
Core Concepts
MCP Connector
MCP Connector
Claude and ChatGPT connect to oakallow at https://api.oakallow.io/mcp via OAuth 2.1. The authorization server is self-hosted on our own infrastructure, with full compliance details at /docs/oauth. This page documents the connection, the standard read-only toolkit every account ships with, how customer-defined tools are governed, and the error shapes you'll see.
Connection
| Endpoint | https://api.oakallow.io/mcp |
|---|---|
| Transport | JSON-RPC 2.0 over HTTPS POST (single messages and batched arrays). |
| Authentication | OAuth 2.1 Bearer token, self-hosted authorization server. |
| Protected Resource Metadata | GET /.well-known/oauth-protected-resource/mcp, RFC 9728. |
| Authorization server | https://oakallow.io |
| Scopes | mcp:read and mcp:write. The user picks which to grant on the consent screen. |
| Compliance docs | oakallow.io/docs/oauth |
| Rate limits | None on MCP calls. The token + register endpoints are rate-limited per IP. |
| Origin header | Not validated. Authentication is by Bearer token only. |
Authentication flow
The oakallow MCP connector uses self-hosted OAuth 2.1. The MCP client (Claude, ChatGPT, or any other MCP-compatible application) discovers our authorization server, registers via Dynamic Client Registration (RFC 7591), and walks a standard authorization-code flow with PKCE. The user signs in with their oakallow account and chooses which scopes to grant on a consent screen we host.
- The client fetches the Protected Resource Metadata at
https://api.oakallow.io/.well-known/oauth-protected-resource/mcp(RFC 9728). The response advertiseshttps://oakallow.ioas the authorization server andhttps://api.oakallow.io/mcpas the resource. - The client fetches the Authorization Server Metadata at
https://api.oakallow.io/.well-known/oauth-authorization-server(RFC 8414) to discover the authorize, token, register, and revoke endpoints. - The client posts to
POST /register(RFC 7591) and receives a freshclient_id. - The client opens
GET /authorizein the user's browser with a PKCEcode_challenge. We render an oakallow-branded consent screen. If the user is not signed in, we bounce throughoakallow.io/loginfirst. - On approval, we redirect to the client's registered redirect URI with an authorization code. The client exchanges the code at
POST /tokenfor an opaque access token (1-hour lifetime) and a refresh token (30-day lifetime, rotated on every use). - The client presents the token as
Authorization: Bearer <token>on every/mcpPOST. We validate it locally against our grant store, extract the user identity from the encrypted grant props, and dispatch the JSON-RPC call.
Tokens are opaque bearer strings, stored encrypted in Cloudflare KV with the access token itself as key material. There are no shared API keys, no client credentials on the user side, and no long-lived secrets to leak. Full compliance documentation lives at /docs/oauth.
Standard tools (oakallow-managed)
Every oakallow account is seeded at signup with a Default organization and four standard read-only tools. These are the tools oakallow itself executes on behalf of the connector — they cannot be deleted from the dashboard and every annotation (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) is precisely known.
check_approval_status
Poll a pending approval request by REF number. Use this between retries of a tool call that is pending approval — do not call the original tool again to check status, that will create a duplicate approval.
Input: reference (string, required) — the REF-XXXXXXXX-XXXX identifier.
Annotations: readOnly, idempotent.
list_pending_approvals
List approval requests still awaiting a human decision for the signed-in user in their primary org. Returns up to 25 rows.
Input: none.
Annotations: readOnly, idempotent.
check_permission
Dry-run permission resolution. Asks whether a given tool call would be allowed, require approval, or be blocked — without actually making the call.
Input: tool_name (string, required); optional resource_id, method, tenant_id.
Annotations: readOnly, idempotent.
Auto-discovery:if the tool isn't registered in the caller's org yet, check_permission auto-creates it as a draft with default_permission = requires_approval and conservative annotation defaults so the eventual tool call is gated and the user can triage the new tool from the dashboard. See Dynamic tools → Auto-discovery for the full behavior, defaults, and rate limits.
list_my_tools
Enumerate the customer-defined tools available in the signed-in user's primary org. Standard oakallow-managed tools are excluded to keep the output focused on the customer's own catalog.
Input: none.
Annotations: readOnly, idempotent.
Dynamic tools (customer-defined)
Customers define their own tools in the oakallow dashboard or via POST /v1/tools. Those tools appear in tools/listfor the owning user with the MCP annotations the customer declared at registration time. The worker relays those annotations unchanged to Claude. oakallow does not execute the tool — it governs it; execution is the customer's responsibility.
Required annotations on create
POST /v1/tools, POST /v1/tools/bulk, and POST /v1/tools/seed all require four annotation fields on create. The singular create also requiresannotations_ack: true:
read_only_hint— true if the tool performs no state changes.destructive_hint— true if effects cannot be trivially undone.idempotent_hint— true if repeat calls with the same args leave the world in the same state.open_world_hint— true if the tool reaches an external system beyond oakallow.annotations_ack— true; the caller acknowledges the four values accurately describe the tool.
Missing any of the four returns HTTP 400 with the errortool annotations are required: read_only_hint, destructive_hint, idempotent_hint, open_world_hint must be set (boolean or 0/1).This matches the Anthropic Connector Directory and OpenAI Apps SDK requirement — both treat missing annotations as a validation error, and ChatGPT's runtime usesreadOnlyHint to decide whether to show a confirmation prompt.
Auto-discovery via check_permission
Auto-discovery is MCP-only. REST callers must register tools explicitly via POST /v1/tools (or bulk/seed) — the REST /v1/permissions/check endpoint does not auto-create. The split is intentional: REST customers are wiring their own backend and manage their catalog deliberately; MCP customers are connecting an agent that may try tools faster than a human can pre-register them.
When an MCP client (Cowork, Claude Desktop, ChatGPT, any compatible agent) calls check_permission with a tool_namethat isn't registered for the caller's org, oakallow inserts a new row with status = draft, auto_created = 1, and default_permission = requires_approval. The verdict still comes back as requires_approval, so the eventual tool call is gated. The user reviews the discovered tool from the dashboard (Tools page → Recently Discovered) and chooses to allow it, block it, or keep it gated. This makes check_permission safe to call eagerly on every tool name your agent might invoke — your catalog grows as your agent works, and nothing executes without your sign-off.
Conservative annotation defaults. Auto-discovered tools come in with the most restrictive annotation values, not blank. The semantics of an unknown tool are unknown, so oakallow plants the cautious reading until the user reviews:
read_only_hint— false (assume it changes state)destructive_hint— true (assume effects can't be undone)idempotent_hint— false (assume repeat calls compound)open_world_hint— true (assume it reaches external systems)annotations_ack— false until the user reviews
Any agent reading the catalog after a discovery sees a tool flagged destructive, non-idempotent, and reaching the outside world — which is what you want until the human says otherwise. The dashboard's needs annotations chip keys off annotations_ack = 0alone (the single signal of "user hasn't reviewed yet"); the user adjusts the four hints from the Recently Discovered queue.
Auto-discovery is rate-limited per org. Defaults: 50 per hour, 500 lifetime; both configurable on the orgs table. When a cap is hit, the verdict is still requires_approval (fail-closed) but no row is added to the Recently Discovered queue until existing discoveries are triaged. The tool.auto_created webhook event fires on every successful auto-create, separately from the approval.created event that fires later on the eventual tool call.
Tool-call behavior
When Claude invokes a customer-defined tool:
- Resolver =
allowed: the worker returns a "no dispatch configured" error text with guidance. Customers who want immediate execution must do it client-side or wire their own backend. - Resolver =
requires_approval: the worker creates an approval row, fires theapproval.createdwebhook, pushes a notification to configured approvers, and returns aREF-…reference. The customer's backend handles execution onapproval.decided. - Resolver =
disabled: the worker returns a policy-denied error with the resolution level that matched.
This is the governed-intent pattern. The MCP connector surfaces the call to a human approver; the customer's infrastructure performs the side effect.
Accountability
oakallow does not vouch for the accuracy of customer-declared annotations. A governance platform enforces the policy a customer configured; it does not certify the third-party code it governs. That is why the dashboard form requires explicit acknowledgment before creating any tool and why oakallow relays annotations to Claude unchanged.
Multi-level approvals
Tools flagged requires_second_approval escalate from level 1 to level 2 after the first human approval lands, when the owning org has a non-empty Approval 2 group configured.
From the MCP surface, this is a single REF-… that stays in pending while current_level advances internally from 1 to 2. Claude does not see the level — it simply polls check_approval_status until the reference reaches a terminal state (approved, denied, expired, or cancelled).
Error responses
Failures inside a tool call are returned as MCP result envelopes with isError: true and a text payload. HTTP-level errors (malformed JSON-RPC, missing token) are standard JSON-RPC errors.
HTTP/2 401
WWW-Authenticate: Bearer realm="OAuth", resource_metadata="…"
{ "error": "Missing Bearer token" }{
"content": [
{
"type": "text",
"text": "Request is still awaiting approval. Reference number: REF-76A7F7ED-8659. The approver needs to open oakallow and approve this reference before the call can execute. Use check_approval_status with reference=REF-76A7F7ED-8659 to poll the status."
}
],
"isError": true
}The isError: true convention for "pending approval" is deliberate: it prevents Claude from treating the call as success and retrying the same tool, while still giving the user a human-readable message carrying the REF-… so they can check back later.
Privacy and data handling
Tool-call parameters pass through an automatic PII redaction filter before any storage (SSN, card, email, phone, address, ZIP patterns). Access tokens are verified on every request and not persisted. Approval records are retained per oakallow's standard retention policy — see the privacy policy for the MCP data-flow section.