Arachne docs Home Open app

MCP server

Authentication

How MCP clients authenticate to Arachne: personal access tokens (the recommended path), OAuth access tokens from Arachne's Cognito pool, and the limits and error codes that protect the endpoint.

Every request to https://arachne.withaadhi.com/mcp carries Authorization: Bearer <token>. Two kinds of token are accepted:

TokenLooks likeUse it when
Personal access token (PAT)arpat_ + 43 charactersAlmost always. Works in every client that can set a header.
OAuth access tokena JWT issued by Arachne's Cognito user poolYour client does OAuth with a pre-registered client id (advanced).

Personal access tokens#

Create one#

  1. Dashboard → MCP access tokensSign in to the app; the section is at the bottom of the Dashboard.
  2. Label it and click Create tokenUse a label that names the client and machine, so you know what to revoke later.
  3. Copy it onceThe full token is displayed once. Arachne stores only its SHA-256 hash, so it can never be shown again; if you lose it, revoke it and create another.

Use it#

POST /mcp HTTP/1.1
Host: arachne.withaadhi.com
Authorization: Bearer arpat_REPLACE_WITH_YOUR_TOKEN
Content-Type: application/json

Keep it in an environment variable or your client's secret storage and reference it from config:

export ARACHNE_TOKEN="arpat_REPLACE_WITH_YOUR_TOKEN"   # e.g. in ~/.zshrc, or load it from your password manager's CLI
[Environment]::SetEnvironmentVariable("ARACHNE_TOKEN", "arpat_REPLACE_WITH_YOUR_TOKEN", "User")

What a token can do#

Never commit a token

Do not paste tokens into repositories, issue trackers, chat or prompts. The arpat_ prefix exists so secret scanners can spot a leaked one; if that happens, revoke it straight away.

How a request is checked#

sequenceDiagram
  autonumber
  participant C as MCP client
  participant S as Arachne /mcp
  participant DB as Token + membership store
  C->>S: POST /mcp, Authorization: Bearer arpat_...
  S->>S: per-IP rate limit and lockout (before any token work)
  S->>S: body at most 256 KiB
  S->>S: prefix arpat_ means PAT, never parsed as a JWT
  S->>DB: look up SHA-256(token)
  alt unknown, malformed or revoked
    DB-->>S: none
    S-->>C: 401 invalid_token (counts as an IP auth failure)
  else live token
    DB-->>S: account + organisation
    S->>DB: live role of that person in that organisation
    alt no longer a member
      S-->>C: 403 insufficient_scope
    else member
      S->>S: tool scopes granted from the role, last-used bumped
      S->>S: per-person rate limit, then tool scope + role check
      S-->>C: 200 JSON-RPC result
    end
  end

OAuth with Cognito#

Clients that implement OAuth can use an access token from Arachne's Cognito user pool instead of a PAT. This path exists for clients that support pre-registered OAuth clients; it is not the recommended way to connect today.

SettingValue
Protected-resource metadatahttps://arachne.withaadhi.com/mcp/.well-known/oauth-protected-resource (also named in every 401's WWW-Authenticate header)
Authorization serverthe Cognito user pool issuer listed in that metadata (authorization_servers)
Clienta public client (no secret), authorization code + PKCE. Ask the operator for its client id.
Redirect URImust match exactly; registered: http://localhost:8976/callback (no wildcard ports)
Scopes to requestopenid email https://arachne.withaadhi.com/mcp/invoke
Token lifetimeaccess token 60 minutes; refresh token 7 days

What the server does with such a token: it verifies the signature against the pool's keys, the issuer and the expiry; a Cognito access token has no audience, so it must carry the …/mcp/invoke scope. Arachne then fetches your email from the pool's UserInfo endpoint with the same token (nothing is stored), signs you in exactly like a web login (same identity, first use creates the account), and acts in your personal organisation with your role's tool scopes.

For Claude Code, which supports pre-registered clients, the configuration would look like this (replace the client id):

claude mcp add-json arachne '{"type":"http","url":"https://arachne.withaadhi.com/mcp",
  "oauth":{"clientId":"YOUR_MCP_CLIENT_ID","callbackPort":8976,
           "scopes":"openid email https://arachne.withaadhi.com/mcp/invoke"}}'
# then, inside Claude Code: /mcp  -> arachne -> authenticate in the browser

Limits of the OAuth path

  • Cognito does not support dynamic client registration, so clients that rely on it ("sign in when needed" with automatic registration, mcp-remote without a header) cannot complete sign-in.
  • The metadata's scopes_supported lists Arachne's tool scopes (mcp:advisories.read, …), which are not Cognito scopes; a client that requests those literally is refused by Cognito. Set the scopes above explicitly.
  • This flow has not been verified end to end with each client listed in Install & connect. If it gives you trouble, use a PAT.

Limits and error codes#

LimitValueWhen exceeded
Per source IP, before any token check120 requests / minuteHTTP 429 {"error":"rate_limited"} with Retry-After
Failed authentications per IP30 in 5 minutesthe IP is locked out for the rest of the window (429)
Per person, all tokens together60 requests / minuteHTTP 429 for protocol calls; a tools/call gets a tool error rate_limited with retry_after_seconds
Action tools (fix_submit + pr_open) per person10 / hourtool error rate_limited
Request body256 KiBHTTP 413
StatusMeaningWhat to do
401 invalid_tokenNo bearer token, an unknown, malformed or revoked PAT, or an expired / wrong-audience OAuth token.Check the header; create a new token if it was revoked.
403 insufficient_scopeYou are no longer a member of the token's organisation, or (on tools/call) your role does not allow that tool. The JSON-RPC error names the scope and role permission needed.Ask an owner or admin for the role, or use a tool your role allows.
quota (web: 402)Your monthly allowance is used up. Over MCP this is a tool result with isError: true and {"error":"quota_exhausted","resets_at":…,"options":["upgrade","request_more","wait"]}, not an HTTP 402.Request more uses in the app, or wait for resets_at.
429 rate_limitedOne of the rate limits above.Back off for Retry-After seconds.
413Request body over 256 KiB.Send smaller arguments.