Tutoriais de VPS
MCP Server auto-hospedado em VPS (2026): guia de implantação segura com Streamable HTTP, OAuth, Docker e Caddy
Quer colocar um MCP Server em uma VPS para que Claude, Cursor e outros clientes o usem remotamente? Este artigo implanta um serviço Streamable HTTP com FastMCP, Docker Compose e Caddy, e explica OAuth, validação de Origin, privilégio mínimo, logs e erros comuns.

MCP Server auto-hospedado em VPS (2026): guia de implantação segura com Streamable HTTP, OAuth, Docker e Caddy
A primeira experiência de muita gente com MCP é rodar stdio na máquina local: Claude Desktop, Claude Code ou Cursor iniciam um processo filho e, se as ferramentas funcionam, já é considerado sucesso.
Mas assim que você começa a enfrentar alguma das situações abaixo, o stdio local deixa de ser suficiente:
- Você quer compartilhar um mesmo conjunto de ferramentas entre notebook, desktop e máquina de desenvolvimento remota
- Você quer disponibilizar as ferramentas para membros da equipe ou vários clientes ao mesmo tempo
- Você precisa de um ponto de entrada remoto que fique sempre online, seja auditável e possa ser atualizado
É aí que entra o MCP Server remoto em uma VPS.
Vamos à conclusão logo de início: para novas implantações remotas de MCP em 2026, a linha principal deve ser Streamable HTTP, e não o antigo HTTP+SSE. stdio continua sendo a opção padrão para integração local; só vale a pena colocá-lo em uma VPS quando você realmente precisa de acesso remoto, HTTPS, autenticação, proxy reverso e reutilização por vários clientes.
Nota de escopo: este artigo segue a especificação publicada do MCP
2025-11-25e também lembra que, na inicialização, ocorre a negociação da versão do protocolo. Ou seja, cliente, SDK e servidor não precisam estar todos no mesmo número de versão; em cenários HTTP, é preciso olhar o resultado da negociação eMCP-Protocol-Version, sem fixar a afirmação de que "todos os clientes só suportam uma versão específica".
Se suas ferramentas só são usadas na máquina local — ler um repositório de código local, chamar um banco de dados local, rodar scripts que só você usa —, stdio continua sendo o mais prático.
Os limites de stdio são claros:
- O próprio cliente inicia o processo filho
- As permissões basicamente herdam o ambiente de execução local
- Não precisa de domínio, proxy reverso, HTTPS nem OAuth
- Também não é adequado para conexões remotas diretas de outros dispositivos
O MCP remoto é mais indicado para estes cenários:
- Você quer expor ferramentas de negócio somente leitura atrás de uma entrada HTTPS pública
- Você quer que Claude, Cursor e um console interno de ferramentas compartilhem um mesmo serviço
- Você quer centralizar logs, cotas, atualizações e acesso às APIs downstream
Se você ainda está avaliando como escolher uma VPS, comece pelo guia de escolha de configuração de VPS.
| Método | Para que serve | Como usar agora |
|---|---|---|
stdio | Clientes locais que iniciam ferramentas locais | Continue usando; padrão em ambiente local |
Antigo HTTP+SSE | Compatibilidade com clientes antigos | Mantenha apenas quando precisar ser compatível com implementações antigas |
Streamable HTTP | A nova linha principal para implantação remota | VPS, auto-hospedagem, HTTPS e autenticação passam por ele |
Há três pontos-chave:
stdionão é um protocolo de implantação remota. É a forma de "o cliente iniciar um processo filho local".- O Streamable HTTP usa um único endpoint MCP. O cliente envia JSON-RPC via POST, e a requisição pode retornar JSON ou um fluxo SSE; quando necessário, o cliente também pode enviar um GET para o mesmo endpoint para estabelecer um fluxo de mensagens do servidor.
- O antigo HTTP+SSE foi substituído. O antigo padrão de "dois endpoints" só serve para compatibilidade com clientes legados e não deve mais ser a opção padrão em projetos novos.
Então, se você vai criar um serviço novo em uma VPS agora, a abordagem mais confiável é:
- Para clientes novos: priorize Streamable HTTP
- Para clientes antigos: tome uma decisão explícita de compatibilidade e, se necessário, mantenha o endpoint antigo separadamente
- Não faça "degradação automática para um modo antigo mais frouxo e menos seguro"
O próprio MCP Server muitas vezes não é a parte que mais consome recursos; o que realmente consome costuma ser:
- Bancos de dados downstream
- Automação de navegador
- Busca vetorial
- Chamadas à API do modelo
- Conversão de arquivos, OCR, crawlers
Se o exemplo deste artigo for apenas uma ferramenta somente leitura, 1C1G ou 2C2G podem servir como ponto de partida para "a escala do exemplo deste artigo". Mas isso não é um requisito mínimo oficial do MCP nem uma promessa geral. No fim, você deve dimensionar conforme a carga das suas próprias ferramentas.
Na prática, vale prestar mais atenção a:
- Se suas ferramentas chamam APIs downstream de forma concorrente
- Se você precisa de cache de estado ou armazenamento de sessão
- Qual é o volume de logs, de backups e de novas tentativas em caso de falha
- Se você vai compartilhar a mesma VPS com outros aplicativos
Se você pretende hospedar vários serviços com Compose, veja também o guia de health checks e limites de recursos do Docker Compose em produção.
Este artigo usa uma arquitetura deliberadamente enxuta:
Claude / Cursor / 其他 MCP Client
↓ HTTPS
Caddy
↓ Docker 内网
FastMCP Streamable HTTP
↓ allowlist / 只读访问
固定的数据源或内部 API
Aqui é preciso separar os requisitos do protocolo das escolhas operacionais:
- No nível do protocolo MCP: a autorização é uma capacidade opcional para todo o ecossistema MCP; não é toda implementação que precisa usar OAuth.
stdioem cenários locais normalmente herda o ambiente de execução local; mas esta arquitetura HTTP de produção deste artigo, voltada para a internet e acessível por clientes remotos, precisa ter autenticação, e, assim que você implementar MCP HTTP auth, deve seguir a especificação de autorização do MCP para metadados de recurso, cabeçalho Bearer, validação de audience/resource do token e HTTPS. - Escolhas operacionais deste artigo: FastMCP, Docker Compose, Caddy, expor apenas 80/443, endpoint de health separado, rotação de logs, limites de recursos
Ou seja, Caddy e Compose não são exigências obrigatórias do protocolo MCP; são apenas convenientes e fáceis de manter nesta implementação.
No exemplo, evito de propósito shell arbitrário, caminhos arbitrários, busca de URL arbitrária e qualquer "ferramenta de consulta universal". O maior medo do MCP remoto não é ter poucas funcionalidades, mas uma entrada ampla demais, parâmetros livres demais e, no fim, o modelo conseguir testar tudo.
A seguir está um app.py completo que se encaixa com o Dockerfile, o Compose e o Caddy que vêm depois. Ele coloca a ferramenta somente leitura, JWTVerifier, a rota não sensível /health e app = mcp.http_app(path="/mcp") no mesmo arquivo; a proteção de Host / Origin continua sendo delegada à configuração oficial de variáveis de ambiente que vem depois:
import os
from typing import Literal
from fastmcp import FastMCP
from fastmcp.server.auth.providers.jwt import JWTVerifier
from starlette.requests import Request
from starlette.responses import PlainTextResponse
auth = JWTVerifier(
jwks_uri=os.environ["AUTH_JWKS_URI"],
issuer=os.environ["AUTH_ISSUER"],
audience=os.environ["AUTH_AUDIENCE"],
)
mcp = FastMCP("ops-readonly", auth=auth)
SERVICE_STATE = {
"api": {"status": "ok", "latency_ms": 32},
"worker": {"status": "degraded", "latency_ms": 85},
"billing": {"status": "ok", "latency_ms": 41},
}
@mcp.tool()
def get_service_status(service: Literal["api", "worker", "billing"]) -> dict:
"""Return a tiny, read-only status snapshot for one allowed service."""
item = SERVICE_STATE[service]
return {
"service": service,
"status": item["status"],
"latency_ms": item["latency_ms"],
}
@mcp.custom_route("/health", methods=["GET"])
async def health(_: Request) -> PlainTextResponse:
return PlainTextResponse("OK")
app = mcp.http_app(
path="/mcp",
)
O limite de segurança deste exemplo é bastante claro:
- A entrada não é uma string arbitrária, mas uma allowlist fixa
- A saída é um JSON pequeno, sem tokens, senhas ou logs brutos
- Não tem efeitos colaterais: não grava arquivos, não apaga dados, não faz requisições externas
/healthexiste separadamente, então a verificação de liveness operacional não precisa chamar de fato uma ferramenta de alto privilégio- A autenticação é vinculada a
FastMCP(..., auth=auth)desde o início, então nunca se torna um/mcppúblico sem autenticação app = mcp.http_app(path="/mcp")e os posterioresuvicorn app:app, Compose e Caddy apontam para a mesma entrada- A proteção de Host / Origin não é "enfiada à força" em
http_app(...)por parâmetros personalizados, mas fornecida de forma unificada pelos posterioresFASTMCP_HTTP_HOST_ORIGIN_PROTECTION,FASTMCP_HTTP_ALLOWED_HOSTSeFASTMCP_HTTP_ALLOWED_ORIGINS
Se você quiser conectar ferramentas a um banco de dados, sistema de tickets ou API interna, recomendo manter esta ideia: uma ferramenta faz uma coisa só, o intervalo de parâmetros é reduzido ao máximo e o número de campos retornados é o menor possível.
6. Colocando em produção com Docker Compose: fixe versões, limite privilégios e não exponha a porta da aplicação diretamente
O erro mais comum em produção é, por conveniência, expor ports: ["8000:8000"] diretamente para fora e depois executar pip install temporariamente na inicialização do contêiner, de modo que nem as versões das dependências nem o conteúdo da imagem são reproduzíveis. Isso funciona no curto prazo, mas a longo prazo deixa brechas com facilidade.
Uma abordagem mais estável: as dependências são instaladas na fase de build da imagem, a aplicação só serve a porta 8000 na rede interna do Docker e a entrada pública fica apenas com o Caddy.
Primeiro fixe as dependências em requirements.txt:
fastmcp==3.2.4
uvicorn==0.35.0
Depois use um Dockerfile explícito:
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir -r /app/requirements.txt \
&& useradd --create-home --uid 10001 appuser
COPY app.py /app/app.py
USER appuser
EXPOSE 8000
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
O sentido deste app.py não é "todo MCP precisa copiar", mas: quando você coloca um MCP HTTP remoto em um ambiente de produção público, no mínimo ele deve ter a autenticação ativada desde o primeiro dia, em vez de ser um endpoint público sem autenticação. Se depois você conectar um provedor como Keycloak, Scalekit ou Azure, siga a documentação oficial de cada provedor; não invente seu próprio código OAuth.
services:
mcp-app:
build:
context: .
dockerfile: Dockerfile
image: mcp-server:2026-07
expose:
- "8000"
environment:
FASTMCP_HTTP_HOST_ORIGIN_PROTECTION: "true"
FASTMCP_HTTP_ALLOWED_HOSTS: '["mcp.example.com"]'
FASTMCP_HTTP_ALLOWED_ORIGINS: '["https://app.example.com"]'
AUTH_JWKS_URI: "https://auth.example.com/.well-known/jwks.json"
AUTH_ISSUER: "https://auth.example.com"
AUTH_AUDIENCE: "https://mcp.example.com/mcp"
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=2).read()"]
interval: 30s
timeout: 3s
retries: 3
start_period: 10s
restart: unless-stopped
read_only: true
tmpfs:
- /tmp
user: "10001:10001"
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
mem_limit: 512m
cpus: 1.0
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
networks:
- edge
caddy:
image: caddy:2.10
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
depends_on:
mcp-app:
condition: service_healthy
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
networks:
- edge
networks:
edge:
volumes:
caddy-data:
caddy-config:
Há vários pontos neste Compose adicionados de propósito:
- Versões fixas: o exemplo fixa
python:3.12-slim,fastmcp==3.2.4,uvicorn==0.35.0ecaddy:2.10; no mínimo, não uselatest - Dependências instaladas no build: não se executa mais
pip installtemporariamente na inicialização do contêiner, e o conteúdo da imagem é reproduzível - Porta da aplicação não exposta diretamente: apenas o Caddy expõe 80/443 para a internet
- Verificação de liveness separada: o healthcheck passa por
/healthe não executa de fato ferramentas de negócio - Privilégio mínimo:
read_only,cap_drop: [ALL], não root,no-new-privileges - Limites de recursos: para que uma requisição anômala não derrube a máquina inteira
- Proteção de Host / Origin: usando as oficiais
FASTMCP_HTTP_ALLOWED_HOSTS,FASTMCP_HTTP_ALLOWED_ORIGINSeFASTMCP_HTTP_HOST_ORIGIN_PROTECTION - Injeção de credenciais: configurações como
AUTH_JWKS_URIpodem ir por variáveis de ambiente; se o provedor que você usa também precisar de client secret, mude para injeção via secret file, não escreva na imagem nem no Git - Rotação de logs: pelo menos mantenha o tamanho dos logs do contêiner sob controle
Para execuções realmente longas, recomenda-se fixar também o digest da imagem base e passar por um ambiente de pré-produção antes de atualizar.
mcp.example.com {
encode zstd gzip
@health path /health
handle @health {
reverse_proxy mcp-app:8000
}
handle /mcp* {
reverse_proxy mcp-app:8000 {
header_up X-Forwarded-Proto {scheme}
header_up X-Forwarded-Host {host}
transport http {
read_timeout 30s
write_timeout 30s
}
}
}
request_body {
max_size 2MB
}
log {
output stdout
format json
}
}
O ponto aqui não é que "o Caddy seja mais avançado que outro", mas que ele se encaixa bem em algumas coisas:
- HTTPS automático
- Entrada pública unificada
- Manter o contêiner da aplicação apenas na rede interna
- Adicionar timeouts, limites de corpo da requisição e logs de acesso na camada de proxy
Aqui escolhi o Caddy escrevendo em stdout, com a rotação de logs a cargo do driver json-file do Docker. É preciso deixar claro o limite: o Caddy é responsável por produzir logs de acesso estruturados; quem realmente rotaciona conforme 10m * 3 é o runtime do contêiner, não o rotacionador de arquivos do próprio Caddy.
Há também um detalhe fácil de passar despercebido: não misture /mcp e /mcp/. Se algum proxy ou cliente primeiro receber um redirecionamento 301/308 e depois reenviar a requisição, o cabeçalho Authorization pode ser perdido. A abordagem mais confiável é escrever a mesma URL exata em app.py, no Caddy e na configuração do cliente; por exemplo, todos escrevem https://mcp.example.com/mcp, sem depender de redirecionamento para corrigir automaticamente.
Se você ainda não domina essa camada, comece pelo guia de proxy reverso e HTTPS automático com Caddy.
Esta seção é especialmente fácil de errar.
No cabeçalho HTTP Authorization:
Authorization: Bearer your-access-token
Não coloque na query string, não coloque na barra de endereços do navegador e não faça um "token fixo na URL" por conveniência. A especificação diz claramente que o access token em requisições HTTP não deve ir na URI.
No mínimo:
- Se o token foi emitido pelo servidor de autorização correspondente
- Se o audience / resource aponta para o recurso MCP atual
- Se expirou e se o scope é insuficiente
Isso é especialmente importante porque token passthrough é um antipadrão. Ou seja, você não pode repassar para uma API downstream o token trazido pelo cliente sem validar o audience.
Ela protege principalmente contra problemas do tipo DNS rebinding. Para Streamable HTTP, se Origin existir e for inválido, o servidor deve retornar diretamente 403 Forbidden. Isso não é um "ponto extra", mas uma das defesas básicas do MCP remoto.
Não.
A sessão apenas correlaciona um conjunto de interações, para facilitar ao servidor manter estado, retomar fluxos SSE e identificar o ciclo de vida da sessão. Ela não pode substituir a autenticação. A autenticação real continua dependendo do Bearer token em cada requisição HTTP.
A especificação também deixa claro: se o servidor encerrar uma sessão e o cliente depois fizer requisições com esse session ID, o servidor deve retornar 404 Not Found, e o cliente deve reinicializar em vez de tratar a sessão antiga como um passe permanente.
Mais uma observação diretamente relacionada ao formato de implantação: uma VPS de máquina única e processo único pode continuar usando sessões com estado. Mas, se depois você for para vários workers, vários contêineres ou escala horizontal, deve seguir o design stateless_http=True da documentação do FastMCP e tratar desde já estado compartilhado, recuperação de sessão e consistência do armazenamento externo.
A documentação do FastMCP já traz orientações sobre HTTP auth, StaticTokenVerifier, JWTVerifier e diferentes provedores. É preciso ver em dois níveis:
- Nível da especificação MCP: HTTP auth é uma capacidade opcional, mas, assim que você a implementar, faça conforme a especificação de autorização do MCP
- Esta implantação pública do artigo: a autenticação é obrigatória por padrão, então você não pode expor
/mcpcomo uma entrada pública sem autenticação
Se você quer um exemplo que já aparece na documentação oficial e com sintaxe estável, JWTVerifier é mais confiável do que "eu escrever à mão o código de ligação de um provedor OAuth para você". Se você não confirmou completamente a API de provedores da branch estável atual, não copie para produção um trecho OAuth de blog que "parece funcionar".
Uma abordagem mais confiável é fixar primeiro os limites:
- O serviço remoto expõe apenas o transporte HTTP
- A autenticação passa por um provedor padrão de OAuth / JWT
- Os metadados do recurso e a descoberta do servidor de autorização são expostos conforme a especificação
- O Caddy cuida do HTTPS; a aplicação cuida da validação Bearer e da validação de Origin
- A sessão faz papel de sessão, não de auth
Depois que um MCP remoto entra no ar, não basta "conseguir conectar"; todos estes resultados precisam ocorrer:
- Com Bearer token válido, concluir initialize / list-tools / tool call: sucesso
- Sem token: 401
- Origin fora da allowlist: 403
- Sessão expirada ou desconhecida: 404, e então o cliente reinicializa
- Servidor não suporta algum método: 405
- Content-Type incorreto ou tipo de mídia não suportado: 415
Se você também precisa ser compatível com clientes antigos, deixe a política clara:
- Clientes novos: conectar diretamente ao Streamable HTTP
/mcp - Clientes antigos: você decide se oferece também uma entrada de compatibilidade com o antigo HTTP+SSE
- Não desative a autenticação, não desative a validação de Origin e não abra um "endpoint temporário sem autenticação" só para ser compatível com clientes antigos
Ser compatível é uma coisa; manter o mesmo limite de segurança ao ser compatível é outra. O segundo é o que determina se você colocou em produção um serviço de verdade ou apenas levou uma demo para a internet.
O maior erro de conceito sobre MCP remoto é colocar todo o risco na "exposição pública". Na verdade, mesmo com HTTPS e OAuth ativados, os problemas abaixo ainda atingem você:
- Injeção de prompt: o modelo recebe contexto malicioso e é induzido a chamar ferramentas que não deveria
- Data exfiltration / vazamento de dados: as ferramentas retornam campos demais e levam dados internos para fora do limite
- Falha do privilégio mínimo: você só queria ler o status, mas no fim a ferramenta consegue ler o banco inteiro, rodar shell, varrer diretórios
- Vazamento em logs: tokens, parâmetros completos e respostas brutas do downstream acabam nos logs
Recomendo revisar seu serviço com esta lista:
- Cada tool tem privilégio mínimo?
- Existem entradas de alto risco como shell arbitrário, arquivos arbitrários ou URL arbitrária?
- A saída passa por corte de campos?
- As API keys downstream são injetadas por variáveis de ambiente ou secret file?
- O acesso de saída está em allowlist ou pelo menos controlado?
- Os logs passam por mascaramento?
- Antes de atualizar, você olhou a spec, o SDK e as release notes do FastMCP?
- Você faz backup do código-fonte, do Compose, da configuração do Caddy e do estado necessário, em vez de confiar que "o contêiner em si é o backup"?
Acrescento três recomendações operacionais especialmente práticas.
Primeira: os logs precisam permitir diagnóstico, mas não devem completar o contexto para o atacante. Uma abordagem mais confiável é registrar request ID, nome da tool, tempo de execução, status HTTP e se a chamada downstream deu timeout; não grave em disco o Bearer token, a entrada completa do usuário nem a resposta bruta do upstream.
Segunda: antes de atualizar, olhe três coisas: mudanças na especificação MCP, release notes do FastMCP/SDK oficial e a versão do cliente que você está usando. Muitos problemas de "ontem conectava, hoje dá 401 / 415" não são porque o código quebrou de repente, mas porque o transporte, os cabeçalhos ou as convenções de auth mudaram.
Terceira: mantenha os passos de recuperação como um ativo de produção. No mínimo, deixe escrito a troca de domínio, a recuperação do diretório de dados do Caddy, a origem do secret file, a redistribuição da allowlist downstream e os comandos de rollback da imagem antiga. Quando o MCP remoto realmente falha, o mais valioso não é quantos trechos de configuração você tem, mas se você consegue restaurar /mcp em 15 minutos.
Se o seu MCP também se conectar a um gateway de IA auto-hospedado, é melhor desenhar o limite de permissões separadamente; não misture sem cuidado um MCP Server que consegue ler dados com um gateway de IA que consegue trocar o modelo upstream. Para ideias relacionadas, veja o guia de auto-hospedagem de gateway de API de IA.
Também não economize em backups: no mínimo, ensaie o processo de recuperação do código-fonte, da configuração, da gestão de segredos e do estado necessário. Em um incidente real, importa mais se você ensaiou do que "quantos pacotes tar você fez backup"; veja também o guia de simulação de backup e recuperação.
Normalmente:
- Sem Bearer token
- Token expirado
- Token não foi emitido para o recurso MCP atual
- Cadeia de descoberta do servidor de autorização mal configurada
Olhe primeiro WWW-Authenticate e o endereço de metadados do recurso, depois confirme se resource / audience batem.
As duas direções mais comuns:
Origininválido- Scope insuficiente
Se for o segundo caso, a especificação permite que o servidor informe ao cliente em WWW-Authenticate quais scopes faltam. Não conceda todos os scopes de uma vez; escalar aos poucos conforme o privilégio mínimo é mais confiável.
Em MCP remoto, 404 não é necessariamente caminho errado; também pode ser sessão invalidada. Se a requisição trouxer um MCP-Session-Id antigo, o servidor pode retornar 404 diretamente, e o cliente deve reinicializar.
Aparece com frequência quando:
- O cliente envia GET para um endpoint que não suporta fluxos SSE
- Você configurou errado as regras do proxy e algum método foi bloqueado
- O servidor não permite DELETE para encerrar a sessão
Normalmente os cabeçalhos da requisição estão errados, por exemplo um POST JSON-RPC sem o Content-Type ou o Accept corretos conforme exigido. Esses problemas muitas vezes não são erro de lógica do servidor, mas o cliente ou a camada de proxy estragou os cabeçalhos.
Se você vai criar um serviço remoto novo, o FastMCP é muito adequado para uma primeira versão, por um motivo simples: exemplos curtos, material de implantação HTTP completo e limites de ferramentas somente leitura fáceis de controlar.
Se você é uma equipe Node.js, ou precisa controlar com mais detalhe middleware, transporte HTTP e helpers de OAuth, o SDK oficial de TypeScript é mais flexível.
Se você já tem um lote de ferramentas locais stdio e não quer reescrevê-las, então considere soluções de ponte como mcp-proxy. Mas isso é mais uma rota de migração do que o formato padrão recomendado neste artigo para projetos novos.
Outro ponto em que é fácil tropeçar: as branches main dos repositórios do SDK oficial de Python e do SDK oficial de TypeScript estão impulsionando o v2 beta agora. O que realmente é estável em produção continua sendo a linha v1.x.
- SDK de Python: o README de
maindiz explicitamente que o v2 é pré-lançamento e que, em produção, recomenda-se olharv1.x - SDK de TypeScript:
maintambém diz explicitamente v2 beta, e em produção continua-se usandov1.x
Então, se você vai colocar em produção hoje, não copie diretamente os exemplos novos da branch main. Primeiro confirme qual linha estável, qual documentação e qual versão do protocolo você está realmente seguindo.
Se você só usa ferramentas na máquina local, continue com stdio; se realmente quiser colocá-lo em uma VPS para acesso remoto por vários clientes, Streamable HTTP + HTTPS + OAuth/Bearer padrão + validação de Origin + ferramentas de privilégio mínimo é a linha principal mais confiável.
FastMCP, Docker Compose e Caddy são boas escolhas de implementação, mas resolvem "como implantar e como operar", não "o protocolo pode ignorar a segurança". O que realmente determina se este MCP remoto consegue rodar a longo prazo continuam sendo aquelas velhas questões práticas:
- Se os limites das ferramentas são estreitos o suficiente
- Se a validação de audience do token foi feita
- Se a sessão foi confundida com autenticação
- Se logs, backups e atualizações estão institucionalizados
Faça isso bem e o MCP remoto vai se comportar como um serviço de longo prazo, e não como uma demo que por enquanto funciona.
- MCP Transports (2025-11-25)
https://modelcontextprotocol.io/specification/2025-11-25/basic/transports - MCP Authorization (2025-11-25)
https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization - MCP Security Best Practices (2025-11-25)
https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices - Build MCP Server
https://modelcontextprotocol.io/docs/develop/build-server - Connect Remote Servers
https://modelcontextprotocol.io/docs/develop/connect-remote-servers - Official Python SDK Repository
https://github.com/modelcontextprotocol/python-sdk - Official TypeScript SDK Repository
https://github.com/modelcontextprotocol/typescript-sdk - FastMCP HTTP Deployment
https://gofastmcp.com/deployment/http - FastMCP Authentication
https://gofastmcp.com/servers/auth/