Imagine um post do seu quadro de feedback que foi parar em Recusado durante a noite. O histórico diz que quem fez isso foi o projeto.
Ninguém da equipe lembra de ter feito isso. Foi o assistente de alguém, rodando com a chave MCP que todo mundo copiou da mesma página de configurações, e descobrir de quem exige uma rodada de mensagens.
É assim que a maioria dos servidores MCP funciona hoje, porque eles pegam emprestado o modelo da chave de API. No Kookee, acabei de trocá-lo por tokens pessoais. A seguir: por que a chave compartilhada não se sustenta, o que lancei no lugar dela e os detalhes que fizeram diferença.
A chave compartilhada é um hábito herdado da chave de API
Uma chave de API identifica uma aplicação. Um app, uma chave, e a chave só faz o que o app precisa — na API pública do Kookee, ler conteúdo publicado. Para aplicações, esse modelo funciona bem.
Um servidor MCP conecta outra coisa: o assistente de uma pessoa, que age sob as instruções dela e muda coisas. Dê a ele uma chave do projeto e três coisas quebram ao mesmo tempo.
Ninguém fez. Toda mudança é atribuída ao projeto. Quando um post muda de categoria, ganha outro responsável ou recebe uma resposta, não dá para saber o assistente de qual colega fez isso, nem se foi uma pessoa.
Todo mundo pode tudo. Uma chave significa um nível de acesso. O colega que só queria que o assistente resumisse o feedback tem nas mãos uma chave que publica.
Não dá para tirar o acesso de uma pessoa só. Quando alguém sai da equipe, o único jeito de cortar o assistente dela é trocar a chave, o que quebra a configuração de todos os outros. Então, na prática, ninguém troca.
E uma chave que todo mundo copia acaba em todo lugar: em dotfiles, num .mcp.json commitado, numa captura de tela num chamado de suporte.
O que lancei: um token por pessoa, por projeto
Agora cada membro cria o próprio token na página do servidor MCP do projeto. O token age como esse membro — com a função e as permissões dele, verificadas a cada requisição — e toda mudança que ele faz fica registrada no nome desse membro.

Somente leitura é o padrão. O acesso de escrita exige um segundo clique, deliberado.
Seis detalhes que decidem se funciona
1. Somente leitura significa que as ferramentas de escrita não existem
A implementação óbvia recusa a escrita quando ela é chamada. Só que a ferramenta continua anunciada: o modelo vê update_feedback na lista de ferramentas, planeja uma triagem inteira em torno dela e falha no último passo com um erro que depois tenta contornar.
Por isso, uma sessão somente leitura nunca registra as ferramentas de escrita, e as instruções do servidor deixam de fora todos os fluxos de escrita e explicam como conseguir acesso de escrita. Em linhas gerais:
registerReadTools(server, auth);
if (auth.token.canWrite) {
registerWriteTools(server, auth);
}
Assim o assistente pode dizer claramente ao usuário que o token dele é somente leitura, em vez de improvisar.
2. Verifique a pessoa por trás do token a cada requisição
Um token dura mais que o motivo pelo qual foi emitido. Por isso, validar um token não para em “o hash confere”: a validação carrega o membro por trás do token e verifica se a conta dele está ativa, se o projeto não está banido e se ele ainda é membro do projeto.
if (!plain.startsWith('kkm_')) return fail('not_mcp_token');
const token = await findTokenByHash(sha256(plain));
if (!token) return fail('not_found');
if (!token.user.isActive) return fail('inactive');
if (token.project.bannedAt) return fail('banned');
if (!(await isMember(token.projectId, token.userId))) return fail('not_member');
Remover ou banir um membro também apaga os tokens dele, na mesma transação da mudança de participação. A verificação a cada requisição já bloqueia o acesso; apagar as linhas mantém a lista de tokens fiel à realidade.
3. Guarde um hash, mostre uma vez, dê um prefixo
Só um hash SHA-256 do token fica guardado. O texto puro aparece uma vez, no diálogo que cria o token, e dali em diante a lista mostra só os quatro últimos caracteres.
O prefixo tem duas funções. kkm_ é diferente do kk_ de uma chave de API, então um scanner de segredos, ou uma pessoa, sabe qual tipo vazou. E ele deixa o servidor devolver um erro útil: uma chave de API enviada ao endpoint MCP recebe MCP_PERSONAL_TOKEN_REQUIRED, que diz à pessoa exatamente o que fazer. Qualquer outra falha recebe um único MCP_TOKEN_INVALID genérico, para que ninguém descubra por tentativa por que um token foi recusado; o motivo exato vai só para o log do servidor.
4. Mostre a autoria onde as pessoas olham
O log de auditoria é o lugar aonde a responsabilização vai para ser ignorada. O Kookee grava um registro de auditoria para cada mudança via MCP, mas o que importa fica visível: cada escrita guarda o id do token junto com o registro de atividade ou o comentário que cria, e o painel marca esses itens com via MCP.

A ferramenta de histórico que o assistente lê informa a mesma marca, então a pergunta “quem moveu isto para Planejado, e foi um assistente?” tem resposta dos dois lados. A API pública de comentários remove essa marca: os visitantes veem uma resposta da equipe, nunca o id de um token.
5. Limite a taxa de escritas por pessoa e por ferramenta
Um agente descontrolado que repete uma edição que falha pode fazer centenas de chamadas por minuto. Um limite de escrita por projeto bloquearia os colegas que não fizeram nada de errado, e um limite por token se contorna criando um segundo token.
Por isso, as ferramentas de escrita têm limite por membro e por ferramenta: 60 chamadas por minuto para update_feedback e outras 60, separadas, para create_comment. A janela de limite do MCP para o projeto inteiro também fica separada da janela da API pública, para que um agente disparando chamadas em paralelo não esgote o limite do site que os seus clientes usam.
6. Mantenha a configuração compartilhada sem segredos
A equipe quer um único .mcp.json commitado, para que todos tenham a mesma configuração. Com tokens pessoais, esse arquivo não pode conter nenhum token. Em vez disso, ele lê o token do ambiente de cada pessoa:
{
"mcpServers": {
"kookee": {
"type": "http",
"url": "https://api.kookee.dev/api/mcp",
"headers": { "Authorization": "Bearer ${KOOKEE_MCP_TOKEN}" }
}
}
}
Cada colega define KOOKEE_MCP_TOKEN com o próprio token. O comando que o painel mostra para o Claude Code faz o mesmo com $KOOKEE_MCP_TOKEN, exceto logo depois que você cria um token: nesse momento, o comando já vem com o token preenchido.
Se você mantém um servidor MCP
Emita credenciais por pessoa e por workspace. Um cliente MCP é o agente de uma pessoa, não uma aplicação.
Faça de somente leitura o padrão e não registre ferramentas que o token não pode usar.
Verifique de novo a pessoa por trás do token a cada requisição: conta, participação, suspensão.
Guarde hashes, mostre os tokens uma vez só e dê a eles um prefixo próprio.
Diferencie “tipo errado de credencial” de “inválido”, e não diga nada além disso.
Registre o token em cada mudança e mostre-o onde as pessoas leem o histórico.
Limite a taxa de escritas por pessoa e por ferramenta, separadamente da sua API pública.
Mantenha a configuração compartilhada do cliente sem segredos, usando variáveis de ambiente.
O servidor MCP do Kookee agora usa tokens pessoais, e os assistentes conseguem ler e fazer a triagem do seu quadro de feedback. Veja Gerenciar seu conteúdo a partir de um assistente de IA (MCP) para a configuração, ou Fazer a triagem do feedback com um assistente de IA para as novas ferramentas.