mcp-oauth2-proxy

Um servidor MCP stdio local que faz proxy para um servidor MCP HTTP remoto protegido por OAuth2.

Documentação

mcp-oauth2-proxy

npm version node license: MIT

Um servidor MCP stdio local que faz proxy para um servidor MCP HTTP remoto protegido por OAuth2. Integre-o ao Claude Desktop, Cursor, VS Code Copilot ou qualquer outro cliente MCP — faça login uma vez no navegador — pronto.

MCP client ─stdio (JSON-RPC)─▶ mcp-oauth2-proxy ─HTTP+SSE + Bearer─▶ upstream MCP server
                                      │
                                      └─ OAuth2 token endpoint (IdP)

Documentação

Este README é um início rápido. A documentação completa está na wiki.

PáginaO que contém
Primeiros passosInstalação, primeira execução, integração com um cliente.
ConfiguraçãoTodos os campos e variáveis de ambiente.
Concessões e tokens OAuth2Concessões, login interativo, ciclo de vida do token e cache.
DescobertaDescoberta de endpoints RFC 9728 / RFC 8414.
SegurançaModelo de ameaças e defesas integradas.
Hosts remotos (encaminhamento de porta SSH)Como fazer login quando o proxy é executado remotamente.
Solução de problemasProblemas comuns e FAQ.
Arquitetura · Bridge · OAuth2 internalsComo funciona internamente.
Contribuição e lançamentosConfiguração de desenvolvimento, testes, processo de release.

Conteúdo

Recursos

  • Fluxo interativo authorization_code + PKCE com um listener de callback local no navegador — sem copiar/colar códigos manualmente.
  • Cache de refresh token em disco (AES‑256‑GCM, 0600), para o navegador abrir apenas uma vez por máquina.
  • Concessão client_credentials para uso headless / service-to-service.
  • Descoberta RFC 9728 + RFC 8414 dos endpoints de token e autorização a partir do upstream — geralmente sem necessidade de configuração OAuth.
  • Renovação proativa de token com margem de tempo, eliminação de duplicidade em voo e loop 401 → invalidar → tentar novamente.
  • Suporte upstream Streamable-HTTP: respostas JSON de disparo único, SSE text/event-stream, e o canal opcional de notificações do servidor de longa duração. Honra Mcp-Session-Id.
  • Logs apenas em stderr (pino) com redação de tokens, segredos e cabeçalhos Authorization — o stdout permanece um canal JSON-RPC limpo.

Como funciona

  1. Descoberta. Opcionalmente, busca metadados RFC 9728/8414 para preencher tokenUrl, authorizationUrl e scope.
  2. Gerenciador de tokens. Envolve o Grant configurado com cache, margem de renovação, eliminação de duplicidade em voo e invalidação por 401.
  3. Pré-busca. Chama getToken() uma vez na inicialização para que o fluxo interativo do navegador (se necessário) aconteça antes da primeira mensagem MCP.
  4. Bridge. Para cada linha JSON-RPC do stdin, faz POST para upstream.url com um token Bearer; respostas JSON de disparo único tornam-se uma linha no stdout, respostas SSE uma linha por evento. Um 401 dispara invalidate() e uma única tentativa de repetição.
  5. Stream do servidor. Após initialize, opcionalmente mantém aberto um canal GET text/event-stream para notificações iniciadas pelo servidor.

Para os detalhes internos completos, consulte as páginas da wiki Arquitetura, Bridge Internals e OAuth2 Internals.

Requisitos

  • Node.js 20+
  • Um servidor MCP protegido por OAuth2 que fale o transporte MCP HTTP Streamable.
  • Um cliente OAuth2 registrado no seu IdP. Para o fluxo interativo, registre http://127.0.0.1:53682/callback como URI de redirecionamento (ou o que você definir em OAUTH2_CALLBACK_PORT).

Instalação

Não é necessário instalar nada — os clientes MCP podem iniciar o proxy diretamente via npx:

npx -y mcp-oauth2-proxy

Para desenvolvimento com uma cópia local:

git clone https://github.com/ChengleiYuan/mcp-oauth2-proxy.git
cd mcp-oauth2-proxy
npm install
npm run build

Início rápido

Login interativo (recomendado para usuários finais)

UPSTREAM_URL=https://mcp.example.com/mcp \
OAUTH2_GRANT=authorization_code \
OAUTH2_CLIENT_ID=<your-client-id> \
  npx -y mcp-oauth2-proxy

Na primeira execução, o proxy descobre os endpoints OAuth, abre o navegador para um login PKCE, captura o código em http://127.0.0.1:53682/callback, armazena em cache o refresh token (criptografado) no diretório de configuração do SO e inicia o bridge. Execuções posteriores reutilizam o token em cache silenciosamente. Detalhes: Concessões e tokens OAuth2.

Headless / conta de serviço

UPSTREAM_URL=https://mcp.example.com/mcp \
OAUTH2_GRANT=client_credentials \
OAUTH2_TOKEN_URL=https://idp.example.com/oauth2/token \
OAUTH2_CLIENT_ID=my-service \
OAUTH2_CLIENT_SECRET='…' \
OAUTH2_SCOPE='mcp:read mcp:write' \
  npx -y mcp-oauth2-proxy

Integre a um cliente MCP

{
  "mcpServers": {
    "remote-oauth2-mcp": {
      "command": "npx",
      "args": ["-y", "mcp-oauth2-proxy"],
      "env": {
        "UPSTREAM_URL": "https://mcp.example.com/mcp",
        "OAUTH2_GRANT": "authorization_code",
        "OAUTH2_CLIENT_ID": "<your-client-id>"
      }
    }
  }
}

Em hosts Windows que não resolvem shims de .cmd (então command: "npx" falha ao iniciar), use a forma explícita:

{
  "command": "cmd",
  "args": ["/c", "npx", "-y", "mcp-oauth2-proxy"]
}

Caminhos no bloco env devem ser absolutos e usar barras normais em todas as plataformas.

Configuração

O proxy pode ser configurado por um arquivo JSON (MCP_PROXY_CONFIG), por variáveis de ambiente, ou por uma combinação — variáveis de ambiente sobrescrevem valores do arquivo e o resultado mesclado é validado. Exemplo mínimo de arquivo:

{
  "upstream": { "url": "https://mcp.example.com/mcp" },
  "oauth2": {
    "grant": "authorization_code",
    "clientId": "my-client",
    "scope": "mcp:read mcp:write"
  }
}

Veja config.example.json para uma amostra mais completa.

Variáveis de ambiente mais usadas:

Variável de ambienteMapeia para
UPSTREAM_URLupstream.url
OAUTH2_GRANToauth2.grant
OAUTH2_CLIENT_IDoauth2.clientId
OAUTH2_CLIENT_SECREToauth2.clientSecret
OAUTH2_TOKEN_URLoauth2.tokenUrl
OAUTH2_SCOPEoauth2.scope
LOG_LEVELlog.level

Referência completa: todos os campos, padrões e variáveis de ambiente estão documentados na página da wiki Configuração. A descoberta automática de endpoints é abordada em Descoberta. Executando o proxy em uma máquina remota? Veja Hosts remotos (encaminhamento de porta SSH). Enfrentando um problema? Veja Solução de problemas.

Segurança

  • Os tokens são obtidos pelo próprio proxy; o cabeçalho Authorization do upstream é sempre definido pelo proxy, nunca repassado do cliente.
  • Tokens de acesso vivem apenas em memória. Refresh tokens são armazenados em cache criptografados (AES‑256‑GCM, modo de arquivo de chave 0600) — obfuscação honesta contra leituras casuais de disco, não uma proteção contra um processo rodando como o mesmo usuário do SO.
  • http:// em texto claro para hosts não loopback é rejeitado na inicialização (sobrescreva com ALLOW_INSECURE_HTTP=true); https:// e loopback http:// são sempre permitidos. O listener de callback interativo valida o cabeçalho Host para impedir rebinding de DNS.
  • Todos os logs vão para stderr com tokens, segredos e cabeçalhos Authorization redigidos; o stdout é reservado para JSON-RPC.

Modelo de ameaças completo e defesas: Segurança.

Desenvolvimento

npm install
npm run build       # compile TS to dist/
npm run dev         # tsx watch
npm test            # vitest (unit + integration)

O teste de integração inicia um endpoint de token OAuth2 simulado e um upstream MCP simulado no mesmo processo e conduz o bridge real através de streams PassThrough — sem necessidade de rede. A estrutura do projeto, a estratégia de testes completa e o processo de release automatizado estão documentados em Contribuição e lançamentos.

Fora do escopo

  • Múltiplos servidores MCP upstream por processo
  • Armazenamento de refresh token com suporte a chaveiro do SO (DPAPI / Keychain / libsecret)
  • Concessões mTLS / JWT-bearer / device-code / ROPC
  • Transporte de entrada HTTP / SSE (este é um servidor MCP stdio)

Licença

MIT