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,
api_key e key) 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
(https://account.kobana.com.br), um JWT assinado em RS256 e emitido para
este recurso: o aud dele é https://mcp.kadu.ia.br (em staging,
https://mcp.staging.kadu.ia.br), e só ele — um token com mais de uma audience
é recusado. O token chega por um de dois caminhos — o fluxo OAuth, feito pelo
próprio cliente, ou um token pessoal que você cria na Conta Kobana — e é
verificado do mesmo jeito nos dois casos, com a mesma checagem local de
assinatura da API v1.
Authorization: Bearer eyJhbGciOiJSUzI1NiIs…
:::info Cada superfície tem a sua audience
Um token emitido para o MCP não vale na API (api.kadu.ia.br), e um token
da API não vale aqui. É o que impede que o token entregue a um assistente seja
reaproveitado como credencial REST — 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 Conta Kobana e você autoriza | Cria no assistente de tokens da Conta Kobana, com o uso MCP, os recursos e acessos e a validade |
| Como o cliente usa | Sozinho, depois da autorização, renovando quando expira | Você cola no cliente como Authorization: Bearer <token> |
| Escopo | Os recursos e os níveis de acesso que você manteve marcados na tela de consentimento | Os recursos e acessos que você marcou |
| Bom para | Claude, ChatGPT, IDEs e qualquer cliente com OAuth | Clientes sem suporte a OAuth, scripts e automações |
O token pessoal tem que ser criado com o uso MCP. Um token criado para a
API 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 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="kadu-mcp",error="invalid_token",resource_metadata="https://mcp.kadu.ia.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.kadu.ia.br/.well-known/oauth-protected-resource{"resource": "https://mcp.kadu.ia.br","authorization_servers": ["https://account.kobana.com.br"],"bearer_methods_supported": ["header"],"scopes_supported": ["kadu/activities","kadu/environments","kadu/executions","kadu/files","kadu/memberships","kadu/memories","kadu/organizations","kadu/repository","kadu/roles","kadu/schedules","kadu/secrets","kadu/system_events","kadu/tasks","kadu/todos","kadu/usage","kadu/users","kadu/webhook_deliveries","kadu/webhooks","kadu/workspaces","read","write","delete","import","export","all"],"resource_documentation": "https://api.kadu.ia.br/v1/docs"}kadu/allfica de fora de propósito: o curinga não é entregue a um cliente que se registrou sozinho. -
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 Conta Kobana, escolhe a organização (se tiver mais de uma), revisa o que o cliente pede 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). É o org que escopa os dados — nenhuma tool aceita organização como
argumento. Um token cuja organização não existe no Kadu recebe
403 unknown_organization.
Autorização: o papel de quem autorizou
O que um chamador vê é mais estreito que o catálogo: as tools são filtradas
pelo papel da pessoa do token na organização — as mesmas permissões
agent.<recurso>.<ação> do console e da API (veja
Permissões). A tool exige exatamente a permissão
da rota que ela espelha. O teto é sempre o que essa pessoa pode fazer no
console.
| Situação | Resultado |
|---|---|
| A pessoa do token não é membro da organização | Nega todas as tools (not_a_member). Autorize de novo com uma conta que pertença à organização. |
| Membro | As permissões do papel, aplicadas tool a tool. Os curingas *, agent.* e agent.<recurso>.* valem como no console. |
| A consulta de permissões falha | Nega todas as tools. Falha fechada. |
Diferente da API, o MCP não tem token de integração sem pessoa: o token tem
que levar a uma pessoa da organização (pelo sub ou pelo e-mail do token),
senão nenhuma tool roda.
As permissões são relidas pelo menos a cada minuto, então mudar ou remover o papel de alguém vale em até um minuto, sem esperar o token expirar.
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 | Você, autorizando de novo com o acesso que faltou — ou usando um token pessoal que alcance o recurso com o nível pedido. |
insufficient_permission | Um administrador da organização, concedendo a permissão (o erro nomeia qual). |
not_a_member | Autorizar de novo com uma conta que seja membro da organização. |
:::tip Postura recomendada para agentes Autorize o cliente com uma conta que só tenha as permissões de que o agente precisa. Um cliente autorizado por alguém que só lê expõe só as tools de leitura, faça o que fizer o modelo do outro lado. :::
Escopo do token
Além do papel, o MCP aplica o escopo que o token carrega, no claim scope, com
a mesma gramática da API (veja
Autenticação da API → Escopos). As duas
checagens se somam: uma tool só aparece se as duas deixarem.
| Tool | Exige |
|---|---|
Leitura (*_list, *_get, kadu_usage_get…) | O escopo do recurso (kadu/tasks) |
Exclusão (*_delete) | O escopo do recurso + delete |
| Qualquer outra alteração (criar, alterar, disparar, cancelar, continuar, sincronizar, reenviar, girar segredo) | O escopo do recurso + write |
kadu_me_get | Qualquer token válido |
O recurso é o primeiro segmento da rota que a tool espelha:
kadu_executions_logs_list é do recurso executions, e kadu_tasks_run exige
kadu/tasks write. As aprovações por link (kadu_operation_approvals_*) são
lidas com kadu/system_events. Um nível não substitui outro: write não
exclui. Nenhuma tool exige import ou export.
| Escopo do token | Resultado |
|---|---|
kadu/executions kadu/todos read write | Lê e altera execuções e pendências — cancela, continua e resolve — e nada mais |
kadu/tasks kadu/executions write | Lê, dispara e acompanha tarefas, sem excluir nada |
read (sem nenhum kadu/…) | Lê tudo, não escreve nada |
:::info Clientes conectados pelo fluxo OAuth pedem todos os níveis — você escolhe quais conceder
O documento /.well-known/oauth-protected-resource anuncia os recursos do Kadu
e os seis níveis de acesso, e é isso que o cliente pede na tela de
consentimento. Lá você desmarca o que não quer conceder: o token emitido traz
só os recursos e os níveis que ficaram marcados, e o MCP lista só as tools que
eles alcançam. Uma tool fora dele volta insufficient_scope, e a saída é
autorizar de novo concedendo o acesso que faltou — por exemplo, delete para
as tools de exclusão.
:::
Revogação do token pessoal
O Kadu verifica o token sozinho, pela assinatura, sem consultar a Conta Kobana a cada chamada: revogar um token pessoal lá não corta o acesso na hora, 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, remova o vínculo da pessoa com a organização ou troque o papel dela — vale em até um minuto.
Modos de falha
| HTTP | code | Causa |
|---|---|---|
| 400 | -32022 (JSON-RPC) | Versão do protocolo não suportada, no cabeçalho MCP-Protocol-Version ou no _meta; data.supported lista as que são. Veja Transporte. |
| 401 | invalid_token | Token ausente, malformado, inválido, emitido para outro recurso (por exemplo, para a API) ou com mais de uma audience no aud. |
| 401 | token_expired | O token expirou. O cliente renova e tenta de novo. |
| 401 | token_revoked | O token foi revogado no Kadu. |
| 401 | invalid_request | Token na query string — veja o aviso no topo da página. |
| 403 | unknown_organization | O token é válido, mas a organização que ele nomeia não existe no Kadu. |
| 403 | FORBIDDEN | Origin fora da lista permitida (origin_not_allowed). |
| 404 | NOT_FOUND | A requisição chegou por um host que não serve MCP. |
| 413 | -32600 (JSON-RPC) | Corpo acima de 1 MB. |
| 429 | TOO_MANY_REQUESTS | Limite de Requisições atingido, com Retry-After e X-RateLimit-* — veja Limite de Requisições. |
| 503 | SERVICE_UNAVAILABLE | Não conseguimos verificar o token contra o Authorization Server. É 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. O desafio diz sempre error="invalid_token" (ou
invalid_request, para token na URL), como manda a RFC 6750; é o code do
corpo que separa um token expirado (token_expired) dos demais. Tentativas
com token recusado contam num limite por IP, e passado o limite a resposta vira
429.
Nenhuma resposta do MCP é cacheável: todas, inclusive as recusas, vêm com
Cache-Control: no-store e Content-Security-Policy: default-src 'none'. A
exceção é o documento /.well-known/oauth-protected-resource, público, com
Cache-Control: public, max-age=300.
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
O MCP só responde por mcp.kadu.ia.br — nunca pelo host do console, nunca pelo
host da API, que apontam para a mesma aplicação. Por qualquer outro caminho a
resposta é 404: o endpoint não anuncia que existe em outro lugar.
O identificador de recurso OAuth — e, portanto, a audience que o token precisa
ter — vem da configuração do servidor, nunca do Host da requisição, para
que um Host forjado não consiga apontar um cliente para outro identificador.