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.
- 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()
- 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.
- 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.
- 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.
- 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]