Herramientas MCP
Las herramientas permiten que los LLMs ejecuten acciones a través de servidores MCP. Aprende cómo crear herramientas efectivas y seguras.
¿Qué son las Herramientas?
Las herramientas son funciones que el LLM puede ejecutar a través del servidor MCP. A diferencia de los recursos (que son de solo lectura), las herramientas permiten realizar acciones: modificar datos, ejecutar operaciones, interactuar con sistemas externos, etc.
Listar
El cliente puede solicitar una lista de todas las herramientas disponibles.
Ejecutar
El LLM puede llamar a una herramienta con parámetros específicos.
Resultado
La herramienta ejecuta la acción y retorna un resultado estructurado.
Cómo Crear Herramientas en MCP
Para crear herramientas en tu servidor MCP, necesitas implementar dos métodos: list_tools para listar las herramientas disponibles, y call_tool para ejecutarlas.
Ejemplo: Python SDK
from mcp.server import Server
from mcp.types import Tool, TextContent
import mcp.types as types
import os
app = Server("file-tools-server")
@app.list_tools()
async def list_tools() -> list[Tool]:
"""Lista todas las herramientas disponibles"""
return [
Tool(
name="create_file",
description="Crea un nuevo archivo con el contenido especificado",
inputSchema={
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "Ruta del archivo a crear"
},
"content": {
"type": "string",
"description": "Contenido del archivo"
}
},
"required": ["path", "content"]
}
),
Tool(
name="read_file",
description="Lee el contenido de un archivo",
inputSchema={
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "Ruta del archivo a leer"
}
},
"required": ["path"]
}
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
"""Ejecuta una herramienta específica"""
if name == "create_file":
path = arguments.get("path")
content = arguments.get("content")
# Validar entrada
if not path or not content:
raise ValueError("path y content son requeridos")
# Crear archivo
with open(path, "w", encoding="utf-8") as f:
f.write(content)
return [TextContent(
type="text",
text=f"Archivo creado exitosamente: {path}"
)]
elif name == "read_file":
path = arguments.get("path")
if not path:
raise ValueError("path es requerido")
if not os.path.exists(path):
raise FileNotFoundError(f"Archivo no encontrado: {path}")
with open(path, "r", encoding="utf-8") as f:
content = f.read()
return [TextContent(
type="text",
text=content
)]
else:
raise ValueError(f"Herramienta desconocida: {name}")
# Ejecutar servidor
if __name__ == "__main__":
from mcp.server.stdio import stdio_server
stdio_server(app)Ejemplo: TypeScript SDK
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { writeFileSync, readFileSync, existsSync } from "fs";
const server = new Server(
{
name: "file-tools-server",
version: "0.1.0",
},
{
capabilities: {
tools: {},
},
}
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "create_file",
description: "Crea un nuevo archivo con el contenido especificado",
inputSchema: {
type: "object",
properties: {
path: {
type: "string",
description: "Ruta del archivo a crear",
},
content: {
type: "string",
description: "Contenido del archivo",
},
},
required: ["path", "content"],
},
},
{
name: "read_file",
description: "Lee el contenido de un archivo",
inputSchema: {
type: "object",
properties: {
path: {
type: "string",
description: "Ruta del archivo a leer",
},
},
required: ["path"],
},
},
],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "create_file") {
const path = args?.path as string;
const content = args?.content as string;
if (!path || !content) {
throw new Error("path y content son requeridos");
}
writeFileSync(path, content, "utf-8");
return {
content: [
{
type: "text",
text: `Archivo creado exitosamente: ${path}`,
},
],
};
}
if (name === "read_file") {
const path = args?.path as string;
if (!path) {
throw new Error("path es requerido");
}
if (!existsSync(path)) {
throw new Error(`Archivo no encontrado: ${path}`);
}
const content = readFileSync(path, "utf-8");
return {
content: [
{
type: "text",
text: content,
},
],
};
}
throw new Error(`Herramienta desconocida: ${name}`);
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main();Tipos de Herramientas
Las herramientas pueden realizar diferentes tipos de operaciones. Cada tipo tiene sus propias características y casos de uso.
Manipulación de Archivos
Crear, leer, modificar y eliminar archivos. Útil para gestión de documentos y código.
Ejemplos:
create_file, read_file, delete_file, write_file
Caso de uso:
Gestión de documentación, edición de código, organización de archivos.
Consultas de Base de Datos
Ejecutar consultas SQL, insertar datos, actualizar registros. Permite interacción con bases de datos.
Ejemplos:
query_database, insert_record, update_user
Caso de uso:
Análisis de datos, gestión de usuarios, reportes dinámicos.
Comunicación Externa
Enviar emails, mensajes, notificaciones. Conectar con servicios de comunicación.
Ejemplos:
send_email, post_message, send_notification
Caso de uso:
Notificaciones automáticas, envío de reportes, comunicación con usuarios.
Búsqueda Web
Buscar información en internet, obtener datos de APIs, realizar scraping.
Ejemplos:
web_search, fetch_url, get_weather
Caso de uso:
Investigación, obtención de datos en tiempo real, integración con servicios.
Procesamiento de Datos
Transformar, validar, calcular y procesar información. Operaciones sobre datos.
Ejemplos:
transform_json, validate_data, calculate_metrics
Caso de uso:
ETL, validación de datos, cálculos complejos, transformaciones.
Operaciones del Sistema
Ejecutar comandos, gestionar procesos, interactuar con el sistema operativo.
Ejemplos:
run_command, list_processes, system_info
Caso de uso:
Automatización, gestión de servidores, operaciones de sistema.
Ejemplos Prácticos
Aquí tienes ejemplos completos de diferentes tipos de herramientas.
Ejemplo 1: Herramienta de Base de Datos
Herramienta para ejecutar consultas SQL en una base de datos
from mcp.server import Server
from mcp.types import Tool, TextContent
import sqlite3
import json
app = Server("database-tools")
@app.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="query_database",
description="Ejecuta una consulta SQL SELECT en la base de datos",
inputSchema={
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Consulta SQL SELECT a ejecutar"
}
},
"required": ["query"]
}
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "query_database":
query = arguments.get("query", "").strip()
# Validación de seguridad: solo SELECT
if not query.upper().startswith("SELECT"):
raise ValueError("Solo se permiten consultas SELECT")
conn = sqlite3.connect("example.db")
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
try:
cursor.execute(query)
rows = cursor.fetchall()
# Convertir a JSON
results = [dict(row) for row in rows]
return [TextContent(
type="text",
text=json.dumps(results, indent=2, ensure_ascii=False)
)]
except Exception as e:
raise ValueError(f"Error ejecutando consulta: {str(e)}")
finally:
conn.close()
raise ValueError(f"Herramienta desconocida: {name}")Ejemplo 2: Herramienta de Búsqueda Web
Herramienta para buscar información en internet
from mcp.server import Server
from mcp.types import Tool, TextContent
import httpx
app = Server("web-search-tools")
@app.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="web_search",
description="Busca información en internet usando una API de búsqueda",
inputSchema={
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Término de búsqueda"
},
"max_results": {
"type": "number",
"description": "Número máximo de resultados (default: 5)",
"default": 5,
"minimum": 1,
"maximum": 20
}
},
"required": ["query"]
}
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "web_search":
query = arguments.get("query")
max_results = arguments.get("max_results", 5)
if not query:
raise ValueError("query es requerido")
# Llamar a API de búsqueda (ejemplo con DuckDuckGo)
async with httpx.AsyncClient() as client:
response = await client.get(
"https://api.duckduckgo.com/",
params={
"q": query,
"format": "json",
"no_html": "1",
"skip_disambig": "1"
}
)
results = response.json()
# Formatear resultados
formatted_results = []
for i, result in enumerate(results.get("Results", [])[:max_results]):
formatted_results.append(
f"{i+1}. {result.get('Text', 'Sin descripción')}\n"
f" URL: {result.get('FirstURL', 'N/A')}"
)
return [TextContent(
type="text",
text="\n\n".join(formatted_results) if formatted_results else "No se encontraron resultados"
)]
raise ValueError(f"Herramienta desconocida: {name}")Ejemplo 3: Herramienta de Procesamiento
Herramienta para transformar y procesar datos JSON
from mcp.server import Server
from mcp.types import Tool, TextContent
import json
app = Server("data-processing-tools")
@app.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="transform_json",
description="Transforma un objeto JSON aplicando una función de transformación",
inputSchema={
"type": "object",
"properties": {
"data": {
"type": "string",
"description": "JSON string a transformar"
},
"operation": {
"type": "string",
"enum": ["uppercase_keys", "lowercase_keys", "sort_keys"],
"description": "Operación a realizar"
}
},
"required": ["data", "operation"]
}
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "transform_json":
data_str = arguments.get("data")
operation = arguments.get("operation")
try:
data = json.loads(data_str)
except json.JSONDecodeError:
raise ValueError("data debe ser un JSON válido")
if operation == "uppercase_keys":
transformed = {k.upper(): v for k, v in data.items()}
elif operation == "lowercase_keys":
transformed = {k.lower(): v for k, v in data.items()}
elif operation == "sort_keys":
transformed = dict(sorted(data.items()))
else:
raise ValueError(f"Operación desconocida: {operation}")
return [TextContent(
type="text",
text=json.dumps(transformed, indent=2)
)]
raise ValueError(f"Herramienta desconocida: {name}")Buenas Prácticas
Sigue estas recomendaciones para crear herramientas seguras, eficientes y fáciles de usar.
Descripciones Claras
Proporciona descripciones detalladas y específicas para cada herramienta. El LLM usa estas descripciones para decidir cuándo usarlas.
Esquemas de Validación
Define esquemas JSON Schema completos para los parámetros. Incluye tipos, requeridos, valores por defecto y validaciones.
Manejo de Errores
Implementa manejo robusto de errores. Retorna mensajes claros y útiles cuando algo falla para ayudar al LLM a entender qué pasó.
Idempotencia
Diseña herramientas idempotentes cuando sea posible. Ejecutar la misma herramienta múltiples veces debería tener el mismo efecto.
Validación de Entrada
Valida y sanitiza todos los parámetros de entrada antes de procesarlos. Nunca confíes en la entrada del usuario.
Respuestas Estructuradas
Retorna respuestas estructuradas y consistentes. Usa TextContent o ImageContent según corresponda, con formato claro.
¿Listo para crear tus propias herramientas?
Explora más sobre recursos, prompts y la arquitectura completa de MCP.