docs: refresh translations for recent English changes (#3379)

This commit is contained in:
Max 2026-08-24 19:29:54 +01:00 • committed by GitHub
commit c5d7d0b6d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,13 +1,13 @@
---
translation:
sections: [e33d441f12d50535, 7099694c603e0f5f, c1df4cf9673433e6, c9cd294541422e6e, 6cec073617bfd037, efa92b8f99e908c8, 6a22a29e27fb4601]
sections: [7be05607887e6853, e7375894888d9750, c36f73fc7e3af13b, 2fec2d7e129e62fe, 809b0e0a7c27295a, b4395a04d2a5d906, 1a436007f5f54779, c6b2078ed1e63ba5]
tool: 1
---
# Manejo de errores {#handling-errors}
Una herramienta puede fallar de dos maneras, y el SDK las trata de forma muy distinta.
Una herramienta puede fallar de tres maneras, y el SDK trata cada una de forma distinta.
Lanza una excepción ordinaria y la ve el **modelo**. Lanza `MCPError` y la ve el **protocolo**.
Lanza `ToolError` y el **modelo** ve tu mensaje. Lanza `MCPError` y lo ve el **protocolo**. Lanza cualquier otra cosa y es un fallo inesperado: el modelo solo se entera de que la llamada falló, y el traceback va a tu log.
Esta página trata de cómo elegir.
@ -15,11 +15,11 @@ Esta página trata de cómo elegir.
Toma una herramienta que busca algo, y deja que la búsqueda falle:
```python title="server.py" hl_lines="11-12"
```python title="server.py" hl_lines="2 12-13"
--8<-- "docs_src/handling_errors/tutorial001.py"
```
No hay nada de MCP en esas dos líneas. `get_author` lanza un `ValueError` común y corriente, como lo haría cualquier función de Python.
`ToolError`, de `mcp.server.mcpserver.exceptions`, es la forma en que una herramienta le dice al modelo que algo salió mal.
Llámala con un título que no esté en el catálogo y observa el resultado:
@ -30,13 +30,15 @@ result.structured_content # None
```
* La solicitud **tuvo éxito**. Hay un resultado; no se lanzó nada del lado de quien llama.
* `is_error` es `True`, y el mensaje de tu excepción (con el nombre de la herramienta como prefijo) está en `content`, justo donde lee el modelo.
* `is_error` es `True`, y tu mensaje (con el nombre de la herramienta como prefijo) está en `content`, justo donde lee el modelo.
* `structured_content` es `None`. Una llamada fallida no tiene valor devuelto que estructurar.
Esto es un **error de herramienta**, y es el comportamiento por defecto para *cualquier* excepción que lance tu herramienta. Además, casi siempre es lo que quieres.
Esto es un **error de herramienta**, y casi siempre es lo que quieres.
El modelo es quien llama a tu herramienta. Él eligió los argumentos. Así que un error de herramienta es un turno de la conversación: el modelo lee *"No book titled 'Nothing' in the catalog."*, se da cuenta de que adivinó mal el título y vuelve a llamar con uno mejor. Escribiste un `raise` y obtuviste un agente que se corrige solo.
En el servidor, un `ToolError` es una sola línea `INFO` en el log, sin traceback. Lo veías venir, así que no hay nada que investigar.
!!! tip
Nunca devuelvas con `return` un mensaje de error desde una herramienta. Una cadena devuelta
tiene `is_error=False`, así que para el modelo (y para toda interfaz de cliente) parece que la
@ -44,7 +46,7 @@ El modelo es quien llama a tu herramienta. Él eligió los argumentos. Así que
## Un error que el modelo no puede corregir {#an-error-the-model-cannot-fix}
Ahora cambia `ValueError` por `MCPError`.
Ahora cambia `ToolError` por `MCPError`.
```python title="server.py" hl_lines="1 3 14"
--8<-- "docs_src/handling_errors/tutorial002.py"
@ -77,10 +79,10 @@ Ahora cambia `ValueError` por `MCPError`.
Los dos caminos responden a dos preguntas distintas.
* **Lanza cualquier excepción** ante un fallo de *ejecución*: lo que tu herramienta intentó hacer no funcionó. El modelo eligió la llamada, así que el modelo debería ver la consecuencia y tener la oportunidad de recuperarse. Un título mal escrito, una API externa que agotó el tiempo de espera, una fila que no existe: todos son errores de herramienta.
* **Lanza `ToolError`** ante un fallo de *ejecución*: lo que tu herramienta intentó hacer no funcionó. El modelo eligió la llamada, así que el modelo debería ver la consecuencia y tener la oportunidad de recuperarse. Un título mal escrito, una API externa que agotó el tiempo de espera, una fila que no existe: todos son errores de herramienta.
* **Lanza `MCPError`** cuando debe rechazarse la *solicitud misma*: al cliente le falta una capacidad de la que depende tu herramienta, el servidor no está en condiciones de atender a nadie, quien llama se saltó un paso obligatorio. Ningún reintento del modelo arregla nada de eso, así que no se gana nada entregándole el mensaje.
Una sola pregunta lo decide: **¿podría haberlo evitado un modelo más inteligente?** Sí -> excepción ordinaria. No -> `MCPError`.
Una sola pregunta lo decide: **¿podría haberlo evitado un modelo más inteligente?** Sí -> `ToolError`. No -> `MCPError`.
Según ese criterio, la segunda versión de `get_author` eligió mal: un título mejor lo arregla, así que el modelo merecía ver el mensaje. Está ahí para mostrarte el mecanismo, no para recomendarlo.
@ -89,6 +91,25 @@ Según ese criterio, la segunda versión de `get_author` eligió mal: un título
opcional `data`. Lo que pongas en ellos es lo que recibe el cliente: el SDK reenvía un
`MCPError` lanzado tal cual, en lugar de sanearlo.
## Cualquier otra excepción {#any-other-exception}
Ahora quita la comprobación y deja que la búsqueda en el diccionario falle por sí sola:
```python title="server.py" hl_lines="11"
--8<-- "docs_src/handling_errors/tutorial004.py"
```
`CATALOG[title]` lanza `KeyError`. No lo tenías previsto, así que el SDK lo trata como un fallo inesperado:
```python
result.is_error # True
result.content # [TextContent(text="Error executing tool get_author")]
```
La llamada sigue devolviendo `is_error=True`, así que el modelo sabe que falló y puede seguir adelante. Lo que no recibe es el texto de la excepción: un `KeyError` de tu código, o un montón de SQL de un driver tres bibliotecas más abajo, puede describir el funcionamiento interno de tu servidor, así que nunca sale del servidor.
Lo recibes tú. El servidor registra el fallo inesperado en nivel `ERROR` con el traceback completo, como `Tool 'get_author' raised an unexpected exception`. Así, un log de producción en `WARNING` se mantiene en silencio ante cada `ToolError` y habla en cuanto algo está realmente roto.
## Un recurso que no existe {#a-resource-that-doesnt-exist}
Los recursos trazan la misma línea, e incluyen una excepción con nombre propio para el caso común.
@ -109,7 +130,7 @@ Cuando no pueda, lanza `ResourceNotFoundError`. El SDK lo convierte en el error
}
```
Fíjate en que aquí no hay un resultado a medias con `is_error=True`. La lectura de un recurso devuelve contenido o falla: los recursos solo tienen el camino del protocolo. Las plantillas y todo lo demás sobre recursos están en **[Recursos](resources.md)**.
Fíjate en que aquí no hay un resultado a medias con `is_error=True`. La lectura de un recurso devuelve contenido o falla: los recursos solo tienen el camino del protocolo. `ResourceError` es lo mismo para un fallo que no es "no encontrado" (`-32603`, tu mensaje), y ambos son una sola línea `INFO` en tu log. Cualquier otra excepción salvo `MCPError` es un fallo inesperado: el cliente recibe un `-32603` que solo nombra la URI, y el traceback va a tu log en nivel `ERROR`. Las plantillas y todo lo demás sobre recursos están en **[Recursos](resources.md)**.
## Errores que nunca lanzas {#errors-you-never-raise}
@ -120,20 +141,22 @@ Envíale a `get_author` un `title` que no sea una cadena y el SDK lo rechaza con
Eso significa toda una clase de sentencias `raise` que no escribes: no vuelvas a validar tus propias anotaciones de tipo.
!!! info
Todo lo de esta página es lo que ve un **cliente**, y el `Client` en memoria con el que
escribirás pruebas ve exactamente lo mismo. Ni siquiera `raise_exceptions=True` convierte un
error de herramienta de nuevo en un traceback: para cuando ese indicador podría actuar, tu
excepción ya es el resultado con `is_error=True`. Haz las aserciones sobre el resultado.
Todo lo que ve un **cliente** en esta página lo ve también el `Client` en memoria con el que
escribirás pruebas. Ni siquiera `raise_exceptions=True` le devuelve a quien llama la excepción
de una herramienta que falla: para cuando ese indicador podría actuar, tu excepción ya es el
resultado con `is_error=True`. Haz las aserciones sobre el resultado. Si necesitas el traceback
de un fallo inesperado, está en el log del servidor, y el `caplog` de pytest lo captura.
**[Pruebas](../get-started/testing.md)** cubre el patrón.
## Resumen {#recap}
* Lanza **cualquier excepción** en una herramienta -> la llamada devuelve `is_error=True` con tu mensaje en `content`. El modelo lo lee y puede reintentar. Este es el comportamiento por defecto.
* Lanza **`ToolError`** en una herramienta -> la llamada devuelve `is_error=True` con tu mensaje en `content`. El modelo lo lee y puede reintentar.
* Lanza **`MCPError`** -> la llamada misma falla con un error JSON-RPC. El modelo no ve nada; el host se encarga. `code`, `message` y `data` sobreviven intactos.
* La pregunta decisiva: *¿podría haberlo evitado un modelo más inteligente?* Sí -> excepción. No -> `MCPError`.
* La pregunta decisiva: *¿podría haberlo evitado un modelo más inteligente?* Sí -> `ToolError`. No -> `MCPError`.
* Cualquier **otra excepción** es un fallo inesperado -> `is_error=True` con solo `Error executing tool <name>` para el modelo, y un registro `ERROR` con el traceback para ti.
* `ResourceNotFoundError` desde un handler de recurso -> el `-32602` del protocolo, con la URI en `data`.
* Los argumentos incorrectos se rechazan contra el esquema antes de que se ejecute tu función; para esos no usas `raise`.
* `from mcp import MCPError`; las constantes de códigos de error vienen de `mcp.types`.
* Importaciones: `from mcp import MCPError`, `from mcp.server.mcpserver.exceptions import ToolError, ResourceError, ResourceNotFoundError`, y las constantes de códigos de error de `mcp.types`.
Errores resueltos. Eso es todo lo que un servidor *expone*. Lo que cada handler puede leer, y hacer de vuelta hacia el cliente mientras se ejecuta, es la siguiente sección: **[Dentro de tu handler](../handlers/index.md)**.