Skip to main content

Authentication

The MCP server accepts a Bearer token in the Authorization header, and nothing else — no cookie, no client certificate.

:::warning Never in the URL A token in the query string (access_token, token, bearer, bearer_token, id_token, api_key, apikey) does not authenticate, even next to a valid header: the request is answered 401 with error="invalid_request" in WWW-Authenticate. A token in the URL leaks into proxy and CDN logs, Referer and browser history — the MCP authorization spec and RFC 6750 forbid it. :::

That token is an OAuth 2.1 access token issued by the Kobana authorization server (https://account.kobana.com.br, your Kobana account): an RS256-signed JWT issued for this resource, whose aud is https://mcp.dunning.kobana.com.br. It arrives one of two ways — the OAuth flow, run by the client itself, or a personal token you create in your Kobana account — and it is verified the same way in both cases.

Authorization: Bearer eyJhbGciOiJSUzI1NiIs…

:::info Each surface has its own audience A token issued for the MCP server is not valid on the REST API (api.dunning.*), and an API token is refused here with 401. A token whose aud lists more than one value is refused as well. That is what stops a token handed to an assistant from being reused against the API — and the other way round. :::

Two ways to connect​

OAuth flowPersonal token
How you get itPaste the URL into the client; it opens the Kobana sign-in and you authorizeCreate it at Kobana Account → Tokens and Access → New Personal Token: app Régua de Cobrança, usage MCP, resources and access, validity
How the client uses itOn its own, after you authorizeYou paste it into the client as Authorization: Bearer <token>
ValidityShort-lived, renewed by the client7 days to 1 year, your choice
ScopeAccess level only — read, todayThe resources and access you selected
Good forClaude, ChatGPT, IDEs and any client with OAuthClients without OAuth support, scripts, and agents that need to write

The personal token has to be created with the MCP usage. A token created for the REST API usage carries the API's audience and is refused here with 401 — and the reverse holds too.

The OAuth flow​

You run none of these steps: the MCP client (Claude, ChatGPT, an IDE) does all of it on its own, starting from the URL. They are here for when something fails halfway.

  1. Challenge. A request without a token gets 401 with the challenge every OAuth 2.1 resource server must send (RFC 9728):

    WWW-Authenticate: Bearer realm="kobana-dunning-mcp",
    error="invalid_token",
    resource_metadata="https://mcp.dunning.kobana.com.br/.well-known/oauth-protected-resource"
  2. Resource metadata. The client reads the document it points at. It is public, carries no secret — only pointers — and is cacheable for five minutes (Cache-Control: public, max-age=300):

    curl https://mcp.dunning.kobana.com.br/.well-known/oauth-protected-resource
    {
    "resource": "https://mcp.dunning.kobana.com.br",
    "authorization_servers": ["https://account.kobana.com.br"],
    "bearer_methods_supported": ["header"],
    "scopes_supported": [
    "dunning/agreements", "dunning/audit_logs", "dunning/charges",
    "dunning/classifications", "dunning/collection_rules", "dunning/disputes",
    "dunning/email_layouts", "dunning/exports", "dunning/imports",
    "dunning/interactions", "dunning/metrics", "dunning/negativations",
    "dunning/negotiation_campaigns", "dunning/notification_channels",
    "dunning/notification_preferences", "dunning/notifications", "dunning/people",
    "dunning/protests", "dunning/tasks", "dunning/templates",
    "dunning/webhook_endpoints", "read"
    ],
    "resource_documentation": "https://docs.dunning.com.br/en/mcp/overview/introduction"
    }

    scopes_supported is what the client should ask for: the 21 dunning/<resource> scopes plus read. It never lists dunning/all or the verbs (write, …) — the authorization server does not grant them to a dynamically registered client, and asking for them would fail the authorization.

  3. Authorization server metadata (RFC 8414), at https://account.kobana.com.br/.well-known/oauth-authorization-server.

  4. Dynamic client registration (RFC 7591). The client registers itself.

  5. Authorization with PKCE and resource (RFC 8707). You sign in to Kobana, pick the organization (if you have more than one) and authorize.

  6. Token. The client exchanges the code for a short-lived access token and sends it on every request, refreshing it when it expires.

The token carries who you are (sub) and the organization — your Kobana account — you authorized (org). org is what scopes the data — no tool takes an organization as an argument. A token whose organization does not exist in Régua de Cobrança gets 403 unknown_organization.

Authorization: the person's permissions​

This is where MCP differs from the REST API. On the API, a token acts with the integration's authority. On MCP, tools are filtered by the dashboard permissions of the person who owns the token (sub) in that organization, read on every request. The ceiling is always what that person can do in the dashboard.

The person is found through the Kobana identity recorded when they sign in to the Régua de Cobrança dashboard with their Kobana account. That is why someone who has never signed in to Régua de Cobrança with their Kobana account has no tools at all: there is no membership to link the token to, and every tool is denied with key_owner_not_member. The fix is to sign in to the dashboard once with the Kobana account — or, for someone who is not on the team yet, to ask for an invitation. An invitation to another organization works too: the membership is matched by the person's e-mail.

SituationResult
The person is not a member of the organization (or never signed in to Régua de Cobrança with the Kobana account)Denies every tool (key_owner_not_member).
Membership inactive, or with no permission or roleDenies every tool (membership_inactive).
The permission lookup failsDenies every tool. Fail closed.
Active membershipThe person's permissions and roles, applied tool by tool. dunning.dashboard.* opens everything; dunning.dashboard.charges.* opens the charges family.

Administrative areas — members, roles, keys, settings — are never reachable by a token, and have no tool.

Permissions are read on every call, so revoking a role in the dashboard takes effect on the very next call, without waiting for the token to expire.

A tool the caller cannot run is not listed by tools/list. When tools/call refuses, the code says who fixes it:

CodeWho fixes it
insufficient_scopeAuthorizing again, or using a token that reaches the resource with the access requested — for example, a personal token with write access to it.
insufficient_permissionAn organization admin, granting the permission (the error names which).
membership_inactiveReactivating the person — no permission fixes an inactive membership.
key_owner_not_memberThe person signing in to the Régua de Cobrança dashboard with their Kobana account, or asking for an invitation.

:::tip Recommended posture for agents Authorize the client with an account that holds only the permissions the agent needs. A client authorized by a read-only analyst exposes only the read tools, whatever the model on the other end does. :::

Token scope​

On top of permissions, the MCP server applies the scope the token carries. The two checks add up: a tool only shows up if both allow it. Reading is a read tool, a resource, a prompt and a completion; writing is everything else, destructive tools included.

The scope comes in two shapes.

Token with dunning/… resources — the personal-token case. The resources define how far the token reaches, and read/write/all apply only within them:

Token scopeResult
dunning/charges dunning/people read writeReads and writes charges and debtors — and nothing else
dunning/charges readOnly reads charges
dunning/all readReads everything, writes nothing

delete, import and export on their own do not count as writing.

Token without dunning/… resources — the OAuth-flow case. Only the access level applies: reading with read, write or all; writing with write or all.

A token with neither reaches nothing except dunning_me, which any valid token can call.

The resource of a tool is the first segment of its API path: /collection-rules/... is collection_rules. Debtor contact points belong to people, negotiation campaigns to negotiation_campaigns, and collection-rule steps to collection_rules. The 21 resources are the ones in scopes_supported above.

:::info Clients connected through the OAuth flow are read-only The authorization server grants only read to dynamically registered clients — Claude, ChatGPT, the MCP Inspector, IDEs. For them, the MCP server lists only the read tools, and a tool that writes returns insufficient_scope. For an agent to write today, use a personal token with write access to the resources it needs. :::

The scope check is repeated by the /v1 endpoint when the tool is dispatched — defense in depth. If it ever disagreed, the endpoint would answer 403 INSUFFICIENT_SCOPE.

Personal token revocation​

Revoking a personal token in your Kobana account does not cut access right away: Régua de Cobrança verifies the token locally, without asking Kobana on every call, and it keeps being accepted until it expires. So:

  • pick the shortest validity the use allows;
  • select only the resources and access the agent needs;
  • to cut an agent off immediately, deactivate the person in the organization — permissions are read on every call and take effect at once.

Failure modes​

These happen before the JSON-RPC message is processed, as plain HTTP responses:

HTTPcodeCause
400-32022 (JSON-RPC)Unsupported protocol version, in the MCP-Protocol-Version header or in _meta; data.supported lists the supported ones. See Transport.
400-32020 (JSON-RPC)A header disagrees with the body on the 2026-07-28 era.
401UNAUTHORIZEDToken missing, malformed, invalid, expired, issued for another resource (for example, the REST API), or sent in the URL.
403unknown_organizationThe token is valid, but the organization it names does not exist in Régua de Cobrança.
403FORBIDDENorigin_not_allowed: a browser Origin other than the server's own. A request with no Origin passes.
404NOT_FOUNDThe request arrived through a host that does not serve MCP.
413-32600 (JSON-RPC)Body over 2 MB (Request body too large) — the same cap as the API.
429RATE_LIMITEDRate limit reached — see Rate limit.
503SERVICE_UNAVAILABLEWe could not reach the authorization server to verify the token. It is on our side: retry after Retry-After.

Every 401 carries the WWW-Authenticate challenge above, which sends the client back into the OAuth flow.

Permission and scope denials are not HTTP errors: they come back as a tool result flagged isError, so the model can read them and react — see Errors.