Apidog

Plataforma Colaborativa All-in-one para Desenvolvimento de API

Design de API

Documentação de API

Depuração de API

Mock de API

Testes Automatizados

Como Implantar Servidores MCP na AWS Lambda

@apidog

@apidog

Updated on abril 25, 2025

O Protocolo de Contexto do Modelo (MCP) está rapidamente se tornou um padrão para capacitar Modelos de Linguagem de Grande Escala (LLMs) como Claude, ChatGPT e outros a interagir com o mundo exterior. Definindo ferramentas de maneira estruturada, o MCP permite que os LLMs solicitem ações, obtenham dados em tempo real ou interajam com APIs externas, superando seus dados de treinamento estáticos.

No entanto, a implantação desses "servidores" MCP (que fornecem as ferramentas) frequentemente apresenta um desafio, especialmente em ambientes modernos de nuvem. Muitas implementações iniciais do MCP foram projetadas para execução local, comunicando-se por meio de entrada/saída padrão (stdio) ou usando protocolos como Server-Sent Events (SSE) para streaming. Embora funcionais, essas abordagens frequentemente dependem de conexões persistentes e comportamentos com estado, tornando-as inadequadas para plataformas escaláveis, sem estado, orientadas a eventos como o AWS Lambda.

O AWS Lambda oferece enormes benefícios: escalonamento automático, eficiência de custo sob demanda, e zero sobrecarga de gerenciamento de servidor. Como podemos superar essa lacuna e executar servidores MCP robustos e prontos para produção neste ambiente sem servidor?

Apresentando MCPEngine, uma implementação Python de código aberto do MCP, especialmente projetada para abordar esses desafios. O MCPEngine suporta HTTP transmitível juntamente com SSE, tornando-o totalmente compatível com o modelo de solicitação/resposta do AWS Lambda. Também agrupa recursos essenciais para implantações de produção, incluindo suporte de autenticação embutido e empacotamento simplificado.

Este artigo explora como aproveitar o MCPEngine para construir e implantar servidores MCP no AWS Lambda, cobrindo ferramentas sem estado, gerenciamento de estado e autenticação.

💡
Quer acabar com as alucinações no Cursor?

Apidog-MCP-Server permite que o Cursor leia diretamente a documentação da API, que pode ser documentação publicada online ou até mesmo arquivos locais OpenAPI.

Ao fazer do seu design de API a fonte de verdade para a IA, o Servidor MCP da Apidog facilita tarefas como geração de código com base em esquemas, busca inteligente por endpoints e garantindo que as modificações de código estejam alinhadas perfeitamente com o contrato da API, agilizando assim o fluxo de trabalho de desenvolvimento.

Conceitos Básicos: MCPEngine e Lambda

Antes de mergulhar na implantação, vamos entender os principais componentes do MCPEngine para a integração com o Lambda:

  1. MCPEngine: A classe central que orquestra suas ferramentas e gerencia a comunicação do MCP.
  2. @engine.tool() Decorador: Registra uma função Python como uma ferramenta MCP. O nome da função se torna o nome da ferramenta, e sua docstring serve como a descrição fornecida ao LLM.
  3. engine.get_lambda_handler(): Este método gera uma função de manipulador compatível com o AWS Lambda. Você expõe este manipulador, e o MCPEngine cuida de traduzir a carga útil do evento Lambda em solicitações MCP e formatar as respostas.

Construindo uma Ferramenta Simples sem Estado

Vamos começar com o básico: uma ferramenta sem estado implantada no Lambda. Este exemplo fornece uma ferramenta simples de saudação.

Pré-requisitos:

  • Python 3.8+
  • Uma conta AWS com permissões para gerenciar Lambda, ECR e IAM.
  • Docker instalado localmente.
  • AWS CLI configurado.

1. Instale o MCPEngine:

pip install mcpengine[cli,lambda]

2. Crie o Aplicativo (app.py):

# app.py
from mcpengine import MCPEngine, Context

# Inicialize o motor
engine = MCPEngine()

@engine.tool()
def personalized_greeting(name: str) -> str:
    """
    Gera uma saudação amigável para o nome especificado.
    Use esta ferramenta quando solicitado a cumprimentar alguém.
    """
    # Lógica simples sem estado
    return f"Olá, {name}! Bem-vindo ao mundo MCP sem servidor."

# Obtenha a função manipuladora do Lambda
handler = engine.get_lambda_handler()

Este código define uma única ferramenta, personalized_greeting, que recebe um name e retorna uma string. A variável handler é o que o AWS Lambda invocará.

Fluxo de Trabalho de Implantação: Código para a Nuvem

Implantar um aplicativo MCPEngine no Lambda envolve containerizá-lo com Docker, empurrá-lo para o Amazon Elastic Container Registry (ECR) e configurar a função Lambda.

1. Dockerize o Aplicativo (Dockerfile):

# Use a imagem base oficial do AWS Lambda Python
FROM public.ecr.aws/lambda/python:3.12

# Defina o diretório de trabalho no contêiner
WORKDIR ${LAMBDA_TASK_ROOT}

# Copie os requisitos primeiro para aproveitar o cache do Docker
COPY requirements.txt .
# Instale as dependências (assumindo que mcpengine esteja listado em requirements.txt)
# Ou instale diretamente: RUN pip install --no-cache-dir mcpengine[cli,lambda]
RUN pip install --no-cache-dir -r requirements.txt

# Copie o restante do código do aplicativo
COPY app.py .

# Defina o comando para executar a função manipuladora (app.handler significa handler em app.py)
CMD ["app.handler"]

(Certifique-se de ter um arquivo requirements.txt listando mcpengine[cli,lambda] ou modifique o comando RUN de acordo).

2. Construa e Envie a Imagem Docker para o ECR:

Primeiro, crie um repositório ECR (substitua <region> e <repo-name>):

aws ecr create-repository --repository-name <repo-name> --region <region>

Observe o ID da sua Conta AWS e o URI do repositório na saída (<account-id>.dkr.ecr.<region>.amazonaws.com/<repo-name>).

Agora, construa, marque e envie a imagem:

# Autentique o Docker com ECR
aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <account-id>.dkr.ecr.<region>.amazonaws.com

# Construa a imagem (use --platform para builds de arquitetura cruzada, se necessário)
docker build --platform=linux/amd64 -t <repo-name>:latest .

# Marque a imagem para o ECR
docker tag <repo-name>:latest <account-id>.dkr.ecr.<region>.amazonaws.com/<repo-name>:latest

# Envie a imagem para o ECR
docker push <account-id>.dkr.ecr.<region>.amazonaws.com/<repo-name>:latest

3. Crie e Configure a Função Lambda:

Primeiro, você precisará de uma função de execução IAM para o Lambda. Se você não tiver uma, crie uma básica:

# (Simplificado - ajuste a política de confiança e permissões conforme necessário)
aws iam create-role --role-name lambda-mcp-role --assume-role-policy-document '{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Principal": {"Service": "lambda.amazonaws.com"},"Action": "sts:AssumeRole"}]}'
aws iam attach-role-policy --role-name lambda-mcp-role --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

Agora, crie a função Lambda usando a imagem ECR (substitua os espaços reservados):

aws lambda create-function \
  --function-name mcp-greeter-function \
  --package-type Image \
  --code ImageUri=<account-id>.dkr.ecr.<region>.amazonaws.com/<repo-name>:latest \
  --role arn:aws:iam::<account-id>:role/lambda-mcp-role \
  --timeout 30 \
  --memory-size 512 \
  --region <region>

4. Exponha via URL da Função:

Para tornar a Lambda chamável via HTTP sem o API Gateway, crie uma URL da Função:

aws lambda create-function-url-config \
  --function-name mcp-greeter-function \
  --auth-type NONE \
  --region <region>

# Adicione permissão para acesso público (ajuste se a autenticação for necessária)
aws lambda add-permission \
  --function-name mcp-greeter-function \
  --statement-id FunctionURLAllowPublicAccess \
  --action lambda:InvokeFunctionUrl \
  --principal '*' \
  --function-url-auth-type NONE \
  --region <region>

Observe a URL da Função retornada pelo comando create-function-url-config. Seu servidor MCP sem estado agora está ativo!

Gerenciando Estado com o Contexto lifespan

Lambda é sem estado, mas muitas ferramentas precisam acessar bancos de dados, pools de conexão ou outros recursos inicializados na inicialização. O MCPEngine aborda isso com o argumento lifespan, que aceita um gerenciador de contexto assíncrono.

A função lifespan executa seu código de configuração (antes de yield) quando o contêiner Lambda inicia e seu código de limpeza (após yield) quando o contêiner é encerrado. O valor gerado se torna disponível em suas funções de ferramenta via o objeto ctx (Contexto).

Vamos construir um simples registrador de eventos que armazena eventos em um banco de dados RDS Postgres.

1. Modifique app.py:

# app.py (Exemplo com Estado)
import os
import psycopg2
from contextlib import asynccontextmanager
from mcpengine import MCPEngine, Context

# Assume que os detalhes da conexão do DB estão em variáveis de ambiente
DB_HOST = os.environ.get("DB_HOST")
DB_USER = os.environ.get("DB_USER")
DB_PASS = os.environ.get("DB_PASS")
DB_NAME = os.environ.get("DB_NAME")

@asynccontextmanager
async def db_connection_manager():
    """Gerencia o pool de conexão do banco de dados."""
    conn = None
    try:
        print("Estabelecendo conexão com o DB...")
        conn = psycopg2.connect(
            host=DB_HOST,
            user=DB_USER,
            password=DB_PASS,
            dbname=DB_NAME
        )
        # Cria a tabela se não existir (exemplo simples)
        with conn.cursor() as cur:
             cur.execute("""
                CREATE TABLE IF NOT EXISTS events (
                    id SERIAL PRIMARY KEY,
                    event_name TEXT NOT NULL,
                    timestamp TIMESTAMP DEFAULT now()
                );
             """)
        conn.commit()
        print("Conexão com o DB pronta.")
        yield {"db_conn": conn} # Torne a conexão disponível via ctx.db_conn
    finally:
        if conn:
            print("Fechando conexão com o DB.")
            conn.close()

# Inicialize o motor com o gerenciador de lifespan
engine = MCPEngine(lifespan=db_connection_manager)

@engine.tool()
def log_event(event_name: str, ctx: Context) -> str:
    """Registra um evento com o nome fornecido no banco de dados."""
    try:
        with ctx.db_conn.cursor() as cur:
            cur.execute("INSERT INTO events (event_name) VALUES (%s)", (event_name,))
        ctx.db_conn.commit()
        return f"Evento '{event_name}' registrado com sucesso."
    except Exception as e:
        # Tratamento básico de erros
        ctx.db_conn.rollback()
        return f"Erro ao registrar evento: {e}"

@engine.tool()
def get_latest_events(limit: int = 5, ctx: Context) -> list[str]:
    """Recupera os últimos eventos registrados no banco de dados."""
    try:
        with ctx.db_conn.cursor() as cur:
            cur.execute("SELECT event_name, timestamp FROM events ORDER BY timestamp DESC LIMIT %s", (limit,))
            events = [f"[{row[1].strftime('%Y-%m-%d %H:%M:%S')}] {row[0]}" for row in cur.fetchall()]
            return events
    except Exception as e:
        return [f"Erro ao recuperar eventos: {e}"]

# Obtenha o manipulador do Lambda
handler = engine.get_lambda_handler()

2. Considerações de Implantação:

  • Banco de Dados: Você precisa de uma instância RDS acessível (ou outro banco de dados).
  • Rede: Configure as configurações de VPC da função Lambda para permitir acesso à instância RDS (Grupos de Segurança, Sub-redes).
  • Variáveis de Ambiente: Passe DB_HOST, DB_USER, DB_PASS, DB_NAME como variáveis de ambiente para a função Lambda.
  • IAM: O papel de execução do Lambda pode precisar de permissões adicionais se acessar outros serviços AWS (por exemplo, Secrets Manager para credenciais do DB).

Atualize o Dockerfile, se necessário (por exemplo, para instalar psycopg2-binary), reconstruir/enviar a imagem e atualizar o código e a configuração da função Lambda (variáveis de ambiente, configurações de VPC).

Protegendo Ferramentas com Autenticação

Ferramentas de produção precisam de autenticação. O MCPEngine se integra com provedores OpenID Connect (OIDC) como Google, AWS Cognito, Auth0, etc.

1. Configure o Provedor OIDC:
Configure um ID de cliente OAuth com seu provedor escolhido (por exemplo, Google Cloud Console). Você precisará do Client ID e, potencialmente, do Client Secret (dependendo do fluxo).

2. Atualize app.py para Autenticação:

# app.py (Exemplo Autenticado - Trechos)
import os
# ... outras importações ...
from mcpengine import MCPEngine, Context, GoogleIdpConfig # Ou outro IdpConfig

# ... db_connection_manager ...

# Configure o IDP - usando o Google como exemplo
# Assume que GOOGLE_CLIENT_ID está definido como uma variável de ambiente
google_config = GoogleIdpConfig(
   client_id=os.environ.get("GOOGLE_CLIENT_ID")
   # o emissor pode frequentemente ser inferido, ou definido explicitamente
)

# Inicialize o motor com lifespan e configuração do IDP
engine = MCPEngine(
    lifespan=db_connection_manager,
    idp_config=google_config
)

# Proteja a ferramenta log_event
@engine.auth() # Adicione este decorador
@engine.tool()
def log_event(event_name: str, ctx: Context) -> str:
    """Registra um evento com o nome dado no banco de dados. Requer autenticação."""
    # Acesse as informações do usuário autenticado, se necessário: user_email = ctx.user.email
    user_email = ctx.user.email if ctx.user else "desconhecido"
    print(f"Usuário autenticado: {user_email}")
    try:
        # ... (a lógica do banco de dados continua a mesma) ...
         return f"Evento '{event_name}' registrado com sucesso por {user_email}."
    except Exception as e:
        # ... tratamento de erros ...
        return f"Erro ao registrar evento para {user_email}: {e}"

# get_latest_events pode permanecer não autenticado ou ser protegido também
@engine.tool()
def get_latest_events(limit: int = 5, ctx: Context) -> list[str]:
   # ... (a lógica permanece a mesma) ...

# Obtenha o manipulador do Lambda
handler = engine.get_lambda_handler()

Principais Alterações:

  • Importou GoogleIdpConfig (ou o apropriado para seu provedor).
  • Instanciou MCPEngine com o argumento idp_config.
  • Adicionou o decorador @engine.auth() acima de @engine.tool() para a(s) função(ões) que requerem autenticação. O MCPEngine automaticamente rejeitará solicitações sem um token JWT válido verificado contra as chaves públicas do IDP.
  • As informações do usuário autenticado (dos pedidos JWT) estão disponíveis via ctx.user.

3. Implantação:

  • Passe as variáveis de ambiente necessárias para autenticação (por exemplo, GOOGLE_CLIENT_ID) para sua função Lambda.
  • Reconstrua/envie a imagem e atualize a função Lambda.

Conectando um Cliente LLM

Uma vez que seu servidor MCP esteja implantado no Lambda com uma URL da Função, você pode conectar clientes compatíveis. Usar mcpengine proxy é uma maneira conveniente de conectar clientes como Claude:

mcpengine proxy <nome-do-serviço-escolhido> <url-da-função-lambda> --mode http --claude

Se usar autenticação:

mcpengine proxy <nome-do-serviço-escolhido> <url-da-função-lambda> \
  --mode http \
  --claude \
  --client-id <seu-client-id-google> \
  --client-secret <seu-client-secret-google> # Necessário para o fluxo de aquisição de token

Este comando executa um proxy local ao qual Claude se conecta. O proxy então encaminha as solicitações via HTTP para a sua URL da Função Lambda, lidando com o fluxo de autenticação, se configurado. O LLM agora pode descobrir e invocar suas ferramentas sem servidor.

Conclusão

Implantar servidores MCP no AWS Lambda desbloqueia uma escalabilidade incrível e eficiência operacional para estender as capacidades dos LLMs. Implementações tradicionais do MCP frequentemente lutam em ambientes sem estado, mas o MCPEngine fornece uma solução robusta e de código aberto. Ao suportar HTTP transmitível, oferecer gerenciamento de contexto via lifespan, e integrar-se perfeitamente com OIDC para autenticação, o MCPEngine torna o MCP sem servidor não apenas possível, mas prático para casos de uso em produção. Quer você esteja construindo ferramentas simples sem estado ou aplicativos complexos, com estado e autenticados, o MCPEngine combinado com o AWS Lambda oferece uma plataforma poderosa para a próxima geração de interações impulsionadas por IA.

💡
Quer uma ótima ferramenta de Teste de API que gera documentação de API bonita?

Quer uma plataforma integrada, Tudo-em-Um, para sua Equipe de Desenvolvedores trabalharem juntos com máxima produtividade?

Apidog atende todas as suas demandas, e substitui o Postman a um preço muito mais acessível!
button