Tool Result Format
Learn about content types (text, image) and the isError flag.
The CallToolResult Structure
When a tool executes, it returns a CallToolResult object. This
object contains the tool's output in a structured format that supports
multiple content types.
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "The result of the tool execution"
}
],
"isError": false
}
}
Result Fields
| Field | Type | Description |
|---|---|---|
content |
array | Array of content items (text, image, etc.) |
isError |
boolean | Optional. True if the tool execution failed |
Content Types
MCP supports multiple content types in tool results. The most common are
text and image.
Text Content
The most common content type. Use for any text-based output:
{
"content": [
{
"type": "text",
"text": "The sum of 5 and 3 is 8"
}
]
}
Image Content
For tools that generate or return images. Images are base64-encoded:
{
"content": [
{
"type": "image",
"data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJ...",
"mimeType": "image/png"
}
]
}
Multiple Content Items
Tools can return multiple content items of different types:
{
"content": [
{
"type": "text",
"text": "Here is the chart you requested:"
},
{
"type": "image",
"data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJ...",
"mimeType": "image/png"
},
{
"type": "text",
"text": "The chart shows sales data for Q1 2024."
}
]
}
Python Helper Functions
Create helper functions to format tool results consistently:
"""
result_helpers.py - Helper functions for formatting tool results.
"""
import base64
def text_result(text):
"""
Create a text content result.
Args:
text: The text to return
Returns:
dict: MCP content object
"""
return {
"content": [
{
"type": "text",
"text": str(text)
}
]
}
def error_result(message):
"""
Create an error result.
Args:
message: Error message
Returns:
dict: MCP content object with isError flag
"""
return {
"content": [
{
"type": "text",
"text": f"Error: {message}"
}
],
"isError": True
}
def image_result(image_bytes, mime_type="image/png", caption=None):
"""
Create an image content result.
Args:
image_bytes: Raw image bytes
mime_type: MIME type of the image
caption: Optional text caption
Returns:
dict: MCP content object
"""
content = []
if caption:
content.append({
"type": "text",
"text": caption
})
content.append({
"type": "image",
"data": base64.b64encode(image_bytes).decode("utf-8"),
"mimeType": mime_type
})
return {"content": content}
def multi_result(*items):
"""
Create a result with multiple content items.
Args:
*items: Content items (dicts with type, text/data, etc.)
Returns:
dict: MCP content object
"""
return {"content": list(items)}
Using the Helpers
Here's how to use these helpers in your tool handlers:
def execute_add(arguments):
"""Add two numbers."""
a = arguments.get("a")
b = arguments.get("b")
if a is None or b is None:
return error_result("Both 'a' and 'b' are required")
result = a + b
return text_result(f"The sum of {a} and {b} is {result}")
def execute_divide(arguments):
"""Divide two numbers."""
a = arguments.get("a")
b = arguments.get("b")
if b == 0:
return error_result("Cannot divide by zero")
result = a / b
return text_result(f"{a} / {b} = {result}")
def execute_generate_chart(arguments):
"""Generate a chart image."""
# ... chart generation logic ...
chart_bytes = generate_chart(arguments)
return image_result(
chart_bytes,
mime_type="image/png",
caption="Sales chart for the requested period"
)
The isError Flag
The isError flag indicates that the tool executed but
encountered an error. This is different from a JSON-RPC error:
Use isError: true when the tool ran but failed (e.g., API returned
an error, file not found). Use JSON-RPC errors for request-level problems
(e.g., unknown tool, invalid arguments).
def execute_fetch_url(arguments):
"""Fetch content from a URL."""
url = arguments.get("url")
try:
import urllib.request
with urllib.request.urlopen(url, timeout=10) as response:
content = response.read().decode("utf-8")
return text_result(content[:1000]) # First 1000 chars
except urllib.error.HTTPError as e:
return error_result(f"HTTP {e.code}: {e.reason}")
except urllib.error.URLError as e:
return error_result(f"Failed to connect: {e.reason}")
except Exception as e:
return error_result(f"Unexpected error: {str(e)}")
Complete Response Examples
Successful Text Result
{
"jsonrpc": "2.0",
"id": 5,
"result": {
"content": [
{
"type": "text",
"text": "The sum of 10 and 25 is 35"
}
]
}
}
Error Result
{
"jsonrpc": "2.0",
"id": 6,
"result": {
"content": [
{
"type": "text",
"text": "Error: Cannot divide by zero"
}
],
"isError": true
}
}
Always return meaningful error messages. The AI model uses these to understand what went wrong and potentially retry with different arguments or inform the user.