Idempotência
As tools que criam um recurso aceitam um argumento opcional idempotency_key. Ele é enviado ao endpoint como o cabeçalho X-Idempotency-Key que a API REST já honra.
{ "name": "dashboards_portal_users_create",
"arguments": {
"email": "joao@example.com",
"tenant_id": "cliente-1042",
"idempotency_key": "7c4a8d09-ca37-4f21-b1f0-3d2f2b8c9a11"
} }
| Formato | String opaca, 1 a 255 caracteres. Um UUID é a escolha usual. |
| Repetição com o mesmo corpo | Devolve a resposta original, sem rodar a operação de novo. |
| Repetição com corpo diferente | Conflito. |
Por que um agente precisa disto mais que o seu backend
Um cliente MCP que dá timeout e tenta de novo, ou um modelo que decide "não tenho certeza se aquilo funcionou, vou refazer", é exatamente o padrão que cria o segundo papel, o segundo convite e o segundo usuário. Seu backend tem um laço de retry que você escreveu; o agente tem um julgamento.
Mande uma chave sempre que a operação criar algo ou enviar um convite. Leituras e atualizações são naturalmente idempotentes e não pedem chave.
Tools que aceitam
| Família | Tools |
|---|---|
portal-users | dashboards_portal_users_create, dashboards_portal_users_invite |
users, roles | dashboards_dashboard_users_create, dashboards_dashboard_users_upsert, dashboards_team_roles_create, dashboards_team_roles_upsert |
:::caution Os convites
dashboards_portal_users_invite envia um e-mail ao usuário do portal — um efeito no mundo que nenhum _delete desfaz. Mande idempotency_key nele mesmo quando você acha que não vai repetir.
:::
Upsert não é idempotência
*_upsert casa um registro existente por id, depois external_id, depois pela chave de negócio do recurso, e cria só se não achar. Isso resolve "não sei se já existe", que é um problema diferente de "não sei se minha chamada chegou". As duas coisas se combinam bem: um upsert com idempotency_key é seguro nas duas dimensões.