The tools/call Method
Learn how to receive tool calls and validate arguments.
Executing Tools
After a client discovers your tools via tools/list, it can
execute them using tools/call. This is where your tool logic
actually runs.
Client Server
| |
|--- tools/call ------------------> |
| name: "add" |
| arguments: {a: 5, b: 3} |
| |
| [executes] |
| |
|<-- result: 8 -------------------- |
| |
The tools/call Request
The request includes the tool name and arguments:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "add",
"arguments": {
"a": 5,
"b": 3
}
}
}
Request Parameters
| Field | Type | Description |
|---|---|---|
name |
string | The name of the tool to execute |
arguments |
object | Arguments matching the tool's inputSchema |
Implementing tools/call
Here's a complete implementation that handles tool calls:
"""
Tool execution handlers for MCP server.
"""
# Tool implementations - the actual logic
def execute_add(arguments):
"""Add two numbers."""
a = arguments.get("a")
b = arguments.get("b")
return a + b
def execute_subtract(arguments):
"""Subtract two numbers."""
a = arguments.get("a")
b = arguments.get("b")
return a - b
def execute_multiply(arguments):
"""Multiply two numbers."""
a = arguments.get("a")
b = arguments.get("b")
return a * b
# Map tool names to their implementations
TOOL_HANDLERS = {
"add": execute_add,
"subtract": execute_subtract,
"multiply": execute_multiply,
}
def handle_tools_call(request_id, params):
"""
Handle the tools/call method.
Args:
request_id: The JSON-RPC request ID
params: Request parameters with 'name' and 'arguments'
Returns:
JSON-RPC response with tool result or error
"""
tool_name = params.get("name", "")
arguments = params.get("arguments", {})
# Check if tool exists
if tool_name not in TOOL_HANDLERS:
return {
"jsonrpc": "2.0",
"id": request_id,
"error": {
"code": -32602,
"message": f"Unknown tool: {tool_name}"
}
}
try:
# Execute the tool
handler = TOOL_HANDLERS[tool_name]
result = handler(arguments)
# Return success response
return {
"jsonrpc": "2.0",
"id": request_id,
"result": {
"content": [
{
"type": "text",
"text": str(result)
}
]
}
}
except Exception as e:
# Return error in tool result (not JSON-RPC error)
return {
"jsonrpc": "2.0",
"id": request_id,
"result": {
"content": [
{
"type": "text",
"text": f"Error: {str(e)}"
}
],
"isError": True
}
}
Validating Arguments
It's good practice to validate arguments before executing tools. Here's how to add validation:
def validate_arguments(tool_name, arguments):
"""
Validate arguments against the tool's schema.
Returns:
tuple: (is_valid, error_message)
"""
# Find the tool definition
tool = None
for t in TOOLS:
if t["name"] == tool_name:
tool = t
break
if not tool:
return False, f"Unknown tool: {tool_name}"
schema = tool.get("inputSchema", {})
required = schema.get("required", [])
properties = schema.get("properties", {})
# Check required fields
for field in required:
if field not in arguments:
return False, f"Missing required argument: {field}"
# Check types
for field, value in arguments.items():
if field in properties:
expected_type = properties[field].get("type")
if expected_type == "number" and not isinstance(value, (int, float)):
return False, f"Argument '{field}' must be a number"
if expected_type == "string" and not isinstance(value, str):
return False, f"Argument '{field}' must be a string"
if expected_type == "boolean" and not isinstance(value, bool):
return False, f"Argument '{field}' must be a boolean"
return True, None
def handle_tools_call_with_validation(request_id, params):
"""Handle tools/call with argument validation."""
tool_name = params.get("name", "")
arguments = params.get("arguments", {})
# Validate arguments
is_valid, error = validate_arguments(tool_name, arguments)
if not is_valid:
return {
"jsonrpc": "2.0",
"id": request_id,
"error": {
"code": -32602,
"message": error
}
}
# ... rest of execution logic ...
For production servers, consider using the jsonschema library
for robust validation: pip install jsonschema
Error Handling
There are two types of errors to handle:
| Error Type | When to Use | How to Return |
|---|---|---|
| JSON-RPC Error | Invalid request (unknown tool, bad params) | Use error field in response |
| Tool Error | Tool executed but failed (API error, etc.) | Use isError: true in result |
// JSON-RPC Error (invalid request)
{
"jsonrpc": "2.0",
"id": 3,
"error": {
"code": -32602,
"message": "Unknown tool: nonexistent"
}
}
// Tool Error (execution failed)
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "Error: Division by zero"
}
],
"isError": true
}
}
Testing tools/call
Test your implementation with curl:
# Call the add tool
curl -X POST http://localhost:8080 \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "add",
"arguments": {"a": 5, "b": 3}
}
}'
# Expected response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{"type": "text", "text": "8"}
]
}
}