-
Notifications
You must be signed in to change notification settings - Fork 4
Tools
The ModelContextProtocol::Server::Tool base class allows subclasses to define a tool that the MCP client can use.
Define the tool properties and schemas, then implement the call method to build your tool. Any arguments passed to the tool from the MCP client will be available in the arguments hash with symbol keys (e.g., arguments[:argument_name]), and any context values provided in the server configuration will be available in the context hash. Use the respond_with instance method to ensure your prompt responds with appropriately formatted response data.
You can also send MCP log messages to clients from within your tool by calling a valid logger level method on the client_logger and passing a string message. For server-side debugging and monitoring, use the server_logger to write logs that are not sent to clients.
Use the define block to set tool properties and configure schemas.
| Property | Description |
|---|---|
name |
The programmatic name of the tool |
title |
Human-readable display name |
description |
Short description of what the tool does |
input_schema |
JSON schema block for validating tool inputs |
output_schema |
JSON schema block for validating structured content outputs |
annotations |
Block using a DSL to define tool annotations (e.g., read_only_hint, destructive_hint, open_world_hint) |
security_schemes |
Block returning an array of security scheme hashes (e.g., noauth, oauth2) |
Define your tool properties and schemas, implement the call method using content helpers and respond_with to serialize responses. You can wrap long running operations in a cancellable block to allow clients to cancel the request. Also, you can automatically send progress notifications to clients by wrapping long-running operations in a progressable block.
| Method | Context | Description |
|---|---|---|
define |
Class definition | Block for defining tool metadata and schemas |
call |
Instance method | Main method to implement tool logic and build response |
cancellable |
Within call
|
Wrap long-running operations to allow client cancellation (e.g., cancellable { slow_operation }) |
progressable |
Within call
|
Wrap long-running operations to send clients progress notifications (e.g., progressable { slow_operation }) |
respond_with |
Within call
|
Return properly formatted response data with various content types |
Use content blocks to properly format the content included in tool responses.
| Method | Context | Description |
|---|---|---|
text_content |
Within call
|
Create text content block |
image_content |
Within call
|
Create image content block (requires data: and mime_type:) |
audio_content |
Within call
|
Create audio content block (requires data: and mime_type:) |
embedded_resource_content |
Within call
|
Create embedded resource content block (requires resource:) |
resource_link |
Within call
|
Create resource link content block (requires name: and uri:) |
Tools can return different types of responses using respond_with.
| Response Type | Usage | Description |
|---|---|---|
structured_content: |
respond_with structured_content: data |
Return structured data validated against output schema |
content: |
respond_with content: content_block |
Return single content block |
content: |
respond_with content: [content_blocks] |
Return array of mixed content blocks |
error: |
respond_with error: "message" |
Return tool error response |
Arguments from MCP clients and server context are available, along with logging capabilities.
| Variable | Context | Description |
|---|---|---|
arguments |
Within call
|
Hash containing client-provided arguments (symbol keys) |
context |
Within call
|
Hash containing server configuration context values |
client_logger |
Within call
|
Client logger instance for sending MCP log messages (e.g., client_logger.info("message")) |
server_logger |
Within call
|
Server logger instance for debugging and monitoring (e.g., server_logger.debug("message")) |
This is an example of a tool that returns structured content validated by an output schema:
class TestToolWithStructuredContentResponse < ModelContextProtocol::Server::Tool
define do
# The name of the tool for programmatic use
name "get_weather_data"
# The human-readable tool name for display in UI
title "Weather Data Retriever"
# A short description of what the tool does
description "Get current weather data for a location"
# The JSON schema for validating tool inputs
input_schema do
{
type: "object",
properties: {
location: {
type: "string",
description: "City name or zip code"
}
},
required: ["location"]
}
end
# The JSON schema for validating structured content
output_schema do
{
type: "object",
properties: {
temperature: {
type: "number",
description: "Temperature in celsius"
},
conditions: {
type: "string",
description: "Weather conditions description"
},
humidity: {
type: "number",
description: "Humidity percentage"
}
},
required: ["temperature", "conditions", "humidity"]
}
end
end
def call
# Use values provided by the server as context
user_id = context[:user_id]
client_logger.info("Initiating request for user #{user_id}...")
# Use values provided by clients as tool arguments
location = arguments[:location]
client_logger.info("Getting weather data for #{location}...")
# Returns a hash that validates against the output schema
weather_data = get_weather_data(location)
# Respond with structured content
respond_with structured_content: weather_data
end
private
# Simulate calling an external API to get weather data for the provided input
def get_weather_data(location)
{
temperature: 22.5,
conditions: "Partly cloudy",
humidity: 65
}
end
endThis is an example tool that returns a text response:
class TestToolWithTextResponse < ModelContextProtocol::Server::Tool
define do
name "double"
title "Number Doubler"
description "Doubles the provided number"
input_schema do
{
type: "object",
properties: {
number: {
type: "string"
}
},
required: ["number"]
}
end
end
def call
client_logger.info("Silly user doesn't know how to double a number")
number = arguments[:number].to_i
calculation = number * 2
user_id = context[:user_id]
salutation = user_id ? "User #{user_id}, " : ""
text_content = text_content(text: salutation << "#{number} doubled is #{calculation}")
respond_with content: text_content
end
endThis is an example of a tool that returns an image:
class TestToolWithImageResponse < ModelContextProtocol::Server::Tool
define do
name "custom-chart-generator"
description "Generates a chart in various formats"
input_schema do
{
type: "object",
properties: {
chart_type: {
type: "string",
description: "Type of chart (pie, bar, line)"
},
format: {
type: "string",
description: "Image format (jpg, svg, etc)"
}
},
required: ["chart_type", "format"]
}
end
end
def call
# Map format to mime type
mime_type = case arguments[:format].downcase
when "svg"
"image/svg+xml"
when "jpg", "jpeg"
"image/jpeg"
else
"image/png"
end
# In a real implementation, we would generate an actual chart
# This is a small valid base64 encoded string (represents "test")
data = "dGVzdA=="
image_content = image_content(data:, mime_type:)
respond_with content: image_content
end
endThis is an example of a tool that returns an embedded resource response:
class TestToolWithResourceResponse < ModelContextProtocol::Server::Tool
define do
name "resource-finder"
description "Finds a resource given a name"
input_schema do
{
type: "object",
properties: {
name: {
type: "string",
description: "The name of the resource"
}
},
required: ["name"]
}
end
end
RESOURCE_MAPPINGS = {
test_annotated_resource: TestAnnotatedResource,
test_binary_resource: TestBinaryResource,
test_resource: TestResource
}.freeze
def call
name = arguments[:name]
resource_klass = RESOURCE_MAPPINGS[name.downcase.to_sym]
unless resource_klass
return respond_with :error, text: "Resource `#{name}` not found"
end
resource_data = resource_klass.call(client_logger, context)
respond_with content: embedded_resource_content(resource: resource_data)
end
endThis is an example of a tool that returns mixed content:
class TestToolWithMixedContentResponse < ModelContextProtocol::Server::Tool
define do
name "get_temperature_history"
description "Gets comprehensive temperature history for a zip code"
input_schema do
{
type: "object",
properties: {
zip: {
type: "string"
}
},
required: ["zip"]
}
end
end
def call
client_logger.info("Getting comprehensive temperature history data")
zip = arguments[:zip]
temperature_history = retrieve_temperature_history(zip:)
temperature_history_block = text_content(text: temperature_history.join(", "))
temperature_chart = generate_weather_history_chart(temperature_history)
temperature_chart_block = image_content(
data: temperature_chart[:base64_chart_data],
mime_type: temperature_chart[:mime_type]
)
respond_with content: [temperature_history_block, temperature_chart_block]
end
private
def retrieve_temperature_history(zip:)
# Simulates a call to an API or DB to retrieve weather history
[85.2, 87.4, 89.0, 95.3, 96.0]
end
def generate_weather_history_chart(history)
# SImulate a call to generate a chart given the weather history
{
base64_chart_data: "dGVzdA==",
mime_type: "image/png"
}
end
endThis is an example of a tool that returns a tool error response:
class TestToolWithToolErrorResponse < ModelContextProtocol::Server::Tool
define do
name "api-caller"
description "Makes calls to external APIs"
input_schema do
{
type: "object",
properties: {
api_endpoint: {
type: "string",
description: "API endpoint URL"
},
method: {
type: "string",
description: "HTTP method (GET, POST, etc)"
}
},
required: ["api_endpoint", "method"]
}
end
end
def call
# Simulate an API call failure
respond_with error: "Failed to call API at #{arguments[:api_endpoint]}: Connection timed out"
end
endThis is an example of a tool that allows a client to cancel a long-running operation:
class TestToolWithCancellableSleep < ModelContextProtocol::Server::Tool
define do
name "cancellable_sleep"
title "Cancellable Sleep Tool"
description "Sleep for 3 seconds with cancellation support"
input_schema do
{
type: "object",
properties: {},
additionalProperties: false
}
end
end
def call
client_logger.info("Starting 3 second sleep operation")
result = cancellable do
sleep 3
"Sleep completed successfully"
end
respond_with content: text_content(text: result)
end
endThis is an example of a tool that automatically sends progress notifications to the client and allows the client to cancel the operation:
class TestToolWithProgressableAndCancellable < ModelContextProtocol::Server::Tool
define do
name "test_tool_with_progressable_and_cancellable"
description "A test tool that demonstrates combined progressable and cancellable functionality"
input_schema do
{
type: "object",
properties: {
max_duration: {
type: "number",
description: "Expected maximum duration in seconds"
},
work_steps: {
type: "number",
description: "Number of work steps to perform"
}
},
required: ["max_duration"]
}
end
end
def call
max_duration = arguments[:max_duration] || 10
work_steps = arguments[:work_steps] || 10
client_logger.info("Starting progressable call with max_duration=#{max_duration}, work_steps=#{work_steps}")
result = progressable(max_duration:, message: "Processing #{work_steps} items") do
cancellable do
processed_items = []
work_steps.times do |i|
sleep(max_duration / work_steps.to_f)
processed_items << "item_#{i + 1}"
end
processed_items
end
end
response = text_content(text: "Successfully processed #{result.length} items: #{result.join(", ")}")
respond_with content: response
end
endTools can use annotations to provide hints about their behavior to clients. The annotations block uses a validated DSL with the following methods:
| Method | Type | Description |
|---|---|---|
read_only_hint |
Boolean | If true, the tool does not modify its environment (default: false) |
destructive_hint |
Boolean | If true, the tool may perform destructive updates (default: true). Only meaningful when read_only_hint is false
|
idempotent_hint |
Boolean | If true, calling the tool repeatedly with the same arguments has no additional effect (default: false). Only meaningful when read_only_hint is false
|
open_world_hint |
Boolean | If true, the tool may interact with external entities (default: true) |
class TestToolWithAnnotations < ModelContextProtocol::Server::Tool
define do
name "fetch"
description "Fetch the full contents of a single resource"
input_schema do
{
type: "object",
properties: {
id: {
type: "string",
description: "Unique identifier of the resource to fetch"
}
},
required: ["id"]
}
end
annotations do
read_only_hint true
open_world_hint false
end
end
def call
id = arguments[:id]
client_logger.info("Fetching resource #{id}")
respond_with content: text_content(text: "Contents of resource #{id}")
end
endThis is an example of a tool that declares security schemes to indicate authentication requirements:
class TestToolWithSecuritySchemes < ModelContextProtocol::Server::Tool
define do
name "search"
description "Search indexed documents"
input_schema do
{
type: "object",
properties: {
q: {
type: "string",
description: "Search query"
}
},
required: ["q"]
}
end
security_schemes do
[
{type: "noauth"},
{type: "oauth2", scopes: ["search.read"]}
]
end
end
def call
query = arguments[:q]
client_logger.info("Searching for: #{query}")
respond_with content: text_content(text: "Results for '#{query}'")
end
endGetting Started
Server Guide
Handlers
Testing