Integração de LLMs com Ferramentas MCP via Stdio

Demonstrações anteriores focaram na criação manual de clientes e na invocação de ferramentas MCP sem o uso de modelos de linguagem grandes (LLMs). Este artigo explora como utilizar LLMs para acionar ferramentas MCP.

A comunicação com o MCP Server pode ser realizada de duas formas: via stdio ou via HTTP Streamable. A abordagem apresentada a seguir utiliza o modo stdio.

  1. Configuração do Servidor MCP para Modo Stdio

Modifique o arquivo server.py para operar em modo stdio. Substitua a linha:

mcp.run(transport="http", port=3002, host="0.0.0.0", path="/mcp_atlas")

por:

mcp.run()
  1. Criação do Cliente Agente Stdio

Adicione um novo arquivo, agent_stdio.py, no diretório test com o seguinte código:

import asyncio
import os
import json
from openai import OpenAI
from dotenv import load_dotenv
from contextlib import AsyncExitStack

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

# Carrega variáveis de ambiente do arquivo .env
load_dotenv()

class MlpAgentClient:
    def __init__(self):
        """Inicializa o cliente do agente MCP."""
        self.exit_stack = AsyncExitStack()
        self.api_key = os.getenv("API_KEY")
        self.base_url = os.getenv("BASE_URL")
        self.model_name = os.getenv("MODEL")

        if not self.api_key:
            raise ValueError("Chave API não encontrada. Configure API_KEY no arquivo .env")

        self.openai_client = OpenAI(api_key=self.api_key, base_url=self.base_url)

    async def process_user_request(self, user_query: str) -> str:
        """
        Processa a consulta do usuário usando o LLM e aciona ferramentas conforme necessário.
        """
        conversation_history = [{"role": "user", "content": user_query}]

        # Obtém a lista de ferramentas disponíveis no servidor
        server_tools_response = await self.session.list_tools()
        available_tools_config = [{
            "type": "function",
            "function": {
                "name": tool.name,
                "description": tool.description,
                "input_schema": tool.inputSchema
            }
        } for tool in server_tools_response.tools]
        print('Ferramentas disponíveis no servidor:', available_tools_config)

        # Envia a consulta para o LLM
        llm_response = self.openai_client.chat.completions.create(
            model=self.model_name,
            messages=conversation_history,
            tools=available_tools_config
        )

        # Analisa a resposta do LLM
        first_choice = llm_response.choices[0]
        if first_choice.finish_reason == "tool_calls":
            tool_call_info = first_choice.message.tool_calls[0]
            tool_name_to_call = tool_call_info.function.name
            tool_arguments = json.loads(tool_call_info.function.arguments)

            print(f"\n\n[Executando ferramenta '{tool_name_to_call}' com argumentos {tool_arguments}]\n\n")
            # Executa a ferramenta
            tool_execution_result = await self.session.call_tool(tool_name_to_call, tool_arguments)
            tool_result_content = tool_execution_result.content[0].text

            # Adiciona a chamada da ferramenta e o resultado ao histórico da conversa
            conversation_history.append(first_choice.message.model_dump())
            conversation_history.append({
                "role": "tool",
                "content": tool_result_content,
                "tool_call_id": tool_call_info.id,
            })

            # Reenvia a conversa atualizada para o LLM para obter a resposta final
            final_llm_response = self.openai_client.chat.completions.create(
                model=self.model_name,
                messages=conversation_history,
            )
            return final_llm_response.choices[0].message.content

        return first_choice.message.content

    async def start_interactive_chat(self):
        """Inicia o loop de chat interativo."""
        print("Cliente MCP Agent iniciado! Digite 'exit' para sair.")

        while True:
            try:
                user_input = input("Você: ").strip()
                if user_input.lower() == 'exit':
                    break

                agent_response = await self.process_user_request(user_input)
                print(f"Agent: {agent_response}")

            except Exception as e:
                print(f"Ocorreu um erro: {str(e)}")

    async def close_resources(self):
        """Fecha todos os recursos assíncronos."""
        await self.exit_stack.aclose()

    async def establish_server_connection(self, server_script_path: str):
        """
        Conecta-se ao servidor MCP via stdio.
        """
        if not (server_script_path.endswith('.py') or server_script_path.endswith('.js')):
            raise ValueError("Tipo de script não suportado. Use .py ou .js")

        executor_command = "python" if server_script_path.endswith('.py') else "node"
        server_process_params = StdioServerParameters(command=executor_command,
                                                      args=[server_script_path],
                                                      env=None)

        # Inicia o servidor stdio e estabelece a comunicação
        stdio_transport = await self.exit_stack.enter_async_context(
            stdio_client(server_process_params))
        self.stdio_stream, self.write_stream = stdio_transport
        self.session = await self.exit_stack.enter_async_context(
            ClientSession(self.stdio_stream, self.write_stream))

        await self.session.initialize()

    async def discover_available_tools(self):
        """Lista as ferramentas disponíveis no servidor MCP."""
        response = await self.session.list_tools()
        tool_names = [tool.name for tool in response.tools]
        print("Conectado ao servidor. Ferramentas disponíveis:", tool_names)

async def main_execution():
    """Ponto de entrada principal para a execução do cliente."""
    agent_client = MlpAgentClient()
    try:
        # Estabelece a conexão com o servidor MCP
        await agent_client.establish_server_connection('../server.py')
        # Lista as ferramentas disponíveis
        await agent_client.discover_available_tools()
        # Inicia o loop de chat interativo
        await agent_client.start_interactive_chat()
    finally:
        # Garante que os recursos sejam liberados
        await agent_client.close_resources()

if __name__ == "__main__":
    asyncio.run(main_execution())

Nota: Ajuste o caminho para o arquivo server.py se ele estiver em um local diferente.

  1. Refinamento das Definições de Ferramentas

No arquivo math_tools.py, adicione descrições detalhadas para os parâmetros das funções. Isso melhora a capacidade do LLM de utilizar as ferramentas corretamente.

# -*- coding: utf-8 -*-
import random
from base.tool_registry import mcp_tool

@mcp_tool()
def add_numbers(a: int, b: int) -> int:
    """
    Soma dois números inteiros.

    Args:
        a (int): O primeiro número a ser somado.
        b (int): O segundo número a ser somado.

    Returns:
        int: A soma dos dois números fornecidos.
    """
    return a + b

A ausência de descrições claras nos parâmetros pode levar o LLM a omitir argumentos ao chamar as ferrramentas.

  1. Configuração do Arquivo .env

Crie um arquivo chamado .env na raiz do seu projeto com as seguintes informações:

API_KEY=sua_chave_api_aqui
BASE_URL=seu_base_url_aqui
MODEL=nome_do_seu_modelo_llm

Substitua os placeholders pelos seus valores de configuração.

  1. Demonstração de Execução

Execute o agente usando o seguinte comando (certifique-se de estar em um ambiente virtual ativado com Python 3.10+):

python agent_stdio.py

Após a execução, insira uma consulta como 3+2= para observar a interação e a resposta gerada pelo agente.

Código Fonte: [Link para o repositório ou arquivo, se aplicável]

Tags: LLM MCP stdio Integração de Ferramentas OpenAI API

Publicado em 7-18 03:06