modelcontextprotocol/ruby-sdk
GitHub: modelcontextprotocol/ruby-sdk
Model Context Protocol 的官方 Ruby SDK,提供完整的 MCP 服务器和客户端实现,帮助 Ruby/Rails 应用与 LLM 工具链无缝集成。
Stars: 805 | Forks: 110
# MCP Ruby SDK [](https://rubygems.org/gems/mcp) [](https://github.com/modelcontextprotocol/ruby-sdk/blob/main/LICENSE) [](https://github.com/modelcontextprotocol/ruby-sdk/actions/workflows/ci.yml)
Model Context Protocol 服务器和客户端的官方 Ruby SDK。
## 安装
将以下行添加到您的应用程序的 Gemfile 中:
```
gem 'mcp'
```
然后执行:
```
$ bundle install
```
或者自行安装:
```
$ gem install mcp
```
您可能需要根据您希望访问的功能添加额外的依赖项。
## 构建 MCP 服务器
`MCP::Server` 类是处理 JSON-RPC 请求和响应的核心组件。
它实现了 Model Context Protocol 规范,处理模型上下文请求和响应。
### 主要功能
- 实现 JSON-RPC 2.0 消息处理
- 支持协议初始化和能力协商
- 管理 tool 注册和调用
- 支持 prompt 注册和执行
- 支持 resource 注册和检索
- 支持 stdio 和 Streamable HTTP(包括 SSE)传输
- 支持列表更改(tools、prompts、resources)的通知
- 支持 roots(服务器到客户端的文件系统边界查询)
- 支持 sampling(服务器到客户端的 LLM 补全请求)
- 支持列表操作的基于游标的分页
- 支持服务器端取消正在进行的请求(notifications/cancelled)
### 支持的方法
- `initialize` - 初始化协议并返回服务器能力
- `ping` - 简单的健康检查
- `tools/list` - 列出所有已注册的 tools 及其 schemas
- `tools/call` - 使用提供的参数调用特定的 tool
- `prompts/list` - 列出所有已注册的 prompts 及其 schemas
- `prompts/get` - 按名称检索特定的 prompt
- `resources/list` - 列出所有已注册的 resources 及其 schemas
- `resources/read` - 按名称检索特定的 resource
- `resources/templates/list` - 列出所有已注册的 resource 模板及其 schemas
- `resources/subscribe` - 订阅特定 resource 的更新
- `resources/unsubscribe` - 取消订阅特定 resource 的更新
- `completion/complete` - 返回 prompt 参数和 resource URI 的自动补全建议
- `roots/list` - 从客户端请求文件系统 roots(服务器到客户端)
- `sampling/createMessage` - 从客户端请求 LLM 补全(服务器到客户端)
- `elicitation/create` - 从客户端请求用户输入(服务器到客户端)
### 用法
#### Stdio 传输
如果您想构建本地命令行应用程序,可以使用 stdio 传输:
```
require "mcp"
# 创建一个简单的工具
class ExampleTool < MCP::Tool
description "A simple example tool that echoes back its arguments"
input_schema(
properties: {
message: { type: "string" },
},
required: ["message"]
)
class << self
def call(message:, server_context:)
MCP::Tool::Response.new([{
type: "text",
text: "Hello from example tool! Message: #{message}",
}])
end
end
end
# 设置服务器
server = MCP::Server.new(
name: "example_server",
tools: [ExampleTool],
)
# 创建并启动 transport
transport = MCP::Server::Transports::StdioTransport.new(server)
transport.open
```
您可以运行此脚本,然后在命令行向服务器输入请求。
```
$ ruby examples/stdio_server.rb
{"jsonrpc":"2.0","id":"1","method":"ping"}
{"jsonrpc":"2.0","id":"2","method":"tools/list"}
{"jsonrpc":"2.0","id":"3","method":"tools/call","params":{"name":"example_tool","arguments":{"message":"Hello"}}}
```
#### Streamable HTTP 传输
`MCP::Server::Transports::StreamableHTTPTransport` 是一个标准的 Rack 应用,因此它可以挂载到任何兼容 Rack 的框架中。
以下示例展示了在 Rails 中的两种常见集成方式。
##### Rails(挂载)
`StreamableHTTPTransport` 是一个 Rack 应用,可以直接在 Rails 路由中挂载:
```
# config/routes.rb
server = MCP::Server.new(
name: "my_server",
title: "Example Server Display Name",
version: "1.0.0",
instructions: "Use the tools of this server as a last resort",
tools: [SomeTool, AnotherTool],
prompts: [MyPrompt],
)
transport = MCP::Server::Transports::StreamableHTTPTransport.new(server)
Rails.application.routes.draw do
mount transport => "/mcp"
end
```
`mount` 将 `/mcp` 上的所有 HTTP 方法定向到传输层。`StreamableHTTPTransport` 内部根据 [MCP Streamable HTTP 传输规范](https://modelcontextprotocol.io/specification/latest/basic/transports#streamable-http) 分发 `POST`(客户端到服务器的 JSON-RPC 消息,其响应可选通过 SSE 流式传输)、`GET`(可选的独立 SSE 流,用于服务器到客户端消息)和 `DELETE`(会话终止),因此不需要额外的路由配置。
##### Rails(控制器)
虽然 mount 方法在启动时创建单个服务器,但控制器方法会为每个请求创建一个新服务器。
这允许您根据请求自定义 tools、prompts 或配置(例如,不同的路由使用不同的 tools)。
`StreamableHTTPTransport#handle_request` 返回正确的 HTTP 状态码(例如,通知返回 202 Accepted):
```
class McpController < ActionController::API
def create
server = MCP::Server.new(
name: "my_server",
title: "Example Server Display Name",
version: "1.0.0",
instructions: "Use the tools of this server as a last resort",
tools: [SomeTool, AnotherTool],
prompts: [MyPrompt],
server_context: { user_id: current_user.id },
)
# Since the `MCP-Session-Id` is not shared across requests, `stateless: true` is set.
transport = MCP::Server::Transports::StreamableHTTPTransport.new(server, stateless: true)
status, headers, body = transport.handle_request(request)
render(json: body.first, status: status, headers: headers)
end
end
```
### 配置
可以使用 `MCP.configure` 块配置该 gem:
```
MCP.configure do |config|
config.exception_reporter = ->(exception, server_context) {
# Your exception reporting logic here
# For example with Bugsnag:
Bugsnag.notify(exception) do |report|
report.add_metadata(:model_context_protocol, server_context)
end
}
config.around_request = ->(data, &request_handler) {
logger.info("Start: #{data[:method]}")
request_handler.call
logger.info("Done: #{data[:method]}, tool: #{data[:tool_name]}")
}
end
```
或者通过创建显式配置并将其传递给服务器。
这对于一个应用程序托管多个 MCP 服务器但它们可能需要不同配置的系统非常有用。
```
configuration = MCP::Configuration.new
configuration.exception_reporter = ->(exception, server_context) {
# Your exception reporting logic here
# For example with Bugsnag:
Bugsnag.notify(exception) do |report|
report.add_metadata(:model_context_protocol, server_context)
end
}
configuration.around_request = ->(data, &request_handler) {
logger.info("Start: #{data[:method]}")
request_handler.call
logger.info("Done: #{data[:method]}, tool: #{data[:tool_name]}")
}
server = MCP::Server.new(
# ... all other options
configuration:,
)
```
### 服务器上下文和配置块数据
#### `server_context`
`server_context` 是一个传递给服务器实例并供 tool 和 prompt 调用使用的用户自定义哈希。
它可用于提供上下文信息,如身份验证状态、用户 ID 或特定于请求的数据。
**类型:**
```
server_context: { [String, Symbol] => Any }
```
**示例:**
```
server = MCP::Server.new(
name: "my_server",
server_context: { user_id: current_user.id, request_id: request.uuid }
)
```
这个哈希随后将作为 `server_context` 关键字参数传递给 tool 和 prompt 调用。
请注意,exception 和 instrumentation 回调不会接收此用户自定义哈希。
有关它们接收的参数,请参阅下面的相关部分。
#### 特定于请求的 `_meta` 参数
MCP 协议在请求中支持一个特殊的 [`_meta` 参数](https://modelcontextprotocol.io/specification/2025-06-18/basic#general-fields),允许客户端传递特定于请求的元数据。服务器会自动提取此参数,并将其作为 `server_context` 中的嵌套字段提供给 tools 和 prompts。
**访问模式:**
当客户端在请求参数中包含 `_meta` 时,它会作为 `server_context[:_meta]` 可用:
```
class MyTool < MCP::Tool
def self.call(message:, server_context:)
# Access provider-specific metadata
session_id = server_context.dig(:_meta, :session_id)
request_id = server_context.dig(:_meta, :request_id)
# Access server's original context
user_id = server_context.dig(:user_id)
MCP::Tool::Response.new([{
type: "text",
text: "Processing for user #{user_id} in session #{session_id}"
}])
end
end
```
**客户端请求示例:**
```
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "my_tool",
"arguments": { "message": "Hello" },
"_meta": {
"session_id": "abc123",
"request_id": "req_456"
}
}
}
```
#### 配置块数据
##### 异常报告器
异常报告器接收:
- `exception`:引发的 Ruby 异常对象
- `server_context`:一个描述失败发生位置的哈希(例如,用于请求处理的 `{ request: <原始 JSON-RPC 请求> }`,用于通知传递的 `{ notification: "tools_list_changed" }`)。
这不是传递给 `Server.new` 的用户自定义 `server_context`。
**签名:**
```
exception_reporter = ->(exception, server_context) { ... }
```
##### 包裹请求
`around_request` 钩子包裹请求处理,允许您在每个请求之前和之后执行代码。
这对于应用性能监控 (APM) 追踪、日志记录或其他可观测性需求非常有用。
该钩子接收一个 `data` 哈希和一个 `request_handler` 块。您必须调用 `request_handler.call` 来执行请求:
**签名:**
```
around_request = ->(data, &request_handler) { request_handler.call }
```
**`data` 按时间点的可用性:**
- 在 `request_handler.call` 之前:`method`
- 在 `request_handler.call` 之后:`tool_name`、`tool_arguments`、`prompt_name`、`resource_uri`、`error`、`client`
- 在 `around_request` 内部不可用:`duration`(在 `around_request` 返回后添加)
**示例:**
```
MCP.configure do |config|
config.around_request = ->(data, &request_handler) {
logger.info("Start: #{data[:method]}")
request_handler.call
logger.info("Done: #{data[:method]}, tool: #{data[:tool_name]}")
}
end
```
##### Instrumentation 回调(软弃用)
Instrumentation 回调在每个请求完成后调用,无论成功还是出错。
它接收一个包含以下可能键的哈希:
- `method`:(String) 调用的协议方法(例如,"ping"、"tools/list")
- `tool_name`:(String, 可选) 调用的 tool 的名称
- `tool_arguments`:(Hash, 可选) 传递给 tool 的参数
- `prompt_name`:(String, 可选) 调用的 prompt 的名称
- `resource_uri`:(String, 可选) 调用的 resource 的 URI
- `error`:(String, 可选) 查找失败时的错误代码
- `duration`:(Float) 调用持续时间(以秒为单位)
- `client`:(Hash, 可选) 客户端信息,包含来自初始化请求的 `name` 和 `version` 键
**签名:**
```
instrumentation_callback = ->(data) { ... }
```
### 服务器协议版本
可以使用 `protocol_version` 关键字参数覆盖服务器的协议版本:
```
configuration = MCP::Configuration.new(protocol_version: "2024-11-05")
MCP::Server.new(name: "test_server", configuration: configuration)
```
如果未指定协议版本,默认将应用最新的稳定版本。
最新的稳定版本包含来自[草案版本](https://modelcontextprotocol.io/specification/draft)的新功能。
这将使所有新服务器实例使用指定的协议版本而不是默认版本。可以通过将其设置为 `nil` 将协议版本重置为默认值:
```
MCP::Configuration.new(protocol_version: nil)
```
如果设置了无效的 `protocol_version` 值,将引发 `ArgumentError`。
请务必查看 [MCP 规范](https://modelcontextprotocol.io/specification/versioning) 以了解协议版本,从而理解所设置版本支持的功能。
### 异常报告
异常报告器接收两个参数:
- `exception`:引发的 Ruby 异常对象
- `server_context`:一个包含有关错误发生位置的上下文信息的哈希
server_context 哈希包括:
- 对于 tool 调用:`{ tool_name: "name", arguments: { ... } }`
- 对于一般请求处理:`{ request: { ... } }`
当异常发生时:
1. 异常通过配置的报告器进行报告
2. 对于 tool 调用,向客户端返回一般性错误响应:`{ error: "Internal error occurred", isError: true }`
3. 对于其他请求,异常在报告后重新引发
如果未配置异常报告器,则使用默认的空操作报告器,该报告器会静默忽略异常。
### Tools
MCP 规范包含 [Tools](https://modelcontextprotocol.io/specification/latest/server/tools),为 LLM 应用提供功能。
此 gem 提供了一个 `MCP::Tool` 类,可以通过三种方式创建 tools:
1. 作为类定义:
```
class MyTool < MCP::Tool
title "My Tool"
description "This tool performs specific functionality..."
input_schema(
properties: {
message: { type: "string" },
},
required: ["message"]
)
output_schema(
properties: {
result: { type: "string" },
success: { type: "boolean" },
timestamp: { type: "string", format: "date-time" }
},
required: ["result", "success", "timestamp"]
)
annotations(
read_only_hint: true,
destructive_hint: false,
idempotent_hint: true,
open_world_hint: false,
title: "My Tool"
)
def self.call(message:, server_context:)
MCP::Tool::Response.new([{ type: "text", text: "OK" }])
end
end
tool = MyTool
```
2. 通过使用带有块的 `MCP::Tool.define` 方法:
```
tool = MCP::Tool.define(
name: "my_tool",
title: "My Tool",
description: "This tool performs specific functionality...",
annotations: {
read_only_hint: true,
title: "My Tool"
}
) do |args, server_context:|
MCP::Tool::Response.new([{ type: "text", text: "OK" }])
end
```
3. 通过使用带有块的 `MCP::Server#define_tool` 方法:
```
server = MCP::Server.new
server.define_tool(
name: "my_tool",
description: "This tool performs specific functionality...",
annotations: {
title: "My Tool",
read_only_hint: true
}
) do |args, server_context:|
Tool::Response.new([{ type: "text", text: "OK" }])
end
```
server_context 参数是传递给服务器的 server_context,可用于传递每个请求的信息,例如身份验证状态。
### Tool 注解
Tools 可以包含提供有关其行为额外元数据的注解。支持以下注解:
- `destructive_hint`:指示 tool 是否执行破坏性操作。默认为 true
- `idempotent_hint`:指示 tool 的操作是否是幂等的。默认为 false
- `open_world_hint`:指示 tool 是否在开放世界上下文中运行。默认为 true
- `read_only_hint`:指示 tool 是否仅读取数据(不修改状态)。默认为 false
- `title`:tool 的人类可读标题
可以通过使用 `annotations` 类方法的类定义,或在使用 `define` 方法定义 tool 时设置注解。
### Tool 输出 Schemas
Tools 可以选择定义一个 `output_schema` 来指定其结果的预期结构。这与 `input_schema` 的定义方式类似,可以通过三种方式使用:
1. **带有 output_schema 的类定义:**
```
class WeatherTool < MCP::Tool
tool_name "get_weather"
description "Get current weather for a location"
input_schema(
properties: {
location: { type: "string" },
units: { type: "string", enum: ["celsius", "fahrenheit"] }
},
required: ["location"]
)
output_schema(
properties: {
temperature: { type: "number" },
condition: { type: "string" },
humidity: { type: "integer" }
},
required: ["temperature", "condition", "humidity"]
)
def self.call(location:, units: "celsius", server_context:)
# Call weather API and structure the response
api_response = WeatherAPI.fetch(location, units)
weather_data = {
temperature: api_response.temp,
condition: api_response.description,
humidity: api_response.humidity_percent
}
output_schema.validate_result(weather_data)
MCP::Tool::Response.new([{
type: "text",
text: weather_data.to_json
}])
end
end
```
2. **使用带有 output_schema 的 Tool.define:**
```
tool = MCP::Tool.define(
name: "calculate_stats",
description: "Calculate statistics for a dataset",
input_schema: {
properties: {
numbers: { type: "array", items: { type: "number" } }
},
required: ["numbers"]
},
output_schema: {
properties: {
mean: { type: "number" },
median: { type: "number" },
count: { type: "integer" }
},
required: ["mean", "median", "count"]
}
) do |args, server_context:|
# Calculate statistics and validate against schema
MCP::Tool::Response.new([{ type: "text", text: "Statistics calculated" }])
end
```
3. **使用 OutputSchema 对象:**
```
class DataTool < MCP::Tool
output_schema MCP::Tool::OutputSchema.new(
properties: {
success: { type: "boolean" },
data: { type: "object" }
},
required: ["success"]
)
end
```
Output schema 也可以描述对象数组:
```
class WeatherTool < MCP::Tool
output_schema(
type: "array",
items: {
properties: {
temperature: { type: "number" },
condition: { type: "string" },
humidity: { type: "integer" }
},
required: ["temperature", "condition", "humidity"]
}
)
end
```
请注意:在这种情况下,您必须提供 `type: "array"`。output schema 的默认类型是 `object`。
[Output Schema](https://modelcontextprotocol.io/specification/latest/server/tools#output-schema) 的 MCP 规范规定:
- **服务器验证**:服务器必须提供符合 output schema 的结构化结果
- **客户端验证**:客户端应针对 output schema 验证结构化结果
- **更好的集成**:实现严格的 schema 验证、类型信息和改进的开发者体验
- **向后兼容性**:返回结构化内容的 Tools 也应包含 TextContent 块中序列化的 JSON
Output schema 遵循标准的 JSON Schema 格式,有助于确保 MCP 服务器和客户端之间一致的数据交换。
### 包含结构化内容的 Tool 响应
Tools 可以使用 `structured_content` 参数与文本内容一起返回结构化数据。
结构化内容将作为 `structuredContent` 字段包含在 JSON-RPC 响应中。
```
class WeatherTool < MCP::Tool
description "Get current weather and return structured data"
def self.call(location:, units: "celsius", server_context:)
# Call weather API and structure the response
api_response = WeatherAPI.fetch(location, units)
weather_data = {
temperature: api_response.temp,
condition: api_response.description,
humidity: api_response.humidity_percent
}
output_schema.validate_result(weather_data)
MCP::Tool::Response.new(
[{
type: "text",
text: weather_data.to_json
}],
structured_content: weather_data
)
end
end
```
### 包含错误的 Tool 响应
Tools 可以使用 `error` 参数与文本内容一起返回错误信息。
该错误将作为 `isError` 字段包含在 JSON-RPC 响应中。
```
class WeatherTool < MCP::Tool
description "Get current weather and return structured data"
def self.call(server_context:)
# Do something here
content = {}
MCP::Tool::Response.new(
[{
type: "text",
text: content.to_json
}],
structured_content: content,
error: true
)
end
end
```
### Prompts
MCP 规范包含 [Prompts](https://modelcontextprotocol.io/specification/latest/server/prompts),允许服务器定义可重用的 prompt 模板和工作流,客户端可以轻松将其呈现给用户和 LLM。
`MCP::Prompt` 类提供了三种创建 prompts 的方法:
1. 作为带有元数据的类定义:
```
class MyPrompt < MCP::Prompt
prompt_name "my_prompt" # Optional - defaults to underscored class name
title "My Prompt"
description "This prompt performs specific functionality..."
arguments [
MCP::Prompt::Argument.new(
name: "message",
title: "Message Title",
description: "Input message",
required: true
)
]
meta({ version: "1.0", category: "example" })
class << self
def template(args, server_context:)
MCP::Prompt::Result.new(
description: "Response description",
messages: [
MCP::Prompt::Message.new(
role: "user",
content: MCP::Content::Text.new("User message")
),
MCP::Prompt::Message.new(
role: "assistant",
content: MCP::Content::Text.new(args["message"])
)
]
)
end
end
end
prompt = MyPrompt
```
2. 使用 `MCP::Prompt.define` 方法:
```
prompt = MCP::Prompt.define(
name: "my_prompt",
title: "My Prompt",
description: "This prompt performs specific functionality...",
arguments: [
MCP::Prompt::Argument.new(
name: "message",
title: "Message Title",
description: "Input message",
required: true
)
],
meta: { version: "1.0", category: "example" }
) do |args, server_context:|
MCP::Prompt::Result.new(
description: "Response description",
messages: [
MCP::Prompt::Message.new(
role: "user",
content: MCP::Content::Text.new("User message")
),
MCP::Prompt::Message.new(
role: "assistant",
content: MCP::Content::Text.new(args["message"])
)
]
)
end
```
3. 使用 `MCP::Server#define_prompt` 方法:
```
server = MCP::Server.new
server.define_prompt(
name: "my_prompt",
description: "This prompt performs specific functionality...",
arguments: [
Prompt::Argument.new(
name: "message",
title: "Message Title",
description: "Input message",
required: true
)
],
meta: { version: "1.0", category: "example" }
) do |args, server_context:|
Prompt::Result.new(
description: "Response description",
messages: [
Prompt::Message.new(
role: "user",
content: Content::Text.new("User message")
),
Prompt::Message.new(
role: "assistant",
content: Content::Text.new(args["message"])
)
]
)
end
```
server_context 参数是传递给服务器的 server_context,可用于传递每个请求的信息,例如身份验证状态或用户偏好。
### 主要组件
- `MCP::Prompt::Argument` - 定义 prompt 模板的输入参数,包括名称、标题、描述和必填标志
- `MCP::Prompt::Message` - 表示对话中具有角色和内容的消息
- `MCP::Prompt::Result` - prompt 模板的输出,包含描述和消息
- `MCP::Content::Text` - 消息的文本内容
### 用法
在 MCP 服务器上注册 prompts:
```
server = MCP::Server.new(
name: "my_server",
prompts: [MyPrompt],
server_context: { user_id: current_user.id },
)
```
服务器将通过 MCP 协议方法处理 prompt 列表和执行:
- `prompts/list` - 列出所有已注册的 prompts 及其 schemas
- `prompts/get` - 使用参数检索并执行特定的 prompt
### ResourcesMCP 规范包含 [Resources](https://modelcontextprotocol.io/specification/latest/server/resources)。
### 读取 Resources
`MCP::Resource` 类提供了一种向服务器注册 resources 的方法。
```
resource = MCP::Resource.new(
uri: "https://example.com/my_resource",
name: "my-resource",
title: "My Resource",
description: "Lorem ipsum dolor sit amet",
mime_type: "text/html",
)
server = MCP::Server.new(
name: "my_server",
resources: [resource],
)
```
服务器必须注册一个用于 `resources/read` 方法的处理器才能动态检索 resource。
```
server.resources_read_handler do |params|
[{
uri: params[:uri],
mimeType: "text/plain",
text: "Hello from example resource! URI: #{params[:uri]}"
}]
end
```
否则 `resources/read` 请求将是一个空操作。
### Resource 模板
`MCP::ResourceTemplate` 类提供了一种向服务器注册 resource 模板的方法。
```
resource_template = MCP::ResourceTemplate.new(
uri_template: "https://example.com/my_resource_template",
name: "my-resource-template",
title: "My Resource Template",
description: "Lorem ipsum dolor sit amet",
mime_type: "text/html",
)
server = MCP::Server.new(
name: "my_server",
resource_templates: [resource_template],
)
```
### Roots
Model Context Protocol 允许服务器通过 `roots/list` 方法从客户端请求文件系统 roots。
Roots 定义了服务器可以操作的边界,提供客户端已使其可用的目录和文件列表。
**关键概念:**
- **服务器到客户端请求**:与 sampling 一样,roots 列表由服务器发起
- **客户端能力**:客户端必须在初始化期间声明 `roots` 能力
- **更改通知**:支持 `roots.listChanged` 的客户端在 roots 更改时发送 `notifications/roots/list_changed`
**在 Tools 中使用 Roots:**
接受 `server_context:` 参数的 Tools 可以在其上调用 `list_roots`。
该请求会自动路由到正确的客户端会话:
```
class FileSearchTool < MCP::Tool
description "Search files within the client's project roots"
input_schema(
properties: {
query: { type: "string" }
},
required: ["query"]
)
def self.call(query:, server_context:)
roots = server_context.list_roots
root_uris = roots[:roots].map { |root| root[:uri] }
MCP::Tool::Response.new([{
type: "text",
text: "Searching in roots: #{root_uris.join(", ")}"
}])
end
end
```
结果包含一个 root 对象数组:
```
{
roots: [
{ uri: "file:///home/user/projects/myproject", name: "My Project" },
{ uri: "file:///home/user/repos/backend", name: "Backend Repository" }
]
}
```
**处理 Root 更改:**
注册一个回调以在客户端的 roots 更改时收到通知:
```
server.roots_list_changed_handler do
puts "Client's roots have changed, tools will see updated roots on next call."
end
```
**错误处理:**
- 如果客户端不支持 `roots` 能力,则引发 `RuntimeError`
- 如果客户端返回错误响应,则引发 `StandardError`
### Resource 订阅
Resource 订阅允许客户端监视特定 resources 的更改。
当订阅的 resource 更新时,服务器会向客户端发送通知。
SDK 内部不跟踪订阅状态。
服务器开发人员注册处理器并管理他们自己的订阅状态。
提供了三种方法:
- `Server#resources_subscribe_handler` - 注册 `resources/subscribe` 请求的处理器
- `Server#resources_unsubscribe_handler` - 注册 `resources/unsubscribe` 请求的处理器
- `ServerContext#notify_resources_updated` - 向订阅的客户端发送 `notifications/resources/updated` 通知
```
subscribed_uris = Set.new
server = MCP::Server.new(
name: "my_server",
resources: [my_resource],
capabilities: { resources: { subscribe: true } },
)
server.resources_subscribe_handler do |params|
subscribed_uris.add(params[:uri].to_s)
end
server.resources_unsubscribe_handler do |params|
subscribed_uris.delete(params[:uri].to_s)
end
server.define_tool(name: "update_resource") do |server_context:, **args|
if subscribed_uris.include?("test://my-resource")
server_context.notify_resources_updated(uri: "test://my-resource")
end
MCP::Tool::Response.new([MCP::Content::Text.new("Resource updated").to_h])
end
```
### Sampling
Model Context Protocol 允许服务器通过 `sampling/createMessage` 方法从客户端请求 LLM 补全。
这使得服务器能够利用客户端的 LLM 能力,而无需直接访问 AI 模型。
**关键概念:**
- **服务器到客户端请求**:与典型的 MCP 方法(客户端到服务器)不同,sampling 由服务器发起
- **客户端能力**:客户端必须在初始化期间声明 `sampling` 能力
- **Tool 支持**:在 sampling 请求中使用 tools 时,客户端必须声明 `sampling.tools` 能力
- **人类参与循环**:客户端可以在将请求转发给 LLM 之前实现用户批准
**在 Tools 中使用 Sampling:**
接受 `server_context:` 参数的 Tools 可以在其上调用 `create_sampling_message`。
该请求会自动路由到正确的客户端会话:
```
class SummarizeTool < MCP::Tool
description "Summarize text using LLM"
input_schema(
properties: {
text: { type: "string" }
},
required: ["text"]
)
def self.call(text:, server_context:)
result = server_context.create_sampling_message(
messages: [
{ role: "user", content: { type: "text", text: "Please summarize: #{text}" } }
],
max_tokens: 500
)
MCP::Tool::Response.new([{
type: "text",
text: result[:content][:text]
}])
end
end
server = MCP::Server.new(name: "my_server", tools: [SummarizeTool])
```
**参数:**
必需:
- `messages:` (Array) - 带有 `role` 和 `content` 的消息对象数组
- `max_tokens:` (Integer) - 响应中的最大 token 数
可选:
- `system_prompt:` (String) - LLM 的系统 prompt
- `model_preferences:` (Hash) - 模型选择偏好(例如,`{ intelligencePriority: 0.8 }`)
- `include_context:` (String) - 上下文包含:`"none"`、`"thisServer"` 或 `"allServers"`(软弃用)
- `temperature:` (Float) - Sampling 温度
- `stop_sequences:` (Array) - 停止生成的序列
- `metadata:` (Hash) - 附加元数据
- `tools:` (Array) - LLM 可用的 tools(需要 `sampling.tools` 能力)
- `tool_choice:` (Hash) - Tool 选择模式(例如,`{ mode: "auto" }`)
**错误处理:**
- 如果客户端不支持 `sampling` 能力,则引发 `RuntimeError`
- 如果使用了 `tools` 但客户端缺少 `sampling.tools` 能力,则引发 `RuntimeError`
- 如果客户端返回错误响应,则引发 `StandardError`
### 通知
服务器支持在 tools、prompts 或 resources 列表更改时向客户端发送通知。这可以实现无需轮询的实时更新。
#### 通知方法
服务器提供以下通知方法:
- `notify_tools_list_changed` - 当 tools 列表更改时发送通知
- `notify_prompts_list_changed` - 当 prompts 列表更改时发送通知
- `notify_resources_list_changed` - 当 resources 列表更改时发送通知
- `notify_log_message` - 发送结构化日志通知消息
#### 会话作用域
在使用 Streamable HTTP 传输与多个客户端时,每个客户端连接都有自己的会话。通知的作用域如下:
- 通过 tool 处理器内的 `server_context` 调用的 **`report_progress`** 和 **`notify_log_message`** 会自动仅发送给请求客户端。无需额外配置。
- **`notify_tools_list_changed`**、**`notify_prompts_list_changed`** 和 **`notify_resources_list_changed`** 始终广播到所有连接的客户端,因为它们代表服务器范围的状态更改。应直接在 `server` 实例上调用它们。
#### 通知格式
通知遵循 JSON-RPC 2.0 规范并使用以下方法名称:
- `notifications/tools/list_changed`
- `notifications/prompts/list_changed`
- `notifications/resources/list_changed`
- `notifications/cancelled`
- `notifications/progress`
- `notifications/message`
### 取消
MCP Ruby SDK 支持服务器端处理 [MCP `notifications/cancelled` 实用程序](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/cancellation)。
当客户端为正在进行的请求发送 `notifications/cancelled` 时,服务器会协作停止处理并抑制该请求的 JSON-RPC 响应。
取消是协作式的:SDK 不会强制终止 tool 代码。相反,一个 `MCP::Cancellation` token 会贯穿 `server_context`,长时间运行的 tools 会轮询它以提前退出。当 tool 在观察到取消后返回时,服务器会抑制 JSON-RPC 响应,与规范匹配。根据规范,`initialize` 请求永远不可取消。
#### 服务器端:检查取消的处理器
任何选择使用 `server_context:` 的处理器——tools(`Tool.call`)、prompt 模板、`resources_read_handler`、`completion_handler`、`resources_subscribe_handler`、`resources_unsubscribe_handler` 和 `define_custom_method` 块——都会接收一个连接到正在进行的请求的取消 token 的 `MCP::ServerContext`。
处理器在其工作循环中检查 `cancelled?`,或调用 `raise_if_cancelled!` 在安全点引发 `MCP::CancelledError`:
```
class LongRunningTool < MCP::Tool
description "A tool that supports cancellation"
input_schema(properties: { count: { type: "integer" } }, required: ["count"])
def self.call(count:, server_context:)
count.times do |i|
# Exit early if the client has sent `notifications/cancelled`.
break if server_context.cancelled?
do_work(i)
end
MCP::Tool::Response.new([{ type: "text", text: "Done" }])
end
end
```
或者,使用 `raise_if_cancelled!` 在下一个安全点引发:
```
def self.call(count:, server_context:)
count.times do |i|
server_context.raise_if_cancelled!
do_work(i)
end
MCP::Tool::Response.new([{ type: "text", text: "Done" }])
end
```
当处理器观察到取消时(无论是通过 `cancelled?` 提前返回,还是通过 `raise_if_cancelled!` 引发 `MCP::CancelledError`),服务器都会丢弃响应,并且不会向客户端发送任何 JSON-RPC 结果。
相同的模式适用于其他处理器类型:
```
# resources/read
server.resources_read_handler do |params, server_context:|
server_context.raise_if_cancelled!
# read the resource
end
# completion/complete
server.completion_handler do |params, server_context:|
server_context.raise_if_cancelled!
# compute completions
end
# 自定义方法
server.define_custom_method(method_name: "custom/slow") do |params, server_context:|
server_context.raise_if_cancelled!
# do work
end
# prompts(通过 Prompt 子类)
class SlowPrompt < MCP::Prompt
prompt_name "slow_prompt"
def self.template(args, server_context:)
server_context.raise_if_cancelled!
MCP::Prompt::Result.new(messages: [])
end
end
```
未声明 `server_context:` 关键字的处理器继续正常工作——仅在块签名需要时,选择加入检测才会包裹上下文。
#### 嵌套的服务器到客户端请求会自动取消
当 tool 处理器正在等待嵌套的服务器到客户端请求(`server_context.create_sampling_message`、`create_form_elicitation` 或 `create_url_elicitation`)时,取消父 tool 调用会自动从嵌套调用中引发 `MCP::CancelledError`,因此 tool 不需要在其自己的 `cancelled?` 检查中包裹它:
```
def self.call(server_context:)
result = server_context.create_sampling_message(messages: messages, max_tokens: 100)
# If the parent tools/call is cancelled while waiting above, MCP::CancelledError
# is raised here and the tool can let it propagate or clean up as needed.
MCP::Tool::Response.new([{ type: "text", text: result[:content][:text] }])
rescue MCP::CancelledError
# Optional: run cleanup. Re-raising (or letting it propagate) is fine; the server
# will still suppress the JSON-RPC response per the MCP spec.
raise
end
```
嵌套取消传播仅在 `StreamableHTTPTransport` 上受支持。
`StdioTransport` 是单线程的,并会阻塞在 `$stdin.gets` 上,因此即使父 `tools/call` 被取消,tool 内部的嵌套 `server_context.create_sampling_message` 也会运行完成。父 tool 本身仍然通过嵌套调用之间的 `server_context.cancelled?` 观察取消。
### Ping
MCP Ruby SDK 支持 [MCP `ping` 实用程序](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/ping),它允许连接的任一端验证对端是否仍然响应。
`ping` 请求没有参数,接收方必须立即以空结果响应。
#### 服务器端
服务器会自动响应传入的 `ping` 请求——无需任何设置。
任何 `MCP::Server` 实例都会回复空结果。
#### 客户端
`MCP::Client` 暴露了 `ping` 方法,用于向服务器发送 ping:
```
client = MCP::Client.new(transport: transport)
client.ping # => {} on success
```
当服务器返回 JSON-RPC 错误时,`#ping` 会引发 `MCP::Client::ServerError`。
当响应 `result` 缺失或不是 Hash 时(符合规范要求的 `result` 必须是对象),它会引发 `MCP::Client::ValidationError`。
传输层错误(例如,`MCP::Client::Stdio` 的 `read_timeout:` 触发)作为传输层引发的异常进行传播。
### 进度
MCP Ruby SDK 遵循 [MCP Progress 规范](https://modelcontextprotocol.io/specification/latest/server/utilities/progress),支持长时间运行的 tool 操作的进度跟踪。
#### 进度如何工作
1. **客户端请求**:客户端在调用 tool 时在 `_meta` 字段中发送 `progressToken`
2. **服务器通知**:服务器在 tool 执行期间向客户端发送回 `notifications/progress` 消息
3. **Tool 集成**:Tools 调用 `server_context.report_progress` 以报告增量进度
#### 服务器端:带有进度的 Tool
接受 `server_context:` 参数的 Tools 可以在其上调用 `report_progress`。
服务器自动将上下文包裹在提供此方法的 `MCP::ServerContext` 实例中:
```
class LongRunningTool < MCP::Tool
description "A tool that reports progress during execution"
input_schema(
properties: {
count: { type: "integer" },
},
required: ["count"]
)
def self.call(count:, server_context:)
count.times do |i|
# Do work here.
server_context.report_progress(i + 1, total: count, message: "Processing item #{i + 1}")
end
MCP::Tool::Response.new([{ type: "text", text: "Done" }])
end
end
```
`server_context.report_progress` 方法接受:
- `progress`(必需)— 当前进度值(数字)
- `total:`(可选)— 预期总值,以便客户端可以显示百分比
- `message:`(可选)— 人类可读的状态消息
**主要特性:**
- Tools 通过 `server_context.report_progress` 报告进度
- 当客户端未提供 `progressToken` 时,`report_progress` 是一个空操作
- 支持数字和字符串进度 token
### 补全
MCP 规范包含 [Completions](https://modelcontextprotocol.io/specification/latest/server/utilities/completion),它允许服务器为 prompt 参数和 resource URI 提供自动补全建议。
要启用补全,请声明 `completions` 能力并注册一个处理器:
```
server = MCP::Server.new(
name: "my_server",
prompts: [CodeReviewPrompt],
resource_templates: [FileTemplate],
capabilities: { completions: {} },
)
server.completion_handler do |params|
ref = params[:ref]
argument = params[:argument]
value = argument[:value]
case ref[:type]
when "ref/prompt"
values = case argument[:name]
when "language"
["python", "pytorch", "pyside"].select { |v| v.start_with?(value) }
else
[]
end
{ completion: { values: values, hasMore: false } }
when "ref/resource"
{ completion: { values: [], hasMore: false } }
end
end
```
处理器接收一个包含以下内容的 `params` 哈希:
- `ref` - 引用(`{ type: "ref/prompt", name: "..." }` 或 `{ type: "ref/resource", uri: "..." }`)
- `argument` - 正在补全的参数(`{ name: "...", value: "..." }`)
- `context`(可选)- 先前已解析的参数(`{ arguments: { ... } }`)
处理器必须返回一个带有 `completion` 键的哈希,该键包含 `values`(字符串数组),以及可选的 `total` 和 `hasMore`。
SDK 根据 MCP 规范自动强制执行 100 项的限制。
服务器在调用处理器之前会验证所引用的 prompt、resource 或 resource 模板是否已注册。
对未知引用的请求将返回错误。
### Elicitation
MCP Ruby SDK 支持 [elicitation](https://modelcontextprotocol.io/specification/2025-11-25/client/elicitation),它允许服务器在 tool 执行期间通过客户端向用户请求额外信息。
Elicitation 是一种**服务器到客户端的请求**。服务器发送请求并阻塞,直到用户通过客户端响应。
#### 能力
客户端必须在初始化期间声明 `elicitation` 能力。服务器在发送任何 elicitation 请求之前会检查这一点,如果客户端不支持,则会引发 `RuntimeError`。
要支持 URL 模式,客户端还必须声明 `elicitation.url` 能力。
#### 在 Tools 中使用 Elicitation
接受 `server_context:` 参数的 Tools 可以在其上调用 `create_form_elicitation`:
```
server.define_tool(name: "collect_info", description: "Collect user info") do |server_context:|
result = server_context.create_form_elicitation(
message: "Please provide your name",
requested_schema: {
type: "object",
properties: { name: { type: "string" } },
required: ["name"],
},
)
MCP::Tool::Response.new([{ type: "text", text: "Hello, #{result[:content][:name]}" }])
end
```
#### 表单模式
表单模式直接通过 MCP 客户端从用户那里收集结构化数据:
```
server.define_tool(name: "collect_contact", description: "Collect contact info") do |server_context:|
result = server_context.create_form_elicitation(
message: "Please provide your contact information",
requested_schema: {
type: "object",
properties: {
name: { type: "string", description: "Your full name" },
email: { type: "string", format: "email", description: "Your email address" },
},
required: ["name", "email"],
},
)
text = case result[:action]
when "accept"
"Hello, #{result[:content][:name]} (#{result[:content][:email]})"
when "decline"
"User declined"
when "cancel"
"User cancelled"
end
MCP::Tool::Response.new([{ type: "text", text: text }])
end
```
#### URL 模式
URL 模式将用户引导至外部 URL 以进行带外交互,例如 OAuth 流程:
```
server.define_tool(name: "authorize_github", description: "Authorize GitHub") do |server_context:|
elicitation_id = SecureRandom.uuid
result = server_context.create_url_elicitation(
message: "Please authorize access to your GitHub account",
url: "https://example.com/oauth/authorize?elicitation_id=#{elicitation_id}",
elicitation_id: elicitation_id,
)
server_context.notify_elicitation_complete(elicitation_id: elicitation_id)
MCP::Tool::Response.new([{ type: "text", text: "Authorization complete" }])
end
```
#### URLElicitationRequiredError
当 tool 在完成带外 elicitation 之前无法继续时,引发 `MCP::Server::URLElicitationRequiredError`。
这会向客户端返回一个代码为 `-32042` 的 JSON-RPC 错误:
```
server.define_tool(name: "access_github", description: "Access GitHub") do |server_context:|
raise MCP::Server::URLElicitationRequiredError.new([
{
mode: "url",
elicitationId: SecureRandom.uuid,
url: "https://example.com/oauth/authorize",
message: "GitHub authorization is required.",
},
])
end
```
### 日志记录
MCP Ruby SDK 遵循 [MCP Logging 规范](https://modelcontextprotocol.io/specification/latest/server/utilities/logging),通过 `notify_log_message` 方法支持结构化日志记录。
`notifications/message` 通知用于客户端和服务器之间的结构化日志记录。
#### 日志级别
SDK 支持 8 个严重性递增的日志级别:
- `debug` 详细的调试信息
- `info` - 一般信息性消息
- `notice` - 正常但重要的事件
- `warning` - 警告条件
- `error` - 错误条件
- `critical` - 严重条件
- `alert` - 必须立即采取行动
- `emergency` - 系统不可用
#### 日志记录如何工作
1. **客户端配置**:客户端发送 `logging/setLevel` 请求以配置最低日志级别
2. **服务器过滤**:服务器仅发送配置级别或更高严重性的日志消息
3. **通知传递**:日志消息作为 `notifications/message` 发送到客户端
例如,如果客户端将级别设置为 `"error"`(严重性 4),服务器将发送以下级别的消息:`error`、`critical`、`alert` 和 `emergency`。
有关更多详细信息,请参阅 [MCP Logging 规范](https://modelcontextprotocol.io/specification/latest/server/utilities/logging)。
**用法示例:**
```
server = MCP::Server.new(name: "my_server")
transport = MCP::Server::Transports::StdioTransport.new(server)
# 客户端首先配置日志级别(在客户端):
transport.send_request(
request: {
jsonrpc: "2.0",
method: "logging/setLevel",
params: { level: "info" },
id: session_id # Unique request ID within the session
}
)
# 发送不同严重级别的日志消息
server.notify_log_message(
data: { message: "Application started successfully" },
level: "info"
)
server.notify_log_message(
data: { message: "Configuration file not found, using defaults" },
level: "warning"
)
server.notify_log_message(
data: {
error: "Database connection failed",
details: { host: "localhost", port: 5432 }
},
level: "error",
logger: "DatabaseLogger" # Optional logger name
)
```
**主要特性:**
- 支持基于 https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/logging#log-levels 的 8 个日志级别(debug、info、notice、warning、error、critical、alert、emergency)
- 服务器具有发送日志消息的 `logging` 能力
- 仅在配置了传输层时才发送消息
- 消息根据客户端配置的日志级别进行过滤
- 如果客户端未设置日志级别,则不会发送任何消息
#### 传输层支持
- **stdio**:通知作为 JSON-RPC 2.0 消息发送到 stdout
- **Streamable HTTP**:通知通过 HTTP 流式传输(分块传输或 SSE)作为 JSON-RPC 2.0 消息发送
#### 用法示例
```
server = MCP::Server.new(name: "my_server")
# 默认 Streamable HTTP - 面向会话
transport = MCP::Server::Transports::StreamableHTTPTransport.new(server)
# 当 tools 更改时,通知客户端
server.define_tool(name: "new_tool") { |**args| { result: "ok" } }
server.notify_tools_list_changed
```
您可以使用无状态的 Streamable HTTP,其中不支持通知,所有调用都是请求/响应交互。
此模式允许轻松的多节点部署。
在 `MCP::Server::Transports::StreamableHTTPTransport.new` 中设置 `stateless: true`(`stateless` 默认为 `false`):
```
# 无状态 Streamable HTTP - 无会话
transport = MCP::Server::Transports::StreamableHTTPTransport.new(server, stateless: true)
```
您可以启用 JSON 响应模式,其中服务器返回 `application/json` 而不是 `text/event-stream`。
在 `MCP::Server::Transports::StreamableHTTPTransport.new` 中设置 `enable_json_response: true`:
```
# JSON 响应模式
transport = MCP::Server::Transports::StreamableHTTPTransport.new(server, enable_json_response: true)
```
在 JSON 响应模式下,POST 响应是一个单一的 JSON 对象,因此在请求处理期间需要到达的服务器到客户端消息不受支持:请求范围的通知(`progress`、`log`)会被静默丢弃,并且所有服务器到客户端的请求(`sampling/createMessage`、`roots/list`、`elicitation/create`)都会引发错误。
会话范围的独立通知(`resources/updated`、`elicitation/complete`)和广播通知(`tools/list_changed` 等)仍然会流向连接到 GET SSE 流的客户端。
此模式适用于不需要服务器发起请求的简单 tool 服务器。
默认情况下,会话不会过期。为了降低会话劫持风险,您可以设置 `session_idle_timeout`(以秒为单位)。
配置后,在指定持续时间内未收到任何 HTTP 请求的会话将自动过期并清理:
```
# 30 分钟会话超时
transport = MCP::Server::Transports::StreamableHTTPTransport.new(server, session_idle_timeout: 1800)
```
### 分页
MCP Ruby SDK 为可能返回大型结果集的列表操作支持[分页](https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/pagination)。分页使用带有从零开始的偏移量的字符串游标 token,客户端将其视为不透明的:服务器决定页面大小,客户端跟随 `nextCursor` 直到服务器省略它。
分页适用于 `tools/list`、`prompts/list`、`resources/list` 和 `resources/templates/list`。
#### 服务器端:启用分页
将 `page_size:` 传递给 `MCP::Server.new` 以将列表响应拆分为多个页面。当省略 `page_size`(默认值)时,列表响应在单个响应中包含所有项目,保留了分页前的行为。
```
server = MCP::Server.new(
name: "my_server",
tools: tools,
page_size: 50,
)
```
当设置 `page_size` 时,只要有更多页面可用,列表响应就会包含 `nextCursor` 字段:
```
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{ "name": "example_tool" }
],
"nextCursor": "50"
}
}
```
无效的游标(例如,非数字、负数或超出范围)将根据 MCP 规范以 JSON-RPC 错误代码 `-32602 (Invalid params)` 被拒绝。
#### 客户端:迭代页面
`MCP::Client` 暴露了 `list_tools`、`list_prompts`、`list_resources` 和 `list_resource_templates`。
**每次调用仅发出一个 `*/list` JSON-RPC 请求并返回恰好一个页面**——而不是完整的集合。
返回的结果对象(`MCP::Client::ListToolsResult` 等)将页面项和下一个游标作为方法访问器暴露:
```
client = MCP::Client.new(transport: transport)
cursor = nil
loop do
page = client.list_tools(cursor: cursor)
page.tools.each { |tool| process(tool) }
cursor = page.next_cursor
break unless cursor
end
```
相同的模式适用于 `list_prompts`(`page.prompts`)、`list_resources`(`page.resources`)和 `list_resource_templates`(`page.resource_templates`)。`next_cursor` 在最后一页为 `nil`。
由于单次调用返回单个页面,返回多少项目取决于服务器的 `page_size` 配置:
| 服务器 `page_size` | `client.list_tools(cursor: nil)` |
|--------------------|---------------------------------------------------------------------|
| 未设置(默认) | 在一个响应中返回所有项目。`next_cursor` 为 `nil`。 |
| 设置为 `N` | 返回前 `N` 个项目。`next_cursor` 用于继续。 |
如果您的应用程序无论服务器如何配置都需要完整的集合,可以如上所示循环 `next_cursor`,或者使用下面描述的全集合方法。
#### 获取完整集合
`client.tools`、`client.resources`、`client.resource_templates` 和 `client.prompts` 会自动迭代所有页面并返回一个普通的项目数组,无论服务器的 `page_size` 设置如何,都保证完整的集合。当服务器分页时,它们每次调用会发出多次 JSON-RPC 往返,并且作为安全措施,如果服务器连续两次返回相同的 `nextCursor`,它们会跳出分页循环。
```
tools = client.tools # => Array of every tool on the server.
```
当您需要完整列表时使用这些方法;当您需要细粒度迭代(例如,流式处理页面而不将所有内容加载到内存中)时,请使用 `list_tools(cursor:)` 等。
### 高级
#### 自定义方法
服务器允许您使用 `define_custom_method` 方法定义超出标准 MCP 协议方法的自定义 JSON-RPC 方法:
```
server = MCP::Server.new(name: "my_server")
# 定义一个返回结果的自定义方法
server.define_custom_method(method_name: "add") do |params|
params[:a] + params[:b]
end
# 定义一个自定义通知方法(返回 nil)
server.define_custom_method(method_name: "notify") do |params|
# Process notification
nil
end
```
**主要特性:**
- 接受任何字符串作为方法名称
- 块接收请求参数作为哈希
- 可以处理常规方法(带响应)和通知
- 防止覆盖现有的 MCP 协议方法
- 支持用于监控的 instrumentation 回调
**用法示例:**
```
# 客户端请求
{
"jsonrpc": "2.0",
"id": 1,
"method": "add",
"params": { "a": 5, "b": 3 }
}
# 服务器响应
{
"jsonrpc": "2.0",
"id": 1,
"result": 8
}
```
**错误处理:**
- 如果尝试覆盖现有方法,则引发 `MCP::Server::MethodAlreadyDefinedError`
- 支持与标准方法相同的异常报告和 instrumentation
## 构建 MCP 客户端
`MCP::Client` 类提供了一个用于与 MCP 服务器交互的接口。
此类支持:
- 通过 `ping` 方法进行存活性检查(`MCP::Client#ping`)
- 通过 `tools/list` 方法列出 tool(`MCP::Client#tools`)
- 通过 `tools/call` 方法调用 tool(`MCP::Client#call_tools`)
- 通过 `resources/list` 方法列出 resource(`MCP::Client#resources`)
- 通过 `resources/templates/list` 方法列出 resource 模板(`MCP::Client#resource_templates`)
- 通过 `resources/read` 方法读取 resource(`MCP::Client#read_resource`)
- 通过 `prompts/list` 方法列出 prompt(`MCP::Client#prompts`)
- 通过 `prompts/get` 方法检索 prompt(`MCP::Client#get_prompt`)
- 通过 `completion/complete` 方法请求补全(`MCP::Client#complete`)
- 自动 JSON-RPC 2.0 消息格式化
- UUID 请求 ID 生成
客户端使用传输层实例进行初始化,该实例处理底层通信机制。
授权由传输层处理。
## 传输层接口
如果您需要的传输层不包含在 gem 中,您可以构建并传递您自己的实例,只要它们符合以下接口:
```
class CustomTransport
# Sends a JSON-RPC request to the server and returns the raw response.
#
# @param request [Hash] A complete JSON-RPC request object.
# https://www.jsonrpc.org/specification#request_object
# @return [Hash] A hash modeling a JSON-RPC response object.
# https://www.jsonrpc.org/specification#response_object
def send_request(request:)
# Your transport-specific logic here
# - HTTP: POST to endpoint with JSON body
# - WebSocket: Send message over WebSocket
# - stdio: Write to stdout, read from stdin
# - etc.
end
end
```
### Stdio 传输层
使用 `MCP::Client::Stdio` 传输与作为子进程通过标准输入/输出运行的 MCP 服务器进行交互。
`MCP::Client::Stdio.new` 接受以下关键字参数:
| 参数 | 必需 | 描述 |
|---|---|---|
| `command:` | 是 | 用于生成服务器进程的命令(例如,`"ruby"`、`"bundle"`、`"npx"`)。 |
| `args:` | 否 | 传递给命令的参数数组。默认为 `[]`。 |
| `env:` | 否 | 为服务器进程设置的环境变量哈希。默认为 `nil`。 |
| `read_timeout:` | 否 | 等待服务器响应的超时时间(以秒为单位)。默认为 `nil`(无超时)。 |
用法示例:
```
stdio_transport = MCP::Client::Stdio.new(
command: "bundle",
args: ["exec", "ruby", "path/to/server.rb"],
env: { "API_KEY" => "my_secret_key" },
read_timeout: 30
)
client = MCP::Client.new(transport: stdio_transport)
# 列出可用的 tools。
tools = client.tools
tools.each do |tool|
puts "Tool: #{tool.name} - #{tool.description}"
end
# 调用特定的 tool。
response = client.call_tool(
tool: tools.first,
arguments: { message: "Hello, world!" }
)
# 完成后关闭 transport。
stdio_transport.close
```
stdio 传输自动处理:
- 使用 `Open3.popen3` 生成服务器进程
- MCP 协议初始化握手(`initialize` 请求 + `notifications/initialized`)
- 基于换行符分隔的 JSON 的 JSON-RPC 2.0 消息帧封装
### HTTP 传输层
使用 `MCP::Client::HTTP` 传输通过简单的 HTTP 请求与 MCP 服务器进行交互。
您需要将 `faraday` 添加为依赖项才能使用 HTTP 传输层。如果服务器使用 SSE(`text/event-stream`)响应,还需添加 `event_stream_parser`:
```
gem 'mcp'
gem 'faraday', '>= 2.0'
gem 'event_stream_parser', '>= 1.0' # optional, required only for SSE responses
```
用法示例:
```
http_transport = MCP::Client::HTTP.new(url: "https://api.example.com/mcp")
client = MCP::Client.new(transport: http_transport)
# 列出可用的 tools
tools = client.tools
tools.each do |tool|
puts <<~TOOL_INFORMATION
Tool: #{tool.name}
Description: #{tool.description}
Input Schema: #{tool.input_schema}
TOOL_INFORMATION
end
# 调用特定的 tool
response = client.call_tool(
tool: tools.first,
arguments: { message: "Hello, world!" }
)
# 调用带有进度跟踪的 tool。
response = client.call_tool(
tool: tools.first,
arguments: { count: 10 },
progress_token: "my-progress-token"
)
```
服务器将在执行期间向客户端发送 `notifications/progress`。
#### HTTP 授权
默认情况下,HTTP 传输层不向服务器提供身份验证,但如果需要身份验证,您可以提供自定义请求头。例如,要使用 Bearer token 身份验证:
```
http_transport = MCP::Client::HTTP.new(
url: "https://api.example.com/mcp",
headers: {
"Authorization" => "Bearer my_token"
}
)
client = MCP::Client.new(transport: http_transport)
client.tools # will make the call using Bearer auth
```
您可以添加身份验证方案或任何其他目的所需的任何自定义请求头。客户端将在每个请求中包含这些请求头。
#### 自定义 Faraday 连接
您可以将块传递给 `MCP::Client::HTTP.new` 以自定义底层 Faraday 连接。
该块在默认中间件配置之后调用,因此您可以添加中间件或替换 HTTP 适配器:
```
http_transport = MCP::Client::HTTP.new(url: "https://api.example.com/mcp") do |faraday|
faraday.use MyApp::Middleware::HttpRecorder
faraday.adapter :typhoeus
end
```
### Tool 对象
客户端为服务器返回的 tools 提供了一个包装类:
- `MCP::Client::Tool` - 表示带有其元数据的单个 tool
此类提供了对 tool 属性(如名称、描述、input schema 和 output schema)的轻松访问。
## 一致性测试
`conformance/` 目录包含一个测试服务器和运行器,它们使用 [`@modelcontextprotocol/conformance`](https://github.com/modelcontextprotocol/conformance) 根据 MCP 规范验证 SDK。
有关使用说明,请参阅 [conformance/README.md](conformance/README.md)。
## 文档
- [SDK API 文档](https://rubydoc.info/gems/mcp)
- [Model Context Protocol 文档](https://modelcontextprotocol.io)
标签:Apache 2.0, DLL 劫持, HTTP传输, JSON-RPC, LLM, MCP, Model Context Protocol, Prompt管理, Ruby, Ruby Gem, SSE, Unmanaged PE, 人工智能, 大语言模型, 客户端, 工具调用, 开发工具库, 开源, 批量扫描, 服务器, 用户模式Hook绕过, 知识库, 资源管理