Autenticação
O servidor MCP aceita um Bearer token no cabeçalho Authorization, e nada além disso — sem cookie, sem certificado de cliente.
:::warning Nunca na URL
Token na query string (?access_token=…, ou parâmetros como token, bearer, bearer_token, id_token, api_key e apikey) não autentica, mesmo acompanhado de um cabeçalho válido: a requisição responde 401 com error="invalid_request" no WWW-Authenticate. Um token na URL vaza em logs de proxy e CDN, no Referer e no histórico — a especificação de autorização do MCP e a RFC 6750 proíbem esse uso.
:::
Esse token é um access token emitido pelo authorization server da Kobana (a Conta Kobana, https://account.kobana.com.br), um JWT assinado em RS256 e emitido para este recurso: o aud dele é https://mcp.dunning.kobana.com.br. Ele chega por um de dois caminhos — o fluxo OAuth, feito pelo próprio cliente, ou um token pessoal que você cria na sua conta Kobana — e é verificado do mesmo jeito nos dois casos.
Authorization: Bearer eyJhbGciOiJSUzI1NiIs…
:::info Cada superfície tem a sua audience
Um token emitido para o MCP não vale na API REST (api.dunning.*), e um token da API não vale aqui: os dois são recusados com 401. Um token cujo aud lista mais de um valor também é recusado — ele valeria nas duas superfícies. É o que impede que o token entregue a um assistente seja reaproveitado contra a API, e o contrário.
:::
Dois jeitos de conectar
| Fluxo OAuth | Token pessoal | |
|---|---|---|
| Como você obtém | Cola a URL no cliente; ele abre o login da Kobana e você autoriza | Cria em Conta Kobana → Tokens e Acesso → Novo Token Pessoal: app Régua de Cobrança, uso MCP, recursos e acessos, validade |
| Como o cliente usa | Sozinho, depois da autorização | Você cola no cliente como Authorization: Bearer <token> |
| Validade | Curta, renovada pelo cliente | De 7 dias a 1 ano, escolhida por você |
| Escopo | Só o nível de acesso — hoje, read | Os recursos e acessos que você marcou |
| Bom para | Claude, ChatGPT, IDEs e qualquer cliente com OAuth | Clientes sem suporte a OAuth, scripts e agentes que precisam escrever |
O token pessoal tem que ser criado com o uso MCP. Um token criado para API REST tem a audience da API e é recusado aqui com 401 — e o contrário também vale.
O fluxo OAuth
Você não executa nenhum destes passos: o cliente MCP (Claude, ChatGPT, uma IDE) faz tudo sozinho a partir da URL. Eles estão aqui para quando algo falhar no meio.
-
Desafio. Uma requisição sem token recebe
401com o desafio que todo resource server OAuth 2.1 deve mandar (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" -
Metadata do recurso. O cliente lê o documento apontado. Ele é público e não carrega segredo nenhum — só ponteiros:
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é o que um cliente deve pedir: os 21 recursosdunning/<recurso>maisread.dunning/alle os verbos (write, …) ficam de fora de propósito — o authorization server não os concede a um cliente registrado dinamicamente. Sem essa lista, o cliente pediria os escopos de todos os produtos Kobana, e a autorização seria recusada. -
Metadata do authorization server (RFC 8414), em
https://account.kobana.com.br/.well-known/oauth-authorization-server. -
Registro dinâmico do cliente (RFC 7591). O cliente se registra sozinho.
-
Autorização com PKCE e
resource(RFC 8707). Você faz login na Kobana, escolhe a organização (se tiver mais de uma) e autoriza. -
Token. O cliente troca o código por um access token de vida curta e passa a mandá-lo em toda requisição, renovando quando expira.
O token carrega quem você é (sub) e a organização que você autorizou (org, a conta Kobana). É o org que escopa os dados — nenhuma tool aceita organização como argumento. Um token cuja organização não existe na Régua de Cobrança recebe 403 unknown_organization.
Permissões da pessoa
Aqui está a diferença mais importante em relação à API REST. Na API, o token age com a autoridade da integração. No MCP, nunca: as tools são filtradas pelas permissões de dashboard da pessoa dona do token (o sub) naquela organização, lidas a cada requisição. O teto é sempre o que essa pessoa pode fazer na interface.
A pessoa é localizada pela identidade Kobana que a Régua de Cobrança registra quando ela entra no dashboard com a conta Kobana. Por isso, quem nunca entrou no dashboard da Régua de Cobrança com a conta Kobana não tem tool nenhuma: recebe key_owner_not_member, mesmo sendo membro da organização. Resolve-se entrando uma vez no dashboard com a conta Kobana — ou, se a pessoa ainda não é membro, pedindo um convite. Um convite para outra organização também vale: lá, o vínculo é encontrado pelo e-mail da pessoa.
| Situação | Resultado |
|---|---|
| A pessoa não é membro da organização, ou nunca entrou na Régua de Cobrança com a conta Kobana | Nega todas as tools (key_owner_not_member). |
| Vínculo inativo, ou sem nenhuma permissão nem papel | Nega todas as tools (membership_inactive). |
| A consulta de permissões falha | Nega todas as tools. Falha fechada. |
| Vínculo ativo | As permissões e papéis da pessoa, aplicados tool a tool. dunning.dashboard.* libera tudo; dunning.dashboard.charges.* libera a família de cobranças. |
As permissões são lidas a cada chamada, então revogar um papel no dashboard vale já na chamada seguinte, sem esperar o token expirar. Recursos administrativos — membros, papéis, chaves, configurações — nunca são alcançáveis por token, e nem têm tool.
Uma tool que o chamador não pode rodar não é listada em tools/list. Quando tools/call recusa, o código diz quem resolve:
| Código | Quem resolve |
|---|---|
insufficient_scope | Usar um token que alcance o recurso com o acesso pedido — por exemplo, um token pessoal com escrita nesse recurso. |
insufficient_permission | Um admin da organização, concedendo a permissão (o erro nomeia qual). |
membership_inactive | Reativar a pessoa na organização — nenhuma permissão conserta um vínculo inativo. |
key_owner_not_member | A pessoa entrar uma vez no dashboard da Régua de Cobrança com a conta Kobana, ou pedir um convite para a organização. |
:::tip Postura recomendada para agentes Autorize o cliente com uma conta que só tenha as permissões que o agente precisa. Um cliente autorizado por um analista somente-leitura expõe só as tools de leitura, faça o que fizer o modelo do outro lado. :::
Escopo do token
Além das permissões, o MCP aplica o escopo que o token carrega, no claim scope. As duas checagens se somam: uma tool só aparece se as duas deixarem. Leitura é tool de leitura, resource, prompt e completion; escrita é todo o resto, destrutivas incluídas.
O escopo chega em um de dois formatos.
Token com recursos dunning/… — o caso do token pessoal. Os recursos definem até onde o token chega, e read/write/all valem só dentro deles:
| Escopo do token | Resultado |
|---|---|
dunning/charges dunning/people read write | Lê e escreve cobranças e devedores — e nada mais |
dunning/charges read | Só lê cobranças |
dunning/all read | Lê tudo, não escreve nada |
delete, import e export sozinhos não contam como escrita.
Token sem recursos dunning/… — o caso do fluxo OAuth. Vale só o nível de acesso: leitura com read, write ou all; escrita com write ou all.
Um token que não traz nem recurso nem nível de acesso não alcança tool nenhuma — só dunning_me.
O recurso de cada tool é o primeiro segmento do caminho da API: /collection-rules/... é collection_rules. Os contatos de um devedor ficam em people, os passos de régua em collection_rules e as campanhas de negociação em negotiation_campaigns. Os 21 recursos são:
agreements, audit_logs, charges, classifications, collection_rules, disputes, email_layouts, exports, imports, interactions, metrics, negativations, negotiation_campaigns, notification_channels, notification_preferences, notifications, people, protests, tasks, templates, webhook_endpoints.
:::info Clientes conectados pelo fluxo OAuth ficam só com leitura
O authorization server concede apenas read a clientes registrados dinamicamente — Claude, ChatGPT, o MCP Inspector, IDEs. Para eles, o MCP lista só as tools de leitura, e uma tool que escreve volta insufficient_scope. Para um agente escrever hoje, use um token pessoal com acesso de escrita nos recursos de que ele precisa.
:::
A checagem de escopo se repete na /v1 quando a tool é despachada — defesa em profundidade. Se ela recusar lá, a tool volta o 403 INSUFFICIENT_SCOPE da API como erro de tool.
Revogação do token pessoal
Revogar um token pessoal na sua conta Kobana não corta o acesso na hora: a Régua de Cobrança verifica o token localmente, sem consultar a Kobana a cada chamada, e ele continua aceito até a validade dele. Por isso:
- escolha a validade mais curta que o uso permite;
- marque só os recursos e acessos de que o agente precisa;
- para cortar um agente imediatamente, desative a pessoa na organização — as permissões são lidas a cada chamada e valem na hora.
Modos de falha
| HTTP | code | Causa |
|---|---|---|
| 400 | -32022 (JSON-RPC) | Versão do protocolo não suportada; data.supported lista as que são. Veja Transporte. |
| 400 | -32020 (JSON-RPC) | Na era 2026-07-28, um cabeçalho diverge do corpo. Veja Transporte. |
| 401 | UNAUTHORIZED | Token ausente, malformado, inválido, expirado, emitido para outro recurso (por exemplo, para a API REST) ou enviado na URL. |
| 403 | unknown_organization | O token é válido, mas a organização que ele nomeia não existe na Régua de Cobrança. |
| 403 | FORBIDDEN | origin_not_allowed: um Origin de navegador que não é o próprio servidor. Requisição sem Origin passa. |
| 404 | NOT_FOUND | A requisição chegou por um host que não serve MCP. |
| 413 | -32600 (JSON-RPC) | Corpo acima de 2 MB (Request body too large) — o mesmo teto da API v1. |
| 429 | RATE_LIMITED | Limite de requisições atingido — veja Limite de requisições. |
| 503 | SERVICE_UNAVAILABLE | Não conseguimos falar com o authorization server para verificar o token. É do nosso lado: tente de novo depois do Retry-After. |
Todo 401 carrega o desafio WWW-Authenticate acima, que manda o cliente de volta ao fluxo OAuth.
Negativas de escopo e de permissão não são erros HTTP: elas voltam como resultado de tool marcado isError, para que o modelo leia e reaja — veja Erros.
Host e borda
/mcp tem que ser alcançável só por mcp.dunning.* — nunca pelo host do app, nunca pelo host da API, que apontam para a mesma aplicação. Por qualquer outro host, o endpoint responde 404: ele não anuncia que existe em outro lugar.