Eu Tentei Usar Langchain com Servidores MCP, Aqui Estão os Passos:

@apidog

@apidog

10 abril 2025

Eu Tentei Usar Langchain com Servidores MCP, Aqui Estão os Passos:

O Protocolo de Contexto do Modelo (MCP) é um protocolo de código aberto desenvolvido pela Anthropic que aborda um desafio fundamental nas aplicações de Modelos de Linguagem de Grande Escala (LLM): seu isolamento de fontes e ferramentas de dados externas. Este tutorial abrangente irá guiá-lo na implementação do MCP com o LangChain, fornecendo o conhecimento necessário para criar aplicações sofisticadas que utilizam ambas as tecnologias de forma eficaz.

💡
Ao implementar testes para aplicações baseadas em API, desenvolvedores e testadores estão cada vez mais recorrendo a ferramentas especializadas como Apidog, uma alternativa abrangente ao Postman que simplifica o ciclo de desenvolvimento de APIs. 
botão

Compreendendo o MCP e Seu Propósito

O Protocolo de Contexto do Modelo visa padronizar como aplicações baseadas em LLM se conectam a diversos sistemas externos. Pense no MCP como o "USB-C para IA" - uma interface universal que permite a troca de dados de forma contínua, segura e escalável entre LLMs/agentes de IA e recursos externos.

O MCP emprega uma arquitetura cliente-servidor:

O protocolo facilita uma clara separação de preocupações, permitindo que os desenvolvedores criem conectores modulares e reutilizáveis, enquanto mantêm uma segurança robusta por meio de controles de permissão granular.

Arquitetura Técnica

A arquitetura do MCP consiste em três componentes principais:

  1. Servidor: O servidor MCP expõe ferramentas e fontes de dados por meio de uma API padronizada
  2. Cliente: A aplicação cliente se comunica com o servidor para acessar ferramentas e dados
  3. Adaptador: O LangChain fornece adaptadores que simplificam a integração entre servidores MCP e aplicações LLM

O fluxo de comunicação segue este padrão:

Pré-requisitos

Antes de começarmos, certifique-se de ter o seguinte:

Configurando o Ambiente

Primeiro, vamos criar e configurar nosso ambiente de desenvolvimento:

# Criar um ambiente virtual
python3 -m venv MCP_Demo

# Ativar o ambiente virtual
source MCP_Demo/bin/activate

# Instalar pacotes necessários
pip install langchain-mcp-adapters
pip install langchain-openai

# Defina sua chave da API da OpenAI
export OPENAI_API_KEY=seu_api_key

Criando um Servidor MCP Simples

Começaremos construindo um servidor MCP básico que fornece operações matemáticas. Crie um arquivo chamado math_server.py:

# math_server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Math")

@mcp.tool()
def add(a: int, b: int) -> int:
    """Adiciona dois números"""
    return a + b

@mcp.tool()
def multiply(a: int, b: int) -> int:
    """Multiplica dois números"""
    return a * b

if __name__ == "__main__":
    mcp.run(transport="stdio")

Este servidor expõe duas ferramentas matemáticas: add e multiply. A classe FastMCP simplifica a criação de servidores, lidando automaticamente com os detalhes do protocolo. Cada função decorada com @mcp.tool() se torna disponível para os clientes, com a documentação derivada de docstrings.

Implementando o Cliente LangChain

Em seguida, crie um cliente LangChain para interagir com o servidor MCP. Salve isto como client.py:

# Criar parâmetros do servidor para conexão stdio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from langchain_mcp_adapters.tools import load_mcp_tools
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
import asyncio

model = ChatOpenAI(model="gpt-4o")

# Configurar parâmetros do servidor
server_params = StdioServerParameters(
    command="python",
    # Especifique o caminho para o seu arquivo de servidor
    args=["math_server.py"],
)

async def run_agent():
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            # Inicializar a conexão
            await session.initialize()
            
            # Carregar ferramentas MCP no formato do LangChain
            tools = await load_mcp_tools(session)
            
            # Criar e executar o agente
            agent = create_react_agent(model, tools)
            agent_response = await agent.ainvoke({"messages": "qual é (3 + 5) x 12?"})
            return agent_response

# Executar a função assíncrona
if __name__ == "__main__":
    result = asyncio.run(run_agent())
    print(result)

Este cliente estabelece uma conexão com o servidor MCP, carrega as ferramentas disponíveis e cria um agente LangChain que pode usar essas ferramentas para resolver problemas.

Executando o Exemplo

Para executar este exemplo:

  1. Inicie o servidor MCP em uma aba do terminal:
python3 math_server.py
  1. Em outra aba do terminal, execute o cliente:
python3 client.py

O cliente invocará o agente LangChain, que irá:

  1. Interpretar a pergunta "(3 + 5) x 12"
  2. Chamar a ferramenta add com os argumentos 3 e 5
  3. Obter o resultado 8
  4. Chamar a ferramenta multiply com os argumentos 8 e 12
  5. Retornar a resposta final: 96

Implementação Avançada do Servidor MCP

Vamos expandir nossa implementação para criar um servidor MCP mais sofisticado que fornece acesso a bancos de dados. Este exemplo demonstra como construir conectores para sistemas externos:

# db_server.py
from mcp.server.fastmcp import FastMCP
import sqlite3
from typing import List, Dict, Any

class DatabaseConnector:
    def __init__(self, db_path):
        self.conn = sqlite3.connect(db_path)
        self.cursor = self.conn.cursor()
    
    def execute_query(self, query: str) -> List[Dict[str, Any]]:
        self.cursor.execute(query)
        columns = [desc[0] for desc in self.cursor.description]
        results = []
        for row in self.cursor.fetchall():
            results.append({columns[i]: row[i] for i in range(len(columns))})
        return results

mcp = FastMCP("DatabaseTools")
db_connector = DatabaseConnector("example.db")

@mcp.tool()
def run_sql_query(query: str) -> List[Dict[str, Any]]:
    """Executa uma consulta SQL no banco de dados e retorna os resultados"""
    try:
        return db_connector.execute_query(query)
    except Exception as e:
        return {"error": str(e)}

if __name__ == "__main__":
    mcp.run(transport="stdio")

Integrando Múltiplos Servidores MCP com LangChain

Para aplicações mais complexas, você pode precisar integrar vários servidores MCP. Veja como criar um cliente que se conecta a múltiplos servidores:

# multi_server_client.py
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from langchain_mcp_adapters.tools import load_mcp_tools
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
import asyncio
from typing import List, Dict

# Defina nossas configurações de servidor
servers = [
    {
        "name": "math",
        "params": StdioServerParameters(
            command="python", 
            args=["math_server.py"]
        )
    },
    {
        "name": "database",
        "params": StdioServerParameters(
            command="python", 
            args=["db_server.py"]
        )
    }
]

async def connect_to_server(server_config):
    """Conectar a um único servidor MCP e carregar suas ferramentas"""
    name = server_config["name"]
    params = server_config["params"]
    
    read, write = await stdio_client(params).__aenter__()
    session = ClientSession(read, write)
    await session.__aenter__()
    await session.initialize()
    
    tools = await load_mcp_tools(session)
    return {
        "name": name,
        "session": session,
        "tools": tools,
        "cleanup": lambda: asyncio.gather(
            session.__aexit__(None, None, None),
            stdio_client(params).__aexit__(None, None, None)
        )
    }

async def run_multi_server_agent():
    # Conectar a todos os servidores
    connections = await asyncio.gather(
        *[connect_to_server(server) for server in servers]
    )
    
    try:
        # Coletar todas as ferramentas de todos os servidores
        all_tools = []
        for connection in connections:
            all_tools.extend(connection["tools"])
        
        # Criar o agente com todas as ferramentas
        model = ChatOpenAI(model="gpt-4o")
        agent = create_react_agent(model, all_tools)
        
        # Executar o agente com uma consulta complexa que pode usar múltiplos servidores
        response = await agent.ainvoke({
            "messages": "Encontre os clientes que gastaram mais do que o valor médio do pedido e calcule seu gasto total."
        })
        
        return response
    
    finally:
        # Limpar todas as conexões
        for connection in connections:
            await connection["cleanup"]()

# Executar o agente de múltiplos servidores
if __name__ == "__main__":
    result = asyncio.run(run_multi_server_agent())
    print(result)

Tratamento de Erros e Estratégias de Retorno

Implementações robustas do MCP devem incluir tratamento de erros. Aqui está uma versão aprimorada do cliente que demonstra o tratamento de erros e estratégias de retorno:

# robust_client.py
async def run_agent_with_fallbacks():
    try:
        # Tentar a conexão primária
        async with stdio_client(server_params) as (read, write):
            async with ClientSession(read, write) as session:
                try:
                    await session.initialize()
                    tools = await load_mcp_tools(session)
                    agent = create_react_agent(model, tools)
                    return await agent.ainvoke({"messages": "qual é (3 + 5) x 12?"})
                except Exception as e:
                    print(f"Erro ao usar ferramentas MCP: {e}")
                    # Retorno para chamada direta ao modelo sem ferramentas
                    return await model.ainvoke([
                        HumanMessage(content="qual é (3 + 5) x 12?")
                    ])
    except Exception as connection_error:
        print(f"Erro de conexão: {connection_error}")
        # Retorno final
        return {"error": "Não foi possível estabelecer conexão com o servidor MCP"}

Considerações de Segurança

Ao implementar o MCP com o LangChain, considere as seguintes melhores práticas de segurança:

  1. Validação de Entrada: Sempre valide as entradas para as ferramentas MCP para evitar ataques de injeção
  2. Permissões de Ferramenta: Implemente permissões granulares para cada ferramenta
  3. Limitação de Taxa: Aplique limites de taxa para evitar abusos das ferramentas
  4. Autenticação: Implemente autenticação adequada entre clientes e servidores

Aqui está um exemplo de implementação de permissões de ferramenta:

from mcp.server.fastmcp import FastMCP, Permission

mcp = FastMCP("SecureTools")

# Definir níveis de permissão
READ_PERMISSION = Permission("read", "Pode ler dados")
WRITE_PERMISSION = Permission("write", "Pode modificar dados")

@mcp.tool(permissions=[READ_PERMISSION])
def get_data(key: str) -> str:
    """Obter dados por chave (requer permissão de leitura)"""
    # Implementação...
    return f"Dados para {key}"

@mcp.tool(permissions=[WRITE_PERMISSION])
def update_data(key: str, value: str) -> bool:
    """Atualizar dados (requer permissão de escrita)"""
    # Implementação...
    return True

Otimização de Performance

Para implantações de produção, considere essas otimizações de performance:

  1. Pool de Conexões: Reutilize conexões MCP em vez de criar novas para cada solicitação
  2. Processamento em Lote: Agrupe chamadas a múltiplas ferramentas sempre que possível
  3. Processamento Assíncrono: Use asyncio para lidar com múltiplas solicitações simultaneamente

Exemplo de pool de conexões:

class MCPConnectionPool:
    def __init__(self, max_connections=10):
        self.available_connections = asyncio.Queue(max_connections)
        self.max_connections = max_connections
        self.current_connections = 0
    
    async def initialize(self):
        # Pré-criar algumas conexões
        for _ in range(3):  # Começar com 3 conexões
            await self._create_connection()
    
    async def _create_connection(self):
        if self.current_connections >= self.max_connections:
            raise Exception("Máximo de conexões atingido")
        
        read, write = await stdio_client(server_params).__aenter__()
        session = await ClientSession(read, write).__aenter__()
        await session.initialize()
        
        self.current_connections += 1
        await self.available_connections.put(session)
    
    async def get_connection(self):
        if self.available_connections.empty() and self.current_connections < self.max_connections:
            await self._create_connection()
        
        return await self.available_connections.get()
    
    async def release_connection(self, connection):
        await self.available_connections.put(connection)

Testando Implementações MCP

Testes abrangentes são cruciais para implementações confiáveis do MCP. Aqui está uma abordagem de teste usando pytest:

# test_mcp.py
import pytest
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from langchain_mcp_adapters.tools import load_mcp_tools

@pytest.fixture
async def mcp_session():
    server_params = StdioServerParameters(
        command="python",
        args=["math_server.py"],
    )
    
    read, write = await stdio_client(server_params).__aenter__()
    session = ClientSession(read, write)
    await session.__aenter__()
    await session.initialize()
    
    yield session
    
    await session.__aexit__(None, None, None)
    await stdio_client(server_params).__aexit__(None, None, None)

@pytest.mark.asyncio
async def test_add_tool(mcp_session):
    tools = await load_mcp_tools(mcp_session)
    add_tool = next(tool for tool in tools if tool.name == "add")
    
    result = await add_tool.invoke({"a": 5, "b": 7})
    assert result == 12

@pytest.mark.asyncio
async def test_multiply_tool(mcp_session):
    tools = await load_mcp_tools(mcp_session)
    multiply_tool = next(tool for tool in tools if tool.name == "multiply")
    
    result = await multiply_tool.invoke({"a": 6, "b": 8})
    assert result == 48

Conclusão

O Protocolo de Contexto do Modelo fornece uma estrutura poderosa para conectar aplicações LangChain a ferramentas e fontes de dados externas. Ao padronizar essas conexões, o MCP permite que os desenvolvedores criem agentes de IA sofisticados que podem interagir de forma contínua com seu ambiente.

A combinação das capacidades de agente do LangChain com a conectividade do MCP cria uma base para construir aplicações verdadeiramente poderosas e cientes do contexto. À medida que o ecossistema MCP continua a crescer, podemos esperar que mais servidores e ferramentas pré-construídos surjam, simplificando ainda mais o processo de desenvolvimento.

Este tutorial abordou os conceitos fundamentais e detalhes de implementação do uso do MCP com LangChain, desde a configuração básica até padrões avançados como pool de conexões e tratamento de erros. Seguindo essas práticas, você pode criar aplicações robustas e prontas para produção que aproveitam o melhor de ambas as tecnologias.

Para uma exploração mais aprofundada, considere investigar o crescente ecossistema de servidores MCP disponíveis no GitHub ou contribuir com seus próprios servidores para a comunidade. O futuro dos agentes de IA depende de sua capacidade de alavancar efetivamente ferramentas e dados externos, e o MCP é um passo significativo para tornar essa visão uma realidade.

Pratique o design de API no Apidog

Descubra uma forma mais fácil de construir e usar APIs