Saltar al contenido principal
Las herramientas personalizadas extienden el SDK del Agente permitiéndole definir sus propias funciones que Claude puede llamar durante una conversación. Usando el servidor MCP en proceso del SDK, puede dar a Claude acceso a bases de datos, APIs externas, lógica específica del dominio u cualquier otra capacidad que su aplicación necesite. Esta guía cubre cómo definir herramientas con esquemas de entrada y controladores, agruparlas en un servidor MCP, pasarlas a query y controlar a qué herramientas puede acceder Claude. También cubre manejo de errores, anotaciones de herramientas y devolución de contenido no textual como imágenes.

Referencia rápida

Crear una herramienta personalizada

Una herramienta se define por cuatro partes, pasadas como argumentos al ayudante tool() en TypeScript o al decorador @tool en Python:
  • Nombre: un identificador único que Claude usa para llamar a la herramienta.
  • Descripción: qué hace la herramienta. Claude lee esto para decidir cuándo llamarla.
  • Esquema de entrada: los argumentos que Claude debe proporcionar. En TypeScript esto es siempre un esquema Zod, y los args del controlador se tipan automáticamente desde él. En Python esto es un diccionario que mapea nombres a tipos, como {"latitude": float}, que el SDK convierte a JSON Schema para usted. El decorador de Python también acepta un diccionario completo de JSON Schema directamente cuando necesita enumeraciones, rangos, campos opcionales u objetos anidados.
  • Controlador: la función asincrónica que se ejecuta cuando Claude llama a la herramienta. Recibe los argumentos validados y debe devolver un objeto con:
    • content (requerido): un array de bloques de resultado, cada uno con un type de "text", "image", "audio", "resource" o "resource_link". Vea Devolver imágenes y recursos para bloques no textuales.
    • structuredContent (opcional): un objeto JSON que contiene el resultado como datos legibles por máquina, devuelto junto a content. Vea Devolver datos estructurados.
    • isError (opcional): establezca en true para señalar un fallo de herramienta para que Claude pueda reaccionar a él. Vea Manejar errores.
Después de definir una herramienta, envuélvala en un servidor con createSdkMcpServer (TypeScript) o create_sdk_mcp_server (Python). El servidor se ejecuta en proceso dentro de su aplicación, no como un proceso separado.

Ejemplo de herramienta meteorológica

Este ejemplo define una herramienta get_temperature y la envuelve en un servidor MCP. Solo configura la herramienta; para pasarla a query y ejecutarla, vea Llamar a una herramienta personalizada abajo.
Vea la referencia de TypeScript tool() o la referencia de Python @tool para detalles completos de parámetros, incluyendo formatos de entrada JSON Schema y estructura de valor de retorno.
Para hacer un parámetro opcional: en TypeScript, agregue .default() al campo Zod. En Python, el esquema dict trata cada clave como requerida, así que deje el parámetro fuera del esquema, menciónelo en la cadena de descripción y léalo con args.get() en el controlador. La herramienta get_precipitation_chance abajo muestra ambos patrones.

Llamar a una herramienta personalizada

Pase el servidor MCP que creó a query a través de la opción mcpServers. La clave en mcpServers se convierte en el segmento {server_name} en el nombre completamente calificado de cada herramienta: mcp__{server_name}__{tool_name}. Liste ese nombre en allowedTools para que la herramienta se ejecute sin un aviso de permiso. Estos fragmentos reutilizan el weatherServer del ejemplo anterior para preguntarle a Claude cuál es el clima en una ubicación específica.

Agregar más herramientas

Un servidor contiene tantas herramientas como liste en su array tools. Con más de una herramienta en un servidor, puede listar cada una en allowedTools individualmente o usar el comodín mcp__weather__* para cubrir cada herramienta que el servidor expone. El ejemplo abajo agrega una segunda herramienta, get_precipitation_chance, al weatherServer del ejemplo de herramienta meteorológica y lo reconstruye con ambas herramientas en el array.
Cada herramienta en este array consume espacio de ventana de contexto en cada turno. Si está definiendo docenas de herramientas, vea búsqueda de herramientas para cargarlas bajo demanda en su lugar.

Agregar anotaciones de herramientas

Las anotaciones de herramientas son metadatos opcionales que describen cómo se comporta una herramienta. Páselas como el quinto argumento al ayudante tool() en TypeScript o a través del argumento de palabra clave annotations para el decorador @tool en Python. Todos los campos de sugerencia son booleanos. Las anotaciones son metadatos, no aplicación. Una herramienta marcada como readOnlyHint: true aún puede escribir en disco si eso es lo que hace el controlador. Mantenga la anotación precisa con respecto al controlador. Este ejemplo agrega readOnlyHint a la herramienta get_temperature del ejemplo de herramienta meteorológica.
Vea ToolAnnotations en la referencia de TypeScript o Python.

Controlar el acceso a herramientas

El ejemplo de herramienta meteorológica registró un servidor y listó herramientas en allowedTools. Esta sección cubre cómo se construyen los nombres de herramientas y cómo limitar el acceso cuando tiene múltiples herramientas o desea restringir integrados.

Formato de nombre de herramienta

Cuando las herramientas MCP se exponen a Claude, sus nombres siguen un formato específico:
  • Patrón: mcp__{server_name}__{tool_name}
  • Ejemplo: Una herramienta nombrada get_temperature en servidor weather se convierte en mcp__weather__get_temperature

Configurar herramientas permitidas

La opción tools y las listas permitidas/no permitidas afectan dos capas: disponibilidad, que controla si una herramienta aparece en el contexto de Claude, y permiso, que controla si una llamada se aprueba una vez que Claude intenta hacerla. tools y las entradas de disallowedTools con nombre simple cambian la disponibilidad. allowedTools y las reglas de disallowedTools con alcance cambian solo el permiso. Para eliminar un integrado completamente, omítalo de tools o liste su nombre simple en disallowedTools (Python: disallowed_tools); ambos mantienen la herramienta fuera del contexto para que Claude nunca la intente. Una regla de disallowedTools con alcance bloquea las llamadas coincidentes pero deja la herramienta visible, por lo que Claude puede desperdiciar un turno intentándola. Vea Configurar permisos para el orden de evaluación completo.

Manejar errores

Un error del controlador no detiene el bucle del agente. El servidor MCP en proceso del SDK captura excepciones no capturadas y las devuelve como resultados de error, por lo que la forma en que reporta un error determina qué lee Claude, no si la consulta falla: En ambos casos Claude puede reintentar, intentar una herramienta diferente o explicar el fallo. Capture errores usted mismo cuando el mensaje de excepción sin procesar no sea suficiente para que Claude actúe. El ejemplo abajo captura dos tipos de fallos dentro del controlador y compone el mensaje de error que Claude lee. Un estado HTTP no 200 se captura de la respuesta y se devuelve como un resultado de error. Un error de red o JSON inválido se captura por el try/except (Python) o try/catch (TypeScript) circundante y también se devuelve como un resultado de error. En ambos casos Claude recibe un mensaje que describe el fallo en lugar de una cadena de excepción sin procesar.

Devolver imágenes y recursos

El array content en un resultado de herramienta acepta bloques text, image, audio, resource y resource_link. Puede mezclarlos en la misma respuesta. En TypeScript, los bloques de audio se guardan en disco y Claude recibe un bloque de texto con la ruta del archivo guardado; en Python, el SDK elimina los bloques de audio del resultado de la herramienta y registra una advertencia. Los bloques de enlace de recurso se convierten en un bloque de texto que contiene el nombre del enlace, el URI y la descripción.

Imágenes

Un bloque de imagen lleva los bytes de imagen en línea, codificados como base64. No hay campo de URL. Para devolver una imagen que vive en una URL, búsquela en el controlador, lea los bytes de respuesta y codifíquelos en base64 antes de devolver. El resultado se procesa como entrada visual.

Recursos

Un bloque de recurso incrusta un contenido identificado por un URI. El URI es una etiqueta para que Claude la referencie; el contenido real va en el campo text o blob del bloque. Use esto cuando su herramienta produce algo que tiene sentido direccionar por nombre más tarde, como un archivo generado o un registro de un sistema externo. Este ejemplo muestra un bloque de recurso devuelto desde dentro de un controlador de herramienta. El URI file:///tmp/report.md es una etiqueta que Claude puede referenciar más tarde; el SDK no lee desde esa ruta.
Estas formas de bloque provienen del tipo MCP CallToolResult. Vea la especificación MCP para la definición completa.

Devolver datos estructurados

structuredContent es un objeto JSON opcional en el resultado, separado del array content. Úselo para devolver valores sin procesar que Claude pueda leer como campos exactos en lugar de analizarlos de una cadena de texto o imagen. Cuando structuredContent se establece, Claude recibe el JSON más cualquier bloque de imagen o recurso de content. Los bloques de texto en content no se reenvían, ya que se asume que duplican los datos estructurados. El ejemplo abajo renderiza un gráfico como un bloque de imagen y devuelve los puntos de datos detrás de él en structuredContent del mismo controlador.
TypeScript
El decorador @tool de Python reenvía solo content e is_error del diccionario de retorno del controlador. Para devolver structuredContent desde Python, ejecute un servidor MCP independiente en lugar de un servidor SDK en proceso.

Ejemplo: convertidor de unidades

Esta herramienta convierte valores entre unidades de longitud, temperatura y peso. Un usuario puede preguntar “convertir 100 kilómetros a millas” o “¿cuál es 72°F en Celsius?” y Claude elige el tipo de unidad correcto y las unidades de la solicitud. Demuestra dos patrones:
  • Esquemas de enumeración: unit_type está restringido a un conjunto fijo de valores. En TypeScript, use z.enum(). En Python, el esquema dict no admite enumeraciones, por lo que se requiere el diccionario JSON Schema completo.
  • Manejo de entrada no admitida: cuando no se encuentra un par de conversión, el controlador devuelve isError: true para que Claude pueda decirle al usuario qué salió mal en lugar de tratar un fallo como un resultado normal.
Una vez que el servidor se define, páselo a query de la misma manera que el ejemplo meteorológico. Este ejemplo envía tres indicaciones diferentes en un bucle para mostrar la misma herramienta manejando diferentes tipos de unidades. Para cada respuesta, inspecciona objetos AssistantMessage (que contienen las llamadas de herramienta que Claude hizo durante ese turno) e imprime cada ToolUseBlock antes de imprimir el texto final de ResultMessage. Esto le permite ver cuándo Claude está usando la herramienta versus respondiendo desde su propio conocimiento.

Próximos pasos

Las herramientas personalizadas envuelven funciones asincrónicas en una interfaz estándar. Puede mezclar los patrones en esta página en el mismo servidor: un único servidor puede contener una herramienta de base de datos, una herramienta de puerta de enlace de API y un renderizador de imágenes uno al lado del otro. Desde aquí:
  • Si su servidor crece a docenas de herramientas, vea búsqueda de herramientas para diferir la carga hasta que Claude las necesite.
  • Para conectarse a servidores MCP externos (sistema de archivos, GitHub, Slack) en lugar de construir los suyos propios, vea Conectar servidores MCP.
  • Para controlar qué herramientas se ejecutan automáticamente versus requerir aprobación, vea Configurar permisos.