Definición de Herramientas Llamables en un Servidor MCP
Una herramienta llamable es la forma más directa en que un servidor MCP le da algo que hacer a un modelo.
Configurar una herramienta correctamente significa que el modelo puede encontrarla, entender qué hace, llamarla con argumentos válidos y dar sentido a lo que regresa.
Resumen
Una herramienta en el SDK de Python de MCP comienza como una función ordinaria decorada con @mcp.tool().
El nombre de la función, sus parámetros con anotaciones de tipo y su cadena de documentación (docstring) se convierten conjuntamente en el esquema que un cliente utiliza para decidir cuándo y cómo llamarla.
Los manejadores deben ser estrechos y predecibles, haciendo un trabajo bien hecho en lugar de ramificarse en varios comportamientos no relacionados basándose en un argumento de bandera.
Los errores deben elevarse como excepciones con un mensaje claro, no ser suprimidos o devueltos como cadenas ambiguas, para que el modelo obtenga retroalimentación útil cuando una llamada falla.
Las entradas estructuradas y validadas (a través de modelos Pydantic o anotaciones de tipo simples) detectan argumentos incorrectos antes de que su código de manejo se ejecute.
Receta
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("tool-recipes")
@mcp.tool()
def get_weather(city: str, units: str = "celsius") -> str:
"""Obtener el clima actual de una ciudad.
Args:
city: El nombre de la ciudad, por ejemplo, "Austin" o "Tokio".
units: "celsius" o "fahrenheit". Predeterminado a "celsius".
"""
if units not in ("celsius", "fahrenheit"):
raise ValueError('units debe ser "celsius" o "fahrenheit"')
return f"72 grados {units} y soleado en {city}"Cuándo recurrir a esto:
- El modelo necesita realizar una acción o cálculo, no solo leer datos estáticos.
- La operación tiene un conjunto pequeño y bien tipado de entradas que se mapean limpiamente a los parámetros de la función.
- Desea que la misma capacidad sea accesible desde Claude Code, Claude Desktop o una aplicación SDK de Agente personalizada sin reescribirla por cliente.
- El resultado de la llamada debe formar parte de la conversación, no solo ser almacenado en caché o mostrado por separado.
Ejemplo de Trabajo
# server.py
from typing import Literal
from mcp.server.fastmcp import FastMCP
from pydantic import BaseModel, Field
mcp = FastMCP("task-tools")
# Almacén en memoria para este ejemplo; reemplace con una base de datos real en producción.
_tasks: dict[int, dict] = {}
_next_id = 1
class CreateTaskInput(BaseModel):
title: str = Field(..., min_length=1, max_length=200, description="Título corto de la tarea")
priority: Literal["low", "medium", "high"] = Field(
"medium", description="Nivel de prioridad de la tarea"
)
@mcp.tool()
def create_task(input: CreateTaskInput) -> str:
"""Crear una nueva tarea y devolver su id.
Úselo cuando el usuario pida añadir, seguir o recordar un elemento de la lista de tareas pendientes.
"""
global _next_id
task_id = _next_id
_next_id += 1
_tasks[task_id] = {"title": input.title, "priority": input.priority, "done": False}
return f"Tarea creada {task_id}: {input.title} (prioridad: {input.priority})"
@mcp.tool()
def complete_task(task_id: int) -> str:
"""Marcar una tarea existente como completada.
Args:
task_id: El id numérico devuelto por create_task.
"""
task = _tasks.get(task_id)
if task is None:
raise ValueError(f"No se encontró ninguna tarea con el id {task_id}")
task["done"] = True
return f"Tarea {task_id} ({task['title']}) marcada como completada"
@mcp.tool()
def list_tasks(only_open: bool = True) -> str:
"""Listar las tareas seguidas.
Args:
only_open: Si es True, ocultar las tareas ya marcadas como completadas. Predeterminado a True.
"""
rows = [
f"#{tid} [{t['priority']}] {t['title']}{' (completada)' if t['done'] else ''}"
for tid, t in _tasks.items()
if not (only_open and t["done"])
]
return "\n".join(rows) if rows else "No se encontraron tareas."
if __name__ == "__main__":
mcp.run(transport="stdio")Lo que esto demuestra:
- Tres herramientas estrechas y de propósito único (
create_task,complete_task,list_tasks) en lugar de una herramienta con un indicadormode. - Un modelo Pydantic (
CreateTaskInput) que valida una entrada estructurada con una restricción de longitud y un conjunto restringido de valores permitidos. - Un tipo
Literalque restringeprioritya exactamente tres cadenas válidas, aplicado antes de que se ejecute el cuerpo del manejador. - Manejo de errores consistente: los ids desconocidos elevan
ValueErrorcon un mensaje que nombra el problema específico. - Devoluciones de cadenas simples que se leen de forma natural cuando el modelo las presenta en una conversación.
Inmersión Profunda
Cómo Funciona
- Cuando el servidor se inicia,
FastMCPinspecciona cada función decorada con@mcp.tool()y construye un Esquema JSON a partir de sus anotaciones de tipo y valores predeterminados. - La cadena de documentación (docstring) de la función se convierte en la descripción de la herramienta; el cliente muestra esto al modelo para que pueda juzgar cuándo la herramienta es relevante.
- Cuando un cliente llama a
list_tools(), recibe este esquema y descripción para cada herramienta registrada, sin que usted escriba ningún esquema a mano. - Cuando el modelo decide llamar a una herramienta, el cliente envía el nombre de la herramienta más un objeto JSON de argumentos; el SDK valida ese objeto contra el esquema generado antes de invocar su función.
- Si la validación falla (tipo incorrecto, campo requerido faltante, valor fuera de un conjunto
Literal), el SDK devuelve un error de esquema al cliente sin llamar nunca a su manejador.
Reglas de Nomenclatura y Descripción de un Vistazo
| Elemento | Buena Práctica | Por Qué Importa |
|---|---|---|
| Nombre de la herramienta | snake_case, empezando por verbo (create_task, no task) | El modelo empareja nombres con la intención; un verbo señala una acción |
| Primera línea del docstring | Una frase que indica exactamente lo que hace la herramienta | Este es el texto principal que el modelo usa para decidir la relevancia |
Args: del docstring | Una línea por parámetro, lenguaje claro | Clarifica unidades, formatos y rangos válidos que la anotación de tipo no puede expresar |
| Nombres de parámetros | Descriptivos, no abreviados (city, no c) | Reduce llamadas ambiguas o mal formadas |
Notas de Python
# Prefiera un tipo restringido sobre una cadena suelta cuando el conjunto válido sea pequeño y conocido.
from typing import Literal
@mcp.tool()
def set_status(status: Literal["open", "in_progress", "closed"]) -> str:
"""Establecer el estado del elemento actual."""
return f"Estado establecido a {status}"
# Para validación más rica (rangos, formatos, campos anidados), use un modelo Pydantic
# como parámetro único, como se muestra en CreateTaskInput arriba.Parámetros y Valores de Retorno
| Parámetro | Tipo | Descripción |
|---|---|---|
| nombre de la función | str (a través del nombre def) | Se convierte en el identificador de la herramienta; manténgalo estable una vez que los clientes dependan de él |
| anotaciones de tipo | Tipos de Python / Literal / modelo Pydantic | Compilado automáticamente en el Esquema JSON de la herramienta |
| docstring | str | Se convierte en la descripción de la herramienta mostrada al modelo |
| valor de retorno | str o contenido estructurado | Envuelto en el formato de contenido de MCP y devuelto al cliente |
Trampas Comunes
- Nombres de herramientas vagos como
handleoprocess. El modelo no tiene nada con qué emparejar la intención. Solución: use un nombre específico, que comience con un verbo, comocreate_taskosend_email. - Una herramienta con un parámetro de cadena
modeoactionque se ramifica en comportamientos no relacionados. Esto confunde el descubrimiento basado en esquemas y aumenta la probabilidad de una llamada incorrecta. Solución: divídalo en herramientas separadas y de alcance estrecho. - Devolver
Noneo una cadena vacía en caso de fallo en lugar de elevar una excepción. El modelo no tiene señal de que algo salió mal y puede informar incorrectamente del éxito. Solución: eleve una excepción con un mensaje específico; deje que el SDK la traduzca a un error de herramienta. - Docstrings faltantes o delgados. Sin una descripción, el modelo está adivinando lo que hace una herramienta basándose solo en su nombre. Solución: incluya siempre un resumen de una línea más un bloque
Args:. - Omitir la validación de entrada en rangos numéricos o de cadenas. Un manejador que confía ciegamente en sus entradas puede fallar en casos extremos como recuentos negativos o cadenas vacías. Solución: use
Literal, restriccionesFieldde Pydantic o comprobaciones explícitas al principio del manejador. - Mutar estado global compartido sin ninguna protección. Dos llamadas rápidas de herramientas contra el mismo almacén en memoria pueden competir o corromper datos. Solución: use un almacén de datos real con transacciones, o agregue bloqueo, una vez que supere una demostración.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| Recurso MCP | La operación es una lectura pura sin efectos secundarios | El modelo necesita tomar una acción o realizar un cálculo |
| Prompt MCP | Desea estandarizar instrucciones, no exponer una acción llamable | El cliente necesita un valor de resultado de vuelta en la conversación |
| Llamada directa a función (no MCP) | La capacidad solo se usará alguna vez por una aplicación específica | Desea que la misma capacidad sea reutilizable en Claude Code, Claude Desktop y aplicaciones personalizadas |
Un manejador @mcp.server de bajo nivel (en lugar de FastMCP) | Necesita control total sobre el protocolo de enlace o el cableado de transporte personalizado | Solo necesita exponer un puñado de herramientas sencillas rápidamente |
Preguntas Frecuentes
¿Cómo convierte el SDK una función de Python en un esquema de herramienta?
FastMCPlee las anotaciones de tipo de la función para construir un Esquema JSON para sus parámetros.- Lee la cadena de documentación (docstring) para poblar la descripción de la herramienta mostrada al modelo.
- No se requiere redacción manual de esquemas para parámetros escalares típicos o modelos Pydantic.
¿Necesito Pydantic, o las anotaciones de tipo simples son suficientes?
Las anotaciones de tipo simples (str, int, bool, Literal[...]) son suficientes para herramientas simples. Utilice un parámetro BaseModel de Pydantic cuando necesite restricciones a nivel de campo, estructuras anidadas o lógica de validación reutilizable en múltiples herramientas.
¿Qué sucede si el modelo llama a una herramienta con tipos de argumento incorrectos?
El SDK valida los argumentos entrantes contra el esquema generado antes de que se ejecute su manejador. Los argumentos inválidos producen un error de esquema devuelto al cliente, y el cuerpo de su función nunca se ejecuta.
¿Debería una herramienta devolver texto plano o datos estructurados?
- Las cadenas simples funcionan bien cuando el resultado está destinado a ser leído directamente en la conversación.
- Vale la pena devolver contenido estructurado cuando un cliente o una herramienta posterior necesita analizar el resultado programáticamente.
¿Qué tan específico debe ser el docstring de una herramienta?
Lo suficientemente específico como para que un lector que no esté familiarizado con su base de código pueda adivinar exactamente cuándo llamarla y qué significa cada argumento, incluidas las unidades, formatos o rangos válidos que no son obvios por la anotación de tipo.
¿Es mejor tener una herramienta flexible o varias herramientas estrechas?
Varias herramientas estrechas, en la mayoría de los casos. Una herramienta con un indicador mode o action oculta varios comportamientos detrás de un solo esquema, lo que dificulta que el modelo elija correctamente y dificulta que usted pruebe cada comportamiento de forma aislada.
¿Cómo señalo un error desde un manejador de herramientas?
@mcp.tool()
def withdraw(account_id: str, amount: float) -> str:
"""Retirar fondos de una cuenta."""
if amount <= 0:
raise ValueError("el monto debe ser positivo")
return f"Retirados {amount} de {account_id}"Elevar una excepción estándar con un mensaje claro es suficiente; el SDK la convierte en un error de herramienta que el cliente puede mostrar al modelo.
¿Puede una herramienta llamar a otra herramienta internamente?
Nada impide que un manejador llame a una función Python normal que otra herramienta también llama, pero las herramientas no deben llamarse entre sí a través de la capa del protocolo MCP en sí. Comparta la lógica a través de la composición de funciones ordinarias en su lugar.
¿Cuál es el riesgo de usar estado global mutable dentro de una herramienta?
Las llamadas concurrentes pueden competir contra el estado compartido, produciendo resultados inconsistentes o datos corruptos. Está bien para una demostración o prototipo, pero las herramientas de producción deben usar un almacén de datos real con las garantías transaccionales adecuadas.
¿El nombre de la herramienta tiene que coincidir con el nombre de la función Python?
Por defecto sí, FastMCP usa el nombre de la función como nombre de la herramienta. Manténgalo estable una vez que cualquier cliente haya comenzado a depender de él, ya que un cambio de nombre cambia lo que el cliente ve durante el descubrimiento.
¿Cuántos parámetros son demasiados para una herramienta?
No hay un límite estricto, pero si una herramienta necesita muchos parámetros opcionales para cubrir diferentes casos de uso, a menudo es una señal de que debería dividirse en una o más herramientas enfocadas en su lugar.
¿Se pueden usar valores de parámetros predeterminados en una firma de herramienta?
Sí. Un parámetro como units: str = "celsius" se convierte en un campo opcional en el esquema generado, y el cliente puede omitirlo por completo para recurrir al valor predeterminado.
Relacionados
- Entendiendo MCP: Herramientas, Recursos y Prompts - la base conceptual de lo que es una herramienta frente a un recurso o un prompt.
- Conceptos Básicos del Núcleo de MCP - un servidor y cliente mínimo de una sola herramienta sobre el cual construir.
- Exposición de Datos Legibles como Recursos MCP - el primitivo hermano para datos de solo lectura.
- Comparación de Herramientas, Recursos y Prompts de MCP - decidir cuándo una capacidad pertenece como herramienta en absoluto.
- Mejores Prácticas de Conceptos Básicos del Núcleo de MCP - una lista de verificación para la calidad del esquema y la nomenclatura.
Versiones de Stack: Escrito contra la línea de modelos Claude actual a partir de ~junio de 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (el predeterminado), y Claude Haiku 4.5 - y la especificación actual del Protocolo de Contexto del Modelo. Los nombres de los modelos, las versiones del SDK y la especificación MCP se mueven rápidamente: verifique los detalles actuales en platform.claude.com/docs y modelcontextprotocol.io antes de confiar en ellos.