Перейти к содержимому

Ruby SDK

Официальный Ruby SDK для Model Context Protocol — создание MCP-серверов с поддержкой Rails и Rack

Официальный Ruby SDK для Model Context Protocol предоставляет полноценную поддержку создания MCP-серверов и клиентов с интеграцией в Rails и Rack-приложения.

  • JSON-RPC 2.0 обработка сообщений
  • Инициализация протокола и согласование capabilities
  • Регистрация и вызов инструментов (tools)
  • Регистрация и выполнение prompts
  • Регистрация и получение ресурсов (resources)
  • Транспорты: stdio и Streamable HTTP (включая SSE)
  • Уведомления об изменениях списков
gem 'mcp'
Окно терминала
bundle install
require "mcp"
server = MCP::Server.new(
name: "my_server",
title: "Мой MCP Сервер",
version: "1.0.0",
instructions: "Используйте инструменты этого сервера"
)
class McpController < ApplicationController
def index
server = MCP::Server.new(
name: "my_server",
title: "Rails MCP Server",
version: "1.0.0",
tools: [WeatherTool, CalculatorTool],
prompts: [GreetingPrompt],
server_context: { user_id: current_user.id }
)
render json: server.handle_json(request.body.read)
end
end
require "mcp"
server = MCP::Server.new(
name: "example_server",
tools: [ExampleTool]
)
transport = MCP::Server::Transports::StdioTransport.new(server)
transport.open
class WeatherTool < MCP::Tool
title "Погода"
description "Получает текущую погоду для указанной локации"
input_schema(
properties: {
location: { type: "string", description: "Город" },
units: { type: "string", enum: ["metric", "imperial"] }
},
required: ["location"]
)
output_schema(
properties: {
temperature: { type: "number" },
condition: { type: "string" },
humidity: { type: "integer" }
},
required: ["temperature", "condition", "humidity"]
)
annotations(
read_only_hint: true,
destructive_hint: false,
idempotent_hint: true
)
def self.call(location:, units: "metric", server_context:)
weather = fetch_weather(location, units)
MCP::Tool::Response.new(
[{ type: "text", text: weather.to_json }],
structured_content: weather
)
end
end
tool = MCP::Tool.define(
name: "calculator",
title: "Калькулятор",
description: "Выполняет математические вычисления"
) do |args, server_context:|
expression = args["expression"]
result = eval(expression) # Используйте безопасный парсер!
MCP::Tool::Response.new([{
type: "text",
text: "Результат: #{result}"
}])
end
server.define_tool(
name: "echo",
description: "Возвращает переданное сообщение"
) do |args, server_context:|
MCP::Tool::Response.new([{
type: "text",
text: args["message"]
}])
end
class GreetingPrompt < MCP::Prompt
prompt_name "greeting"
title "Приветствие"
description "Генерирует приветственное сообщение"
arguments [
MCP::Prompt::Argument.new(
name: "name",
title: "Имя",
description: "Имя для приветствия",
required: true
)
]
def self.template(args, server_context:)
MCP::Prompt::Result.new(
description: "Приветствие для #{args['name']}",
messages: [
MCP::Prompt::Message.new(
role: "user",
content: MCP::Content::Text.new("Привет!")
),
MCP::Prompt::Message.new(
role: "assistant",
content: MCP::Content::Text.new("Привет, #{args['name']}!")
)
]
)
end
end
# Определение ресурса
resource = MCP::Resource.new(
uri: "https://example.com/docs",
name: "documentation",
title: "Документация",
description: "Техническая документация",
mime_type: "text/html"
)
server = MCP::Server.new(
name: "my_server",
resources: [resource]
)
# Обработчик чтения ресурсов
server.resources_read_handler do |params|
[{
uri: params[:uri],
mimeType: "text/plain",
text: "Содержимое ресурса: #{params[:uri]}"
}]
end
# Создание сервера с Streamable HTTP
server = MCP::Server.new(name: "my_server")
transport = MCP::Server::Transports::StreamableHTTPTransport.new(server)
server.transport = transport
# Уведомление об изменении списка инструментов
server.define_tool(name: "new_tool") { |**args| { result: "ok" } }
server.notify_tools_list_changed
# Уведомление об изменении prompts
server.notify_prompts_list_changed
# Уведомление об изменении ресурсов
server.notify_resources_list_changed
# HTTP-клиент (требуется gem 'faraday')
http_transport = MCP::Client::HTTP.new(
url: "https://api.example.com/mcp",
headers: { "Authorization" => "Bearer token" }
)
client = MCP::Client.new(transport: http_transport)
# Список инструментов
tools = client.tools
tools.each do |tool|
puts "Tool: #{tool.name}"
puts "Description: #{tool.description}"
end
# Вызов инструмента
response = client.call_tool(
tool: tools.first,
arguments: { message: "Hello!" }
)
MCP.configure do |config|
# Обработчик исключений
config.exception_reporter = ->(exception, server_context) {
Bugsnag.notify(exception) do |report|
report.add_metadata(:mcp, server_context)
end
}
# Инструментирование
config.instrumentation_callback = ->(data) {
Rails.logger.info "MCP: #{data.inspect}"
# data содержит: method, tool_name, prompt_name,
# resource_uri, error, duration
}
# Версия протокола (опционально)
config.protocol_version = "2025-06-18"
end

server_context — хэш с контекстной информацией, передаваемой в tools, prompts и callbacks:

server = MCP::Server.new(
name: "my_server",
server_context: {
user_id: current_user.id,
request_id: request.uuid
}
)
# Доступен в инструментах
class MyTool < MCP::Tool
def self.call(server_context:, **args)
user = User.find(server_context[:user_id])
# ...
end
end
# Определение кастомного JSON-RPC метода
server.define_custom_method(method_name: "add") do |params|
params[:a] + params[:b]
end
# Запрос
# {"jsonrpc": "2.0", "id": 1, "method": "add", "params": {"a": 5, "b": 3}}
# Ответ
# {"jsonrpc": "2.0", "id": 1, "result": 8}
МетодОписание
initializeИнициализация протокола
pingПроверка доступности
tools/listСписок инструментов
tools/callВызов инструмента
prompts/listСписок prompts
prompts/getПолучение prompt
resources/listСписок ресурсов
resources/readЧтение ресурса
resources/templates/listСписок шаблонов ресурсов