
Como construí o servidor MCP do Appwrite (e decidi ocultar a maioria de suas capacidades)
Quando a Anthropic introduziu o Modelo de Protocolo de Contexto em 25 de novembro de 2024, todos ficaram de olho, incluindo Christy, que era a Líder de Engenharia da Appwrite na época. Eu havia acabado de começar meu papel como "Estagiário de Engenharia" e não tinha ideia do que um protocolo totalmente novo significava, ou por que era um grande problema.
Analisando a superfície, eu não estava totalmente errado. O MCP é JSON-RPC com um esquema e um handshake anexados. O que nos levou dezesseis meses foi tudo que estava anexado ao redor disso.
HTTP Streamable não existia quando o MCP foi lançado. Ele substituiu HTTP+SSE na revisão de 26-03-2025.
Os anos do stdio
Christy tinha um servidor stdio funcionando no repositório até 26 de fevereiro de 2025. Já tínhamos chaves de API, então a conexão era simples:
claude mcp add appwrite \
--env APPWRITE_PROJECT_ID=<SEU_PROJECT_ID> \
--env APPWRITE_API_KEY=<SUA_API_KEY> \
--env APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1 \
-- uvx mcp-server-appwrite
Uma chave de API é escopo para exatamente um projeto por design, então o teto estava embutido na credencial. Mudar de projetos significava editar a configuração do seu editor. Criar um projeto era impossível. Assim como qualquer coisa a nível de organização.
A credencial é a diferença total entre os dois transportes, e tudo que é difícil na versão hospedada segue da troca por um token que pertence ao usuário em vez do projeto.
A autorização comeu a programação
De acordo com a especificação, a autorização é genuinamente opcional:
A autorização é OPCIONAL para implementações do MCP. [...] Implementações que usam um transporte baseado em HTTP DEVEM conformar-se a esta especificação.
Para um serviço onde uma chamada de ferramenta pode derrubar um banco de dados, não estávamos confortáveis em tratá-la como opcional. Se você usa Auth0 ou WorkOS, isso é uma tela de configuração. A Appwrite mantém tudo internamente, então Matej construiu o servidor de autorização, e eu construí o servidor de recursos mais o que a Cloud ainda estava faltando antes que clientes reais funcionassem.
Os passos 2 a 6 são a parte que faz "basta colar esta URL" funcionar. Nada é pré-provisionado.
Três RFCs carregam esse fluxo. Metadados de Recursos Protegidos (RFC 9728) é o único REAL DEVE em toda a especificação de autorização:
{
"resource": "https://mcp.appwrite.io/",
"authorization_servers": ["https://cloud.appwrite.io/v1/oauth2/console"],
"scopes_supported": ["..."],
"bearer_methods_supported": ["header"]
}
Indicadores de Recursos (RFC 8707) colocam nossa URI canônica no aud do token, então um token gerado para outro serviço não pode ser reproduzido contra nós. O Registro Dinâmico de Clientes (RFC 7591) é o que permite que um cliente se registre automaticamente. Adicione PKCE com S256, descoberta RFC 8414, e você tem a forma disso.
Os RFCs estão documentados. O que não está documentado é que cada cliente os lê de forma diferente, e você descobre isso na produção:
-
Raycast estava na especificação de autorização
2025-03-26, que procura por/.well-known/oauth-authorization-serverem vez da rota de recurso protegido. Eu só descobri isso colocando um proxy de registro na frente do servidor e observando o que realmente estava pedindo. -
Claude Code re-autenticava toda vez que era executado. Ele escuta em uma porta de loopback efêmera, então a URI de redirecionamento nunca combinava. O BCP de aplicativo nativo do OAuth (RFC 8252 §7.3) diz que você deve permitir qualquer porta em
127.0.0.1. Nós não estávamos. -
Nosso próprio catálogo de escopos quebrou o fluxo. Todos os ~118 escopos granulares produziram um parâmetro
scopede ~2.680 caracteres contra um validador limitado a 2.048. Ninguém nunca chegou a uma tela de consentimento.
Um aviso se você está prestes a construir isso: a RFC 7591 passou de DEVE para PODE em 2025-11-25 e está depreciada a partir de 2026-07-28, substituída por Documentos de Metadados de ID do Cliente. Nós também enviamos isso. Esta parte da especificação ainda está em movimento.
Sessões, e então sem sessões
A especificação 2025-06-18 permitiu que um servidor entregasse um Mcp-Session-Id juntamente com o InitializeResult, com DELETE para terminar e Last-Event-ID para retomar. Nós pulamos tudo isso:
StreamableHTTPSessionManager(app=server, json_response=False, stateless=True)
Cada solicitação carrega um token bearer. Verifique-o, construa um cliente a partir dele, atenda à chamada. Nada para armazenar, nada a perder na reinicialização, nada para tornar persistente entre réplicas.
Isso acabou sendo a aposta certa por uma razão pela qual não posso levar crédito. A revisão de 2026-07-28 removeu sessões do protocolo completamente. Mcp-Session-Id, o handshake initialize, o fluxo GET SSE: tudo foi embora. O que carregamos é a negociação de versão, porque você não pode escolher a versão do protocolo de seus clientes.
Fora do tópico, mas por que não REST?
Muitos intelectuais li
O desenvolvimento de protocolos como o MCP pode impactar empresas brasileiras ao facilitar a integração de serviços e melhorar a segurança na comunicação entre aplicações. A adoção de padrões como o MCP pode otimizar processos e aumentar a eficiência operacional.

