
Autenticação para seu servidor MCP, sem rodar um servidor de autorização
Você construiu um servidor MCP remoto. É útil, então agora os agentes de estranhos querem chamá-lo, e você precisa responder à pergunta que todo servidor remoto enfrenta: quem tem permissão para entrar?
A resposta da especificação MCP é OAuth 2.1: coloque um servidor de autorização na frente, registre clientes, emita tokens. O que está correto, mas é pesado. Um servidor de autorização é um serviço crítico para a segurança e com estado, com um banco de dados de usuários — você o executa ou o aluga, e de qualquer forma, agora é uma infraestrutura que suporta carga para o que pode ser um projeto de fim de semana. Também assume que seus chamadores podem fazer uma dança OAuth e que você deseja contas.
Aqui está a configuração sem nenhum servidor de autorização em lugar algum: a credencial — chamada de escritura — se certifica. Um agente a cria localmente assinando um desafio que você emitiu (ou provando a filiação à frota em conhecimento zero — mais abaixo). Seu servidor a verifica com uma biblioteca: criptografia local mais uma eth_call para um contrato de registro público na Base, servido por qualquer provedor RPC. Sem serviço de token, sem registro de cliente, sem tabela de usuários. Todo o seu stack de autenticação é três rotas:
import { DeedVerifier, sessionJwt } from "@grantor/verify";
import { grantorExpress } from "@grantor/verify/express";
import { Registry } from "@grantor/verify/registry";
const verifier = new DeedVerifier(
RPC_URL, Registry.canonical(), CHAIN_ID, TENANT_ID,
AUDIENCE, ORIGIN, MAX_TTL_SECS, CACHE_TTL_SECS,
false, Math.floor(Date.now() / 1000),
);
const g = grantorExpress({
verifier, app,
challengeEndpoint: "/auth/challenge",
chainId: CHAIN_ID,
modes: ["user-sig", "agent-zk"],
vouchSignature: VOUCH_SIGNATURE, vouchEpoch: VOUCH_EPOCH, vouchExp: VOUCH_EXP,
});
app.get("/auth/challenge", g.challenge);
Essa única chamada também publica automaticamente GET /.well-known/grantor-deed — um documento de descoberta nomeando seu inquilino, cadeia, modos e ponto de desafio — e o verifica automaticamente na inicialização, para que uma má configuração falhe sua inicialização, não o login do primeiro usuário.
A segunda rota troca uma escritura por uma sessão, da maneira que um ponto de token faria — um cliente MCP se autentica uma vez, não por solicitação:
app.post("/auth/token", async (req, res) => {
const { deed, challenge } = req.body;
const claims = await g.guard.verify(JSON.stringify(deed), challenge);
// claims.sub é um sujeito pseudônimo verificado — recomputado pelo
// verificador, não lido da escritura. Crie SUA sessão a partir disso:
res.json({ session_jwt: sessionJwt(claims.sub, AUDIENCE, BigInt(TENANT_ID),
SIGNING_KEY_PEM, BigInt(now), BigInt(TTL), { iss: ORIGIN }) });
});
sessionJwt cria um JWT ES256 simples com sua chave — qualquer biblioteca JOSE o verifica sem nunca importar este SDK. A terceira rota é seu transporte MCP existente, controlado por essa sessão. Essa é toda a superfície.
Três propriedades que você normalmente não obtém de uma configuração de autenticação de fim de semana:
Rejeições ensinam ao chamador. Cada 401 carrega WWW-Authenticate:
Grantor-Deed … mais campos de descoberta/aprendizagem apontando para seu documento de descoberta e uma máquina-rea
A implementação de autenticação leve e autossuficiente pode beneficiar empresas brasileiras ao reduzir a complexidade e os custos operacionais. Com a abordagem de 'deeds', é possível garantir segurança sem a necessidade de infraestrutura pesada. Isso é especialmente útil para startups e projetos em fase inicial.
