Actualizacion para mcp

This commit is contained in:
2025-05-16 02:12:33 +02:00
parent 95c1762ca7
commit 9db2f70009
13 changed files with 597 additions and 247 deletions
+96
View File
@@ -0,0 +1,96 @@
from pathlib import Path
from typing import Any, Optional, Union
from pydantic import AnyUrl
from fastmcp.client import Client
from fastmcp.client.transports import (
StreamableHttpTransport,
PythonStdioTransport,
ClientTransport,
)
from mcp.types import *
from fastmcp.exceptions import ClientError
class MCPClient:
def __init__(self, name: str, client: Client):
self.name = name
self.client = client
def __repr__(self) -> str:
return f"<ClientWrapper(name={self.name})>"
@classmethod
def from_http(cls, name: str, url: str | AnyUrl) -> "MCPClient":
transport = StreamableHttpTransport(url=str(url))
client = Client(transport=transport)
return cls(name=name, client=client)
@classmethod
def from_stdio(
cls,
name: str,
script_path: Union[str, Path],
args: Optional[list[str]] = None,
cwd: Optional[Union[str, Path]] = None,
env: Optional[dict[str, str]] = None,
) -> "MCPClient":
transport = PythonStdioTransport(
script_path=script_path, args=args, cwd=cwd, env=env
)
client = Client(transport=transport)
return cls(name=name, client=client)
def is_connected(self) -> bool:
return self.client.is_connected()
async def __aenter__(self):
await self.client.__aenter__()
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
await self.client.__aexit__(exc_type, exc_val, exc_tb)
# Delegación MCP
async def call_tool(
self, name: str, arguments: dict[str, Any] | None = None
) -> list[TextContent | ImageContent | EmbeddedResource]:
return await self.client.call_tool(name, arguments)
async def get_prompt(
self, name: str, arguments: dict[str, str] | None = None
) -> GetPromptResult:
return await self.client.get_prompt(name, arguments)
async def list_tools(self) -> list[Tool]:
return await self.client.list_tools()
async def list_prompts(self) -> list[Prompt]:
return await self.client.list_prompts()
async def list_resources(self) -> list[Resource]:
return await self.client.list_resources()
async def list_resource_templates(self) -> list[ResourceTemplate]:
return await self.client.list_resource_templates()
async def read_resource(
self, uri: AnyUrl | str
) -> list[TextResourceContents | BlobResourceContents]:
return await self.client.read_resource(uri)
async def complete(
self,
ref: ResourceReference | PromptReference,
argument: dict[str, str],
) -> Completion:
return await self.client.complete(ref, argument)
async def ping(self) -> bool:
return await self.client.ping()
async def set_logging_level(self, level: LoggingLevel) -> None:
return await self.client.set_logging_level(level)
async def send_roots_list_changed(self) -> None:
return await self.client.send_roots_list_changed()
+56
View File
@@ -0,0 +1,56 @@
from src.Llms.MCPs.McpClient import MCPClient
from typing import Any
class ClientRegistry:
def __init__(self):
self._clients: dict[str, MCPClient] = {}
def add(self, name: str, wrapper: MCPClient) -> None:
self._clients[name] = wrapper
def get(self, name: str) -> MCPClient:
if name not in self._clients:
raise KeyError(f"Cliente '{name}' no encontrado en el registro.")
return self._clients[name]
def all(self) -> dict[str, MCPClient]:
return self._clients
def list_names(self) -> list[str]:
return list(self._clients.keys())
def __contains__(self, name: str) -> bool:
return name in self._clients
async def listar_tools_por_cliente(self) -> dict[str, list[Any]]:
resultado = {}
for name, wrapper in self._clients.items():
try:
async with wrapper:
resultado[name] = await wrapper.list_tools()
except Exception as e:
print(f"[TOOLS] ❌ Error en '{name}': {e}")
resultado[name] = []
return resultado
async def listar_prompts_por_cliente(self) -> dict[str, list[Any]]:
resultado = {}
for name, wrapper in self._clients.items():
try:
async with wrapper:
resultado[name] = await wrapper.list_prompts()
except Exception as e:
print(f"[PROMPTS] ❌ Error en '{name}': {e}")
resultado[name] = []
return resultado
async def listar_resources_por_cliente(self) -> dict[str, list[Any]]:
resultado = {}
for name, wrapper in self._clients.items():
try:
async with wrapper:
resultado[name] = await wrapper.list_resources()
except Exception as e:
print(f"[RESOURCES] ❌ Error en '{name}': {e}")
resultado[name] = []
return resultado
@@ -1,15 +0,0 @@
from fastmcp import Client
import asyncio
async def main():
async with Client("http://127.0.0.1:8080/mcp") as client:
tools = await client.list_tools()
for tool in tools:
print(f"🔧 {tool.name} - {tool.description or 'sin descripción'}")
client.call_tool_mcp()
if __name__ == "__main__":
asyncio.run(main())
@@ -1,17 +0,0 @@
# cliente_prueba_http.py
import asyncio
from fastmcp import Client
async def main():
async with Client("http://127.0.0.1:8080/mcp") as client:
tools = await client.list_tools()
for tool in tools:
print(f"🔧 {tool.name} - {tool.description or 'sin descripción'}")
# ✅ llamar a la herramienta correctamente
result = await client.call_tool_mcp(name="esperar", arguments={"segundos": 5})
print(f"\nResultado de 'saludar': {result.content[0].text}")
if __name__ == "__main__":
asyncio.run(main())
+92
View File
@@ -0,0 +1,92 @@
from fastmcp import FastMCP
mcp = FastMCP()
@mcp.tool(description="Suma dos números enteros.")
def add(a: int, b: int) -> int:
return a + b
@mcp.tool(description="Resta dos números enteros.")
def subtract(a: int, b: int) -> int:
return a - b
@mcp.tool(description="Multiplica dos números enteros.")
def multiply(a: int, b: int) -> int:
return a * b
@mcp.tool(description="Divide dos números y devuelve el resultado flotante.")
def divide(a: float, b: float) -> float:
if b == 0:
raise ValueError("No se puede dividir entre cero.")
return a / b
@mcp.tool(description="Calcula el módulo de dos números enteros.")
def modulo(a: int, b: int) -> int:
return a % b
@mcp.tool(description="Concatena dos cadenas de texto.")
def concat(a: str, b: str) -> str:
return a + b
@mcp.tool(description="Devuelve la longitud de una cadena.")
def string_length(s: str) -> int:
return len(s)
@mcp.tool(description="Convierte una cadena a mayúsculas.")
def to_upper(s: str) -> str:
return s.upper()
@mcp.tool(description="Convierte una cadena a minúsculas.")
def to_lower(s: str) -> str:
return s.lower()
@mcp.tool(description="Devuelve la suma de todos los elementos en una lista de enteros.")
def sum_list(numbers: list[int]) -> int:
return sum(numbers)
@mcp.tool(description="Devuelve el valor máximo en una lista de enteros.")
def max_in_list(numbers: list[int]) -> int:
return max(numbers)
@mcp.tool(description="Verifica si un número es par.")
def is_even(n: int) -> bool:
return n % 2 == 0
@mcp.tool(description="Verifica si una cadena es un palíndromo.")
def is_palindrome(s: str) -> bool:
return s == s[::-1]
@mcp.tool(description="Calcula el factorial de un número entero positivo.")
def factorial(n: int) -> int:
if n < 0:
raise ValueError("El factorial no está definido para negativos.")
if n == 0:
return 1
result = 1
for i in range(1, n + 1):
result *= i
return result
@mcp.tool(description="Devuelve los primeros n números de Fibonacci.")
def fibonacci(n: int) -> list[int]:
if n <= 0:
return []
seq = [0, 1]
while len(seq) < n:
seq.append(seq[-1] + seq[-2])
return seq[:n]
@mcp.tool(description="Devuelve si un número es primo.")
def is_prime(n: int) -> bool:
if n <= 1:
return False
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True
if __name__ == "__main__":
# mcp.run(transport="streamable-http", host="127.0.0.1", port=4200, path="/math")
mcp.run(transport="stdio")
-30
View File
@@ -1,30 +0,0 @@
# archivo: sse_server.py
from fastmcp.server import FastMCP
import asyncio
from fastmcp import Client
# Crear la instancia del servidor
server = FastMCP(
name="ServidorSSE",
instructions="Este servidor expone herramientas de prueba.",
)
# Herramienta 1: saludar
@server.tool(name="saludar", description="Saluda a una persona por su nombre.")
def saludar(nombre: str) -> str:
return f"¡Hola, {nombre}!"
# Herramienta 2: espera asíncrona
@server.tool(name="esperar", description="Espera N segundos y responde.")
async def esperar(segundos: int) -> str:
await asyncio.sleep(segundos)
return f"Esperé {segundos} segundos como me pediste."
# Punto de entrada para ejecutarlo por SSE
if __name__ == "__main__":
server.run(
transport="streamable-http", # <-- cambio aquí
host="0.0.0.0",
port=8080,
)
@@ -1,30 +0,0 @@
# archivo: sse_server.py
from fastmcp.server import FastMCP
import asyncio
from fastmcp import Client
# Crear la instancia del servidor
server = FastMCP(
name="ServidorSSE",
instructions="Este servidor expone herramientas de prueba.",
)
# Herramienta 1: saludar
@server.tool(name="saludar", description="Saluda a una persona por su nombre.")
def saludar(nombre: str) -> str:
return f"¡Hola, {nombre}!"
# Herramienta 2: espera asíncrona
@server.tool(name="esperar", description="Espera N segundos y responde.")
async def esperar(segundos: int) -> str:
await asyncio.sleep(segundos)
return f"Esperé {segundos} segundos como me pediste."
# Punto de entrada para ejecutarlo por SSE
if __name__ == "__main__":
server.run(
transport="streamable-http", # <-- cambio aquí
host="0.0.0.0",
port=8080,
)
+68
View File
@@ -0,0 +1,68 @@
from fastmcp import FastMCP
import uuid
import datetime
import socket
import platform
import os
mcp = FastMCP()
@mcp.tool(description="Genera un UUID versión 4.")
def generate_uuid() -> str:
return str(uuid.uuid4())
@mcp.tool(description="Devuelve la fecha y hora actuales en formato ISO 8601.")
def current_datetime() -> str:
return datetime.datetime.now().isoformat()
@mcp.tool(description="Devuelve solo la fecha actual.")
def current_date() -> str:
return datetime.date.today().isoformat()
@mcp.tool(description="Devuelve el nombre del host actual.")
def get_hostname() -> str:
return socket.gethostname()
@mcp.tool(description="Devuelve el sistema operativo actual.")
def get_os() -> str:
return platform.system()
@mcp.tool(description="Devuelve el nombre del usuario actual del sistema.")
def get_current_user() -> str:
return os.getlogin()
@mcp.tool(description="Invierte un valor booleano.")
def invert_boolean(flag: bool) -> bool:
return not flag
# @mcp.tool(description="Devuelve los archivos y carpetas del directorio actual.")
# def list_current_directory() -> list[str]:
# return os.listdir()
# @mcp.tool(description="Crea un archivo con un nombre dado.")
# def create_file(filename: str) -> str:
# with open(filename, "w") as f:
# f.write("")
# return f"Archivo '{filename}' creado."
# @mcp.tool(description="Lee el contenido de un archivo de texto dado.")
# def read_file(filename: str) -> str:
# with open(filename, "r") as f:
# return f.read()
# @mcp.tool(description="Escribe contenido a un archivo, sobrescribiéndolo.")
# def write_file(filename: str, content: str) -> str:
# with open(filename, "w") as f:
# f.write(content)
# return f"Contenido escrito en '{filename}'."
@mcp.tool(description="Devuelve el número de CPUs disponibles en el sistema.")
def get_cpu_count() -> int:
return os.cpu_count()
@mcp.tool(description="Devuelve el timestamp actual (UNIX).")
def current_timestamp() -> float:
return datetime.datetime.now().timestamp()
if __name__ == "__main__":
mcp.run(transport="streamable-http", host="127.0.0.1", port=4300, path="/tools")