Tools provide functionality to LLM applications. You can define them using three patterns:
- Class Definition: Subclass
MCP::Tool and implement a self.call method. This is the most robust way to define metadata like title, description, and input_schema. - Block Definition: Use
MCP::Tool.define(name: ..., description: ...) do ... end for a more concise functional approach. - Server-level Definition: Use
server.define_tool(name: ..., description: ...) do ... end to register a tool directly on an existing server instance.
Note on Arguments: Tool arguments are delivered as a Hash with symbol keys at every nesting level. When accessing nested objects, use payload[:key] instead of payload["key"].
# Class Definition
class MyTool < MCP::Tool
title "My Tool"
description "This tool performs specific functionality..."
input_schema(
properties: {
message: { type: "string" },
},
required: ["message"]
)
annotations(
read_only_hint: true,
destructive_hint: false,
)
def self.call(message:, server_context:)
MCP::Tool::Response.new([{ type: "text", text: "OK" }])
end
end
# Block Definition
tool = MCP::Tool.define(
name: "my_tool",
description: "This tool performs specific functionality...",
) do |args, server_context:|
MCP::Tool::Response.new([{ type: "text", text: "OK" }])
end
# Server-level Definition
server = MCP::Server.new
server.define_tool(
name: "my_tool",
description: "This tool performs specific functionality...",
) do |args, server_context:|
MCP::Tool::Response.new([{ type: "text", text: "OK" }])
end