model-context-protocol-rb 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +207 -171
- data/lib/model_context_protocol/server/streamable_http_transport.rb +26 -8
- data/lib/model_context_protocol/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8fd0681c65b47196375ec7f4934cadf226ed01b1b289caf296348698808ef5cb
|
4
|
+
data.tar.gz: 4ce234855d56b724b6f69b97851ff1ee4db65370b7361ea0a65c43a929c448b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3bafa3fe92aa05ff380ad5fc311c0143d5e1bb6c3a91e50893115eb01ad2ea8b2d93dde89240815b50629fcb3b3b3467585766ac42d2c7087cd0be55b431455
|
7
|
+
data.tar.gz: 9d1c4f1c5f93352bf9392f9aeaf63253534348787aecb0866abe160dbf7cebc302bfec751955b19d0a06d9d60675c450960d401af2538aa9b100fe076bc7fd94
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.5.1] - 2025-09-23
|
4
|
+
|
5
|
+
- (Fix) Ensure streams are properly closed when clients disconnect.
|
6
|
+
|
3
7
|
## [0.5.0] - 2025-09-22
|
4
8
|
|
5
9
|
- Make streamable HTTP transport thread-safe by using Redis to manage state.
|
@@ -75,6 +79,7 @@
|
|
75
79
|
- Initial release
|
76
80
|
|
77
81
|
[Unreleased]: https://github.com/dickdavis/model-context-protocol-rb/compare/v0.5.0...HEAD
|
82
|
+
[0.5.1]: https://github.com/dickdavis/model-context-protocol-rb/compare/v0.5.0...v0.5.1
|
78
83
|
[0.5.0]: https://github.com/dickdavis/model-context-protocol-rb/compare/v0.4.0...v0.5.0
|
79
84
|
[0.4.0]: https://github.com/dickdavis/model-context-protocol-rb/compare/v0.3.4...v0.4.0
|
80
85
|
[0.3.4]: https://github.com/dickdavis/model-context-protocol-rb/compare/v0.3.3...v0.3.4
|
data/README.md
CHANGED
@@ -7,24 +7,20 @@ Provides simple abstractions that allow you to serve prompts, resources, resourc
|
|
7
7
|
## Table of Contents
|
8
8
|
|
9
9
|
- [Feature Support (Server)](#feature-support-server)
|
10
|
-
- [
|
11
|
-
- [Building an MCP Server](#building-an-mcp-server)
|
12
|
-
- [Server Configuration Options](#server-configuration-options)
|
13
|
-
- [Pagination Configuration Options](#pagination-configuration-options)
|
14
|
-
- [Transport Configuration Options](#transport-configuration-options)
|
15
|
-
- [STDIO Transport](#stdio-transport)
|
16
|
-
- [Streamable HTTP Transport](#streamable-http-transport)
|
17
|
-
- [Registry Configuration Options](#registry-configuration-options)
|
18
|
-
- [Integration with Rails](#integration-with-rails)
|
19
|
-
- [Server features](#server-features)
|
20
|
-
- [Prompts](#prompts)
|
21
|
-
- [Resources](#resources)
|
22
|
-
- [Resource Templates](#resource-templates)
|
23
|
-
- [Tools](#tools)
|
24
|
-
- [Completions](#completions)
|
10
|
+
- [Quick Start with Rails](#quick-start-with-rails)
|
25
11
|
- [Installation](#installation)
|
12
|
+
- [Building an MCP Server](#building-an-mcp-server)
|
13
|
+
- [Server Configuration Options](#server-configuration-options)
|
14
|
+
- [Pagination Configuration Options](#pagination-configuration-options)
|
15
|
+
- [Transport Configuration Options](#transport-configuration-options)
|
16
|
+
- [Redis Configuration](#redis-configuration)
|
17
|
+
- [Registry Configuration Options](#registry-configuration-options)
|
18
|
+
- [Prompts](#prompts)
|
19
|
+
- [Resources](#resources)
|
20
|
+
- [Resource Templates](#resource-templates)
|
21
|
+
- [Tools](#tools)
|
22
|
+
- [Completions](#completions)
|
26
23
|
- [Development](#development)
|
27
|
-
- [Releases](#releases)
|
28
24
|
- [Contributing](#contributing)
|
29
25
|
- [License](#license)
|
30
26
|
|
@@ -50,15 +46,144 @@ Provides simple abstractions that allow you to serve prompts, resources, resourc
|
|
50
46
|
| ✅ | [Ping](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/ping) |
|
51
47
|
| ✅ | [Progress](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/progress) |
|
52
48
|
|
53
|
-
##
|
49
|
+
## Quick Start with Rails
|
54
50
|
|
55
|
-
|
51
|
+
The `model-context-protocol-rb` works out of the box with any valid Rack request. Currently, this project has no plans for building a deeper Rails integration, but it is fairly simple to build it out yourself. To support modern application deployments across multiple servers, the streamable HTTP transport requires Redis as an external dependency.
|
52
|
+
|
53
|
+
Here's an example of how you can easily integrate with Rails.
|
54
|
+
|
55
|
+
First, configure Redis in an initializer:
|
56
56
|
|
57
57
|
```ruby
|
58
|
-
|
58
|
+
# config/initializers/model_context_protocol.rb
|
59
|
+
require "model_context_protocol"
|
60
|
+
|
61
|
+
ModelContextProtocol::Server.configure_redis do |config|
|
62
|
+
config.redis_url = ENV.fetch("REDIS_URL", "redis://localhost:6379/0")
|
63
|
+
config.pool_size = 20
|
64
|
+
config.pool_timeout = 5
|
65
|
+
config.enable_reaper = true
|
66
|
+
config.reaper_interval = 60
|
67
|
+
config.idle_timeout = 300
|
68
|
+
end
|
59
69
|
```
|
60
70
|
|
61
|
-
|
71
|
+
Then, set the routes:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
constraints format: :json do
|
75
|
+
get "/mcp", to: "model_context_protocol#handle", as: :mcp_get
|
76
|
+
post "/mcp", to: "model_context_protocol#handle", as: :mcp_post
|
77
|
+
delete "/mcp", to: "model_context_protocol#handle", as: :mcp_delete
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
81
|
+
Then, implement a controller endpoint to handle the requests.
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
class ModelContextProtocolController < ActionController::API
|
85
|
+
include ActionController::Live
|
86
|
+
|
87
|
+
before_action :authenticate_user
|
88
|
+
|
89
|
+
def handle
|
90
|
+
server = ModelContextProtocol::Server.new do |config|
|
91
|
+
config.name = "MyMCPServer"
|
92
|
+
config.title = "My MCP Server"
|
93
|
+
config.version = "1.0.0"
|
94
|
+
config.logging_enabled = true
|
95
|
+
config.registry = build_registry
|
96
|
+
config.context = {
|
97
|
+
user_id: current_user.id,
|
98
|
+
request_id: request.id
|
99
|
+
}
|
100
|
+
config.transport = {
|
101
|
+
type: :streamable_http,
|
102
|
+
env: request.env
|
103
|
+
}
|
104
|
+
config.instructions = <<~INSTRUCTIONS
|
105
|
+
This server provides prompts, tools, and resources for interacting with my app.
|
106
|
+
|
107
|
+
Key capabilities:
|
108
|
+
- Does this one thing
|
109
|
+
- Does this other thing
|
110
|
+
- Oh, yeah, and it does that one thing, too
|
111
|
+
|
112
|
+
Use this server when you need to do stuff.
|
113
|
+
INSTRUCTIONS
|
114
|
+
end
|
115
|
+
|
116
|
+
result = server.start
|
117
|
+
handle_mcp_response(result)
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def build_registry
|
123
|
+
ModelContextProtocol::Server::Registry.new do
|
124
|
+
tools do
|
125
|
+
# Implement user authorization logic to dynamically build registry
|
126
|
+
register TestTool if current_user.authorized_for?(TestTool)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def handle_mcp_response(result)
|
132
|
+
if result[:headers]&.dig("Content-Type") == "text/event-stream"
|
133
|
+
setup_streaming_headers
|
134
|
+
stream_response(result[:stream_proc])
|
135
|
+
else
|
136
|
+
render_json_response(result)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def setup_streaming_headers
|
141
|
+
response.headers.merge!(
|
142
|
+
"Content-Type" => "text/event-stream",
|
143
|
+
"Cache-Control" => "no-cache",
|
144
|
+
"Connection" => "keep-alive"
|
145
|
+
)
|
146
|
+
end
|
147
|
+
|
148
|
+
def stream_response(stream_proc)
|
149
|
+
stream_proc&.call(response.stream)
|
150
|
+
ensure
|
151
|
+
response.stream.close rescue nil
|
152
|
+
end
|
153
|
+
|
154
|
+
def render_json_response(result)
|
155
|
+
render json: result[:json],
|
156
|
+
status: result[:status] || 200,
|
157
|
+
headers: result[:headers] || {}
|
158
|
+
end
|
159
|
+
end
|
160
|
+
```
|
161
|
+
|
162
|
+
Read more about the [server configuration options](building-an-mcp-server) to better understand how you can customize your MCP server.
|
163
|
+
|
164
|
+
From here, you can get started building [prompts](#prompts), [resources](#resources), [resource templates](#resource-templates), and [tools](#tools).
|
165
|
+
|
166
|
+
## Installation
|
167
|
+
|
168
|
+
Add this line to your application's Gemfile:
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
gem 'model-context-protocol-rb'
|
172
|
+
```
|
173
|
+
|
174
|
+
And then execute:
|
175
|
+
|
176
|
+
```bash
|
177
|
+
bundle
|
178
|
+
```
|
179
|
+
|
180
|
+
Or install it yourself as:
|
181
|
+
|
182
|
+
```bash
|
183
|
+
gem install model-context-protocol-rb
|
184
|
+
```
|
185
|
+
|
186
|
+
## Building an MCP Server
|
62
187
|
|
63
188
|
Build a simple MCP server by registering your prompts, resources, resource templates, and tools. Then, configure and run the server. Messages from the MCP client will be routed to the appropriate custom handler. This SDK provides several classes that should be used to build your handlers.
|
64
189
|
|
@@ -146,7 +271,7 @@ end
|
|
146
271
|
server.start
|
147
272
|
```
|
148
273
|
|
149
|
-
|
274
|
+
### Server Configuration Options
|
150
275
|
|
151
276
|
The following table details all available configuration options for the MCP server:
|
152
277
|
|
@@ -162,7 +287,7 @@ The following table details all available configuration options for the MCP serv
|
|
162
287
|
| `transport` | Hash | No | `{ type: :stdio }` | Transport configuration |
|
163
288
|
| `registry` | Registry | Yes | - | Registry containing prompts, resources, and tools |
|
164
289
|
|
165
|
-
|
290
|
+
### Pagination Configuration Options
|
166
291
|
|
167
292
|
When `pagination` is set to a Hash, the following options are available:
|
168
293
|
|
@@ -174,16 +299,32 @@ When `pagination` is set to a Hash, the following options are available:
|
|
174
299
|
|
175
300
|
**Note:** Set `config.pagination = false` to completely disable pagination support.
|
176
301
|
|
177
|
-
|
302
|
+
### Transport Configuration Options
|
178
303
|
|
179
304
|
The transport configuration supports two types: `:stdio` (default) and `:streamable_http`.
|
180
305
|
|
181
|
-
|
306
|
+
#### STDIO Transport
|
307
|
+
|
182
308
|
```ruby
|
183
309
|
config.transport = { type: :stdio } # This is the default, can be omitted
|
184
310
|
```
|
185
311
|
|
186
|
-
|
312
|
+
#### Streamable HTTP Transport
|
313
|
+
|
314
|
+
```ruby
|
315
|
+
config.transport = { type: :streamable_http, env: request.env }
|
316
|
+
```
|
317
|
+
|
318
|
+
When using `:streamable_http` transport, the following options are available:
|
319
|
+
|
320
|
+
| Option | Type | Required | Default | Description |
|
321
|
+
|--------|------|----------|---------|-------------|
|
322
|
+
| `type` | Symbol | Yes | `:stdio` | Must be `:streamable_http` for HTTP transport |
|
323
|
+
| `session_ttl` | Integer | No | `3600` | Session timeout in seconds (1 hour) |
|
324
|
+
| `env` | Hash | No | - | Rack environment hash (for Rails integration) |
|
325
|
+
|
326
|
+
### Redis Configuration
|
327
|
+
|
187
328
|
The `:streamable_http` transport requires Redis to be configured globally before use:
|
188
329
|
|
189
330
|
```ruby
|
@@ -206,15 +347,7 @@ end
|
|
206
347
|
| `reaper_interval` | Integer | No | `60` | Reaper check interval in seconds |
|
207
348
|
| `idle_timeout` | Integer | No | `300` | Idle connection timeout in seconds |
|
208
349
|
|
209
|
-
|
210
|
-
|
211
|
-
| Option | Type | Required | Default | Description |
|
212
|
-
|--------|------|----------|---------|-------------|
|
213
|
-
| `type` | Symbol | Yes | `:stdio` | Must be `:streamable_http` for HTTP transport |
|
214
|
-
| `session_ttl` | Integer | No | `3600` | Session timeout in seconds (1 hour) |
|
215
|
-
| `env` | Hash | No | - | Rack environment hash (for Rails integration) |
|
216
|
-
|
217
|
-
#### Registry Configuration Options
|
350
|
+
### Registry Configuration Options
|
218
351
|
|
219
352
|
The registry is configured using `ModelContextProtocol::Server::Registry.new` and supports the following block types:
|
220
353
|
|
@@ -245,96 +378,9 @@ config.registry = ModelContextProtocol::Server::Registry.new do
|
|
245
378
|
end
|
246
379
|
```
|
247
380
|
|
248
|
-
|
249
|
-
|
250
|
-
The streamable HTTP transport works with any valid Rack request. Here's an example of how you can integrate with Rails.
|
381
|
+
---
|
251
382
|
|
252
|
-
|
253
|
-
|
254
|
-
```ruby
|
255
|
-
# config/initializers/model_context_protocol.rb
|
256
|
-
ModelContextProtocol::Server.configure_redis do |config|
|
257
|
-
config.redis_url = ENV.fetch('REDIS_URL')
|
258
|
-
config.pool_size = 20
|
259
|
-
config.pool_timeout = 5
|
260
|
-
config.enable_reaper = true
|
261
|
-
config.reaper_interval = 60
|
262
|
-
config.idle_timeout = 300
|
263
|
-
end
|
264
|
-
```
|
265
|
-
|
266
|
-
Then, set the routes:
|
267
|
-
|
268
|
-
```ruby
|
269
|
-
constraints format: :json do
|
270
|
-
get "/mcp", to: "model_context_protocol#handle", as: :mcp_get
|
271
|
-
post "/mcp", to: "model_context_protocol#handle", as: :mcp_post
|
272
|
-
delete "/mcp", to: "model_context_protocol#handle", as: :mcp_delete
|
273
|
-
end
|
274
|
-
```
|
275
|
-
|
276
|
-
Then, implement a controller endpoint to handle the requests.
|
277
|
-
|
278
|
-
```ruby
|
279
|
-
require 'model_context_protocol'
|
280
|
-
|
281
|
-
class ModelContextProtocolController < ApplicationController
|
282
|
-
before_action :authenticate_user
|
283
|
-
|
284
|
-
def handle
|
285
|
-
server = ModelContextProtocol::Server.new do |config|
|
286
|
-
config.name = "MyMCPServer"
|
287
|
-
config.title = "My MCP Server"
|
288
|
-
config.version = "1.0.0"
|
289
|
-
config.logging_enabled = true
|
290
|
-
config.context = {
|
291
|
-
user_id: current_user.id,
|
292
|
-
request_id: request.id
|
293
|
-
}
|
294
|
-
config.registry = build_registry
|
295
|
-
config.transport = {
|
296
|
-
type: :streamable_http,
|
297
|
-
env: request.env
|
298
|
-
}
|
299
|
-
config.instructions = <<~INSTRUCTIONS
|
300
|
-
This server provides prompts, tools, and resources for interacting with my app.
|
301
|
-
|
302
|
-
Key capabilities:
|
303
|
-
- Does this one thing
|
304
|
-
- Does this other thing
|
305
|
-
- Oh, yeah, and it does that one thing, too
|
306
|
-
|
307
|
-
Use this server when you need to do stuff.
|
308
|
-
INSTRUCTIONS
|
309
|
-
end
|
310
|
-
|
311
|
-
result = server.start
|
312
|
-
|
313
|
-
# For SSE responses
|
314
|
-
if result[:stream]
|
315
|
-
response.headers.merge!(result[:headers] || {})
|
316
|
-
response.content_type = result[:headers]["Content-Type"] || "text/event-stream"
|
317
|
-
# Handle streaming with result[:stream_proc]
|
318
|
-
else
|
319
|
-
# For regular JSON responses
|
320
|
-
render json: result[:json], status: result[:status], headers: result[:headers]
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
|
-
private
|
325
|
-
|
326
|
-
def build_registry
|
327
|
-
ModelContextProtocol::Server::Registry.new do
|
328
|
-
tools do
|
329
|
-
# Implement user authorization logic to dynamically build registry
|
330
|
-
register TestTool if current_user.authorized_for?(TestTool)
|
331
|
-
end
|
332
|
-
end
|
333
|
-
end
|
334
|
-
end
|
335
|
-
```
|
336
|
-
|
337
|
-
### Prompts
|
383
|
+
## Prompts
|
338
384
|
|
339
385
|
The `ModelContextProtocol::Server::Prompt` base class allows subclasses to define a prompt that the MCP client can use.
|
340
386
|
|
@@ -342,7 +388,7 @@ Define the prompt properties and then implement the `call` method to build your
|
|
342
388
|
|
343
389
|
You can also log from within your prompt by calling a valid logger level method on the `logger` and passing a string message.
|
344
390
|
|
345
|
-
|
391
|
+
### Prompt Definition
|
346
392
|
|
347
393
|
Use the `define` block to set [prompt properties](https://spec.modelcontextprotocol.io/specification/2025-06-18/server/prompts/) and configure arguments.
|
348
394
|
|
@@ -353,7 +399,7 @@ Use the `define` block to set [prompt properties](https://spec.modelcontextproto
|
|
353
399
|
| `description` | Short description of what the prompt does |
|
354
400
|
| `argument` | Define an argument block with name, description, required flag, and completion |
|
355
401
|
|
356
|
-
|
402
|
+
### Argument Definition
|
357
403
|
|
358
404
|
Define any arguments using `argument` blocks nested within the `define` block. You can mark an argument as required, and you can optionally provide a completion class. See [Completions](#completions) for more information.
|
359
405
|
|
@@ -364,7 +410,7 @@ Define any arguments using `argument` blocks nested within the `define` block. Y
|
|
364
410
|
| `required` | Whether the argument is required (boolean) |
|
365
411
|
| `completion` | Available hints for completions (array or completion class) |
|
366
412
|
|
367
|
-
|
413
|
+
### Prompt Methods
|
368
414
|
|
369
415
|
Define your prompt properties and arguments, implement the `call` method using the `message_history` DSL to build prompt messages and `respond_with` to serialize them. 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.
|
370
416
|
|
@@ -377,7 +423,7 @@ Define your prompt properties and arguments, implement the `call` method using t
|
|
377
423
|
| `message_history` | Within `call` | DSL method to build an array of user and assistant messages |
|
378
424
|
| `respond_with` | Within `call` | Return properly formatted response data (e.g., `respond_with messages:`) |
|
379
425
|
|
380
|
-
|
426
|
+
### Message History DSL
|
381
427
|
|
382
428
|
Build a message history using the an intuitive DSL, creating an ordered history of user and assistant messages with flexible content blocks that can include text, image, audio, embedded resources, and resource links.
|
383
429
|
|
@@ -386,7 +432,7 @@ Build a message history using the an intuitive DSL, creating an ordered history
|
|
386
432
|
| `user_message` | Within `message_history` | Create a message with user role |
|
387
433
|
| `assistant_message` | Within `message_history` | Create a message with assistant role |
|
388
434
|
|
389
|
-
|
435
|
+
### Content Blocks
|
390
436
|
|
391
437
|
Use content blocks to properly format the content included in messages.
|
392
438
|
|
@@ -398,7 +444,7 @@ Use content blocks to properly format the content included in messages.
|
|
398
444
|
| `embedded_resource_content` | Within message blocks | Create embedded resource content block (requires `resource:`) |
|
399
445
|
| `resource_link` | Within message blocks | Create resource link content block (requires `name:` and `uri:`) |
|
400
446
|
|
401
|
-
|
447
|
+
### Available Instance Variables
|
402
448
|
|
403
449
|
The `arguments` passed from an MCP client are available, as well as the `context` values passed in at server initialization.
|
404
450
|
|
@@ -408,7 +454,7 @@ The `arguments` passed from an MCP client are available, as well as the `context
|
|
408
454
|
| `context` | Within `call` | Hash containing server configuration context values |
|
409
455
|
| `logger` | Within `call` | Logger instance for logging (e.g., `logger.info("message")`) |
|
410
456
|
|
411
|
-
|
457
|
+
### Examples
|
412
458
|
|
413
459
|
This is an example prompt that returns a properly formatted response:
|
414
460
|
|
@@ -487,13 +533,15 @@ class TestPrompt < ModelContextProtocol::Server::Prompt
|
|
487
533
|
end
|
488
534
|
```
|
489
535
|
|
490
|
-
|
536
|
+
---
|
537
|
+
|
538
|
+
## Resources
|
491
539
|
|
492
540
|
The `ModelContextProtocol::Server::Resource` base class allows subclasses to define a resource that the MCP client can use.
|
493
541
|
|
494
542
|
Define the resource properties and optionally annotations, then implement the `call` method to build your resource. Use the `respond_with` instance method to ensure your resource responds with appropriately formatted response data.
|
495
543
|
|
496
|
-
|
544
|
+
### Resource Definition
|
497
545
|
|
498
546
|
Use the `define` block to set [resource properties](https://spec.modelcontextprotocol.io/specification/2025-06-18/server/resources/) and configure annotations.
|
499
547
|
|
@@ -506,7 +554,7 @@ Use the `define` block to set [resource properties](https://spec.modelcontextpro
|
|
506
554
|
| `uri` | URI identifier for the resource |
|
507
555
|
| `annotations` | Block for defining resource annotations |
|
508
556
|
|
509
|
-
|
557
|
+
### Annotation Definition
|
510
558
|
|
511
559
|
Define any [resource annotations](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#annotations) using an `annotations` block nested within the `define` block.
|
512
560
|
|
@@ -516,7 +564,7 @@ Define any [resource annotations](https://modelcontextprotocol.io/specification/
|
|
516
564
|
| `priority` | Priority level (numeric value, e.g., `0.9`) |
|
517
565
|
| `last_modified` | Last modified timestamp (ISO 8601 string) |
|
518
566
|
|
519
|
-
|
567
|
+
### Resource Methods
|
520
568
|
|
521
569
|
Define your resource properties and annotations, implement the `call` method to build resource content and `respond_with` to serialize the response. 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.
|
522
570
|
|
@@ -528,7 +576,7 @@ Define your resource properties and annotations, implement the `call` method to
|
|
528
576
|
| `progressable` | Within `call` | Wrap long-running operations to send clients progress notifications (e.g., `progressable { slow_operation }`) |
|
529
577
|
| `respond_with` | Within `call` | Return properly formatted response data (e.g., `respond_with text:` or `respond_with binary:`) |
|
530
578
|
|
531
|
-
|
579
|
+
### Available Instance Variables
|
532
580
|
|
533
581
|
Resources are stateless and only have access to their configured properties.
|
534
582
|
|
@@ -537,7 +585,7 @@ Resources are stateless and only have access to their configured properties.
|
|
537
585
|
| `mime_type` | Within `call` | The configured MIME type for this resource |
|
538
586
|
| `uri` | Within `call` | The configured URI identifier for this resource |
|
539
587
|
|
540
|
-
|
588
|
+
### Examples
|
541
589
|
|
542
590
|
This is an example resource that returns a text response:
|
543
591
|
|
@@ -599,13 +647,15 @@ class TestBinaryResource < ModelContextProtocol::Server::Resource
|
|
599
647
|
end
|
600
648
|
```
|
601
649
|
|
602
|
-
|
650
|
+
---
|
651
|
+
|
652
|
+
## Resource Templates
|
603
653
|
|
604
654
|
The `ModelContextProtocol::Server::ResourceTemplate` base class allows subclasses to define a resource template that the MCP client can use.
|
605
655
|
|
606
656
|
Define the resource template properties and URI template with optional parameter completions. Resource templates are used to define parameterized resources that clients can instantiate.
|
607
657
|
|
608
|
-
|
658
|
+
### Resource Template Definition
|
609
659
|
|
610
660
|
Use the `define` block to set [resource template properties](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#resource-templates).
|
611
661
|
|
@@ -616,7 +666,7 @@ Use the `define` block to set [resource template properties](https://modelcontex
|
|
616
666
|
| `mime_type` | MIME type of resources created from this template |
|
617
667
|
| `uri_template` | URI template with parameters (e.g., `"file:///{name}"`) |
|
618
668
|
|
619
|
-
|
669
|
+
### URI Template Configuration
|
620
670
|
|
621
671
|
Define the URI template and configure parameter completions within the `uri_template` block.
|
622
672
|
|
@@ -624,7 +674,7 @@ Define the URI template and configure parameter completions within the `uri_temp
|
|
624
674
|
|--------|---------|-------------|
|
625
675
|
| `completion` | Within `uri_template` block | Define completion for a URI parameter (e.g., `completion :name, ["value1", "value2"]`) |
|
626
676
|
|
627
|
-
|
677
|
+
### Resource Template Methods
|
628
678
|
|
629
679
|
Resource templates only use the `define` method to configure their properties - they don't have a `call` method.
|
630
680
|
|
@@ -632,7 +682,7 @@ Resource templates only use the `define` method to configure their properties -
|
|
632
682
|
|--------|---------|-------------|
|
633
683
|
| `define` | Class definition | Block for defining resource template metadata and URI template |
|
634
684
|
|
635
|
-
|
685
|
+
### Examples
|
636
686
|
|
637
687
|
This is an example resource template that provides a completion for a parameter of the URI template:
|
638
688
|
|
@@ -668,13 +718,15 @@ class TestResourceTemplate < ModelContextProtocol::Server::ResourceTemplate
|
|
668
718
|
end
|
669
719
|
```
|
670
720
|
|
671
|
-
|
721
|
+
---
|
722
|
+
|
723
|
+
## Tools
|
672
724
|
|
673
725
|
The `ModelContextProtocol::Server::Tool` base class allows subclasses to define a tool that the MCP client can use.
|
674
726
|
|
675
727
|
Define the tool properties and schemas, then implement the `call` method to build your tool response. Arguments from the MCP client and server context are available, along with logging capabilities.
|
676
728
|
|
677
|
-
|
729
|
+
### Tool Definition
|
678
730
|
|
679
731
|
Use the `define` block to set [tool properties](https://spec.modelcontextprotocol.io/specification/2025-06-18/server/tools/) and configure schemas.
|
680
732
|
|
@@ -686,7 +738,7 @@ Use the `define` block to set [tool properties](https://spec.modelcontextprotoco
|
|
686
738
|
| `input_schema` | JSON schema block for validating tool inputs |
|
687
739
|
| `output_schema` | JSON schema block for validating structured content outputs |
|
688
740
|
|
689
|
-
|
741
|
+
### Tool Methods
|
690
742
|
|
691
743
|
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.
|
692
744
|
|
@@ -698,7 +750,7 @@ Define your tool properties and schemas, implement the `call` method using conte
|
|
698
750
|
| `progressable` | Within `call` | Wrap long-running operations to send clients progress notifications (e.g., `progressable { slow_operation }`) |
|
699
751
|
| `respond_with` | Within `call` | Return properly formatted response data with various content types |
|
700
752
|
|
701
|
-
|
753
|
+
### Content Blocks
|
702
754
|
|
703
755
|
Use content blocks to properly format the content included in tool responses.
|
704
756
|
|
@@ -710,7 +762,7 @@ Use content blocks to properly format the content included in tool responses.
|
|
710
762
|
| `embedded_resource_content` | Within `call` | Create embedded resource content block (requires `resource:`) |
|
711
763
|
| `resource_link` | Within `call` | Create resource link content block (requires `name:` and `uri:`) |
|
712
764
|
|
713
|
-
|
765
|
+
### Response Types
|
714
766
|
|
715
767
|
Tools can return different types of responses using `respond_with`.
|
716
768
|
|
@@ -721,7 +773,7 @@ Tools can return different types of responses using `respond_with`.
|
|
721
773
|
| `content:` | `respond_with content: [content_blocks]` | Return array of mixed content blocks |
|
722
774
|
| `error:` | `respond_with error: "message"` | Return tool error response |
|
723
775
|
|
724
|
-
|
776
|
+
### Available Instance Variables
|
725
777
|
|
726
778
|
Arguments from MCP clients and server context are available, along with logging capabilities.
|
727
779
|
|
@@ -731,7 +783,7 @@ Arguments from MCP clients and server context are available, along with logging
|
|
731
783
|
| `context` | Within `call` | Hash containing server configuration context values |
|
732
784
|
| `logger` | Within `call` | Logger instance for logging (e.g., `logger.info("message")`) |
|
733
785
|
|
734
|
-
|
786
|
+
### Examples
|
735
787
|
|
736
788
|
This is an example of a tool that returns structured content validated by an output schema:
|
737
789
|
|
@@ -1096,13 +1148,15 @@ class TestToolWithProgressableAndCancellable < ModelContextProtocol::Server::Too
|
|
1096
1148
|
end
|
1097
1149
|
```
|
1098
1150
|
|
1099
|
-
|
1151
|
+
---
|
1152
|
+
|
1153
|
+
## Completions
|
1100
1154
|
|
1101
1155
|
The `ModelContextProtocol::Server::Completion` base class allows subclasses to define a completion that the MCP client can use to obtain hints or suggestions for arguments to prompts and resources.
|
1102
1156
|
|
1103
1157
|
Implement the `call` method to build your completion logic using the provided argument name and value. Completions are simpler than other server features - they don't use a `define` block and only provide filtered suggestion lists.
|
1104
1158
|
|
1105
|
-
|
1159
|
+
### Completion Methods
|
1106
1160
|
|
1107
1161
|
Completions only implement the `call` method to provide completion logic.
|
1108
1162
|
|
@@ -1111,7 +1165,7 @@ Completions only implement the `call` method to provide completion logic.
|
|
1111
1165
|
| `call` | Instance method | Main method to implement completion logic and build response |
|
1112
1166
|
| `respond_with` | Within `call` | Return properly formatted completion response (e.g., `respond_with values:`) |
|
1113
1167
|
|
1114
|
-
|
1168
|
+
### Available Instance Variables
|
1115
1169
|
|
1116
1170
|
Completions receive the argument name and current value being completed.
|
1117
1171
|
|
@@ -1120,7 +1174,7 @@ Completions receive the argument name and current value being completed.
|
|
1120
1174
|
| `argument_name` | Within `call` | String name of the argument being completed |
|
1121
1175
|
| `argument_value` | Within `call` | Current partial value being typed by the user |
|
1122
1176
|
|
1123
|
-
|
1177
|
+
### Examples
|
1124
1178
|
|
1125
1179
|
This is an example completion that returns an array of values in the response:
|
1126
1180
|
|
@@ -1137,25 +1191,7 @@ class TestCompletion < ModelContextProtocol::Server::Completion
|
|
1137
1191
|
end
|
1138
1192
|
```
|
1139
1193
|
|
1140
|
-
|
1141
|
-
|
1142
|
-
Add this line to your application's Gemfile:
|
1143
|
-
|
1144
|
-
```ruby
|
1145
|
-
gem 'model-context-protocol-rb'
|
1146
|
-
```
|
1147
|
-
|
1148
|
-
And then execute:
|
1149
|
-
|
1150
|
-
```bash
|
1151
|
-
bundle
|
1152
|
-
```
|
1153
|
-
|
1154
|
-
Or install it yourself as:
|
1155
|
-
|
1156
|
-
```bash
|
1157
|
-
gem install model-context-protocol-rb
|
1158
|
-
```
|
1194
|
+
---
|
1159
1195
|
|
1160
1196
|
## Development
|
1161
1197
|
|
@@ -182,7 +182,13 @@ module ModelContextProtocol
|
|
182
182
|
event_id = next_event_id
|
183
183
|
send_sse_event(stream, {}, event_id)
|
184
184
|
end
|
185
|
+
|
186
|
+
# Close stream immediately when work is complete
|
187
|
+
close_stream(temp_stream_id, reason: "request_completed")
|
188
|
+
rescue IOError, Errno::EPIPE, Errno::ECONNRESET
|
189
|
+
# Client disconnected during processing
|
185
190
|
ensure
|
191
|
+
# Fallback cleanup
|
186
192
|
@stream_registry.unregister_stream(temp_stream_id)
|
187
193
|
end
|
188
194
|
end
|
@@ -201,6 +207,20 @@ module ModelContextProtocol
|
|
201
207
|
stream.flush if stream.respond_to?(:flush)
|
202
208
|
end
|
203
209
|
|
210
|
+
def close_stream(session_id, reason: "completed")
|
211
|
+
if (stream = @stream_registry.get_local_stream(session_id))
|
212
|
+
begin
|
213
|
+
send_sse_event(stream, {type: "stream_complete", reason: reason})
|
214
|
+
stream.close
|
215
|
+
rescue IOError, Errno::EPIPE, Errno::ECONNRESET, Errno::ENOTCONN, Errno::EBADF
|
216
|
+
nil
|
217
|
+
end
|
218
|
+
|
219
|
+
@stream_registry.unregister_stream(session_id)
|
220
|
+
@session_store.mark_stream_inactive(session_id) if @require_sessions
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
204
224
|
def handle_post_request(env)
|
205
225
|
validation_error = validate_headers(env)
|
206
226
|
return validation_error if validation_error
|
@@ -404,7 +424,7 @@ module ModelContextProtocol
|
|
404
424
|
stream.write(": ping\n\n")
|
405
425
|
stream.flush if stream.respond_to?(:flush)
|
406
426
|
true
|
407
|
-
rescue IOError, Errno::EPIPE, Errno::ECONNRESET
|
427
|
+
rescue IOError, Errno::EPIPE, Errno::ECONNRESET, Errno::ENOTCONN, Errno::EBADF
|
408
428
|
false
|
409
429
|
end
|
410
430
|
end
|
@@ -438,12 +458,10 @@ module ModelContextProtocol
|
|
438
458
|
send_ping_to_stream(stream)
|
439
459
|
@stream_registry.refresh_heartbeat(session_id)
|
440
460
|
else
|
441
|
-
|
442
|
-
@session_store.mark_stream_inactive(session_id)
|
461
|
+
close_stream(session_id, reason: "client_disconnected")
|
443
462
|
end
|
444
|
-
rescue IOError, Errno::EPIPE, Errno::ECONNRESET
|
445
|
-
|
446
|
-
@session_store.mark_stream_inactive(session_id)
|
463
|
+
rescue IOError, Errno::EPIPE, Errno::ECONNRESET, Errno::ENOTCONN, Errno::EBADF
|
464
|
+
close_stream(session_id, reason: "network_error")
|
447
465
|
end
|
448
466
|
end
|
449
467
|
|
@@ -468,7 +486,7 @@ module ModelContextProtocol
|
|
468
486
|
send_to_stream(stream, data)
|
469
487
|
return true
|
470
488
|
rescue IOError, Errno::EPIPE, Errno::ECONNRESET
|
471
|
-
|
489
|
+
close_stream(session_id, reason: "client_disconnected")
|
472
490
|
end
|
473
491
|
end
|
474
492
|
|
@@ -493,7 +511,7 @@ module ModelContextProtocol
|
|
493
511
|
@stream_registry.get_all_local_streams.each do |session_id, stream|
|
494
512
|
send_to_stream(stream, notification)
|
495
513
|
rescue IOError, Errno::EPIPE, Errno::ECONNRESET
|
496
|
-
|
514
|
+
close_stream(session_id, reason: "client_disconnected")
|
497
515
|
end
|
498
516
|
end
|
499
517
|
|