model-context-protocol-rb 0.3.4 → 0.5.0

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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -1
  3. data/README.md +886 -196
  4. data/lib/model_context_protocol/server/cancellable.rb +54 -0
  5. data/lib/model_context_protocol/server/configuration.rb +80 -8
  6. data/lib/model_context_protocol/server/content.rb +321 -0
  7. data/lib/model_context_protocol/server/content_helpers.rb +84 -0
  8. data/lib/model_context_protocol/server/pagination.rb +71 -0
  9. data/lib/model_context_protocol/server/progressable.rb +72 -0
  10. data/lib/model_context_protocol/server/prompt.rb +108 -14
  11. data/lib/model_context_protocol/server/redis_client_proxy.rb +134 -0
  12. data/lib/model_context_protocol/server/redis_config.rb +108 -0
  13. data/lib/model_context_protocol/server/redis_pool_manager.rb +110 -0
  14. data/lib/model_context_protocol/server/registry.rb +94 -18
  15. data/lib/model_context_protocol/server/resource.rb +98 -25
  16. data/lib/model_context_protocol/server/resource_template.rb +26 -13
  17. data/lib/model_context_protocol/server/router.rb +36 -3
  18. data/lib/model_context_protocol/server/stdio_transport/request_store.rb +102 -0
  19. data/lib/model_context_protocol/server/stdio_transport.rb +31 -6
  20. data/lib/model_context_protocol/server/streamable_http_transport/event_counter.rb +35 -0
  21. data/lib/model_context_protocol/server/streamable_http_transport/message_poller.rb +101 -0
  22. data/lib/model_context_protocol/server/streamable_http_transport/notification_queue.rb +80 -0
  23. data/lib/model_context_protocol/server/streamable_http_transport/request_store.rb +224 -0
  24. data/lib/model_context_protocol/server/streamable_http_transport/session_message_queue.rb +120 -0
  25. data/lib/model_context_protocol/server/{session_store.rb → streamable_http_transport/session_store.rb} +30 -16
  26. data/lib/model_context_protocol/server/streamable_http_transport/stream_registry.rb +119 -0
  27. data/lib/model_context_protocol/server/streamable_http_transport.rb +352 -112
  28. data/lib/model_context_protocol/server/tool.rb +79 -53
  29. data/lib/model_context_protocol/server.rb +124 -21
  30. data/lib/model_context_protocol/version.rb +1 -1
  31. data/tasks/mcp.rake +28 -2
  32. data/tasks/templates/dev-http.erb +288 -0
  33. data/tasks/templates/dev.erb +7 -1
  34. metadata +61 -3
data/README.md CHANGED
@@ -1,18 +1,54 @@
1
1
  # model-context-protocol-rb
2
2
 
3
- An implementation of the [Model Context Protocol (MCP)](https://spec.modelcontextprotocol.io/specification/2024-11-05/) in Ruby.
4
-
5
- This SDK is experimental and subject to change. The initial focus is to implement MCP server support with the goal of providing a stable API by version `0.4`. MCP client support will follow.
6
-
7
- You are welcome to contribute.
8
-
9
- TODO's:
10
-
11
- * [Pagination](https://spec.modelcontextprotocol.io/specification/2024-11-05/server/utilities/pagination/)
12
- * [Prompt list changed notifications](https://spec.modelcontextprotocol.io/specification/2024-11-05/server/prompts/#list-changed-notification)
13
- * [Resource list changed notifications](https://spec.modelcontextprotocol.io/specification/2024-11-05/server/resources/#list-changed-notification)
14
- * [Resource subscriptions](https://spec.modelcontextprotocol.io/specification/2024-11-05/server/resources/#subscriptions)
15
- * [Tool list changed notifications](https://spec.modelcontextprotocol.io/specification/2024-11-05/server/tools/#list-changed-notification)
3
+ An implementation of the [Model Context Protocol (MCP)](https://spec.modelcontextprotocol.io/specification/2025-06-18/) in Ruby.
4
+
5
+ Provides simple abstractions that allow you to serve prompts, resources, resource templates, and tools via MCP locally (stdio) or in production (streamable HTTP backed by Redis) with minimal effort.
6
+
7
+ ## Table of Contents
8
+
9
+ - [Feature Support (Server)](#feature-support-server)
10
+ - [Usage](#usage)
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)
25
+ - [Installation](#installation)
26
+ - [Development](#development)
27
+ - [Releases](#releases)
28
+ - [Contributing](#contributing)
29
+ - [License](#license)
30
+
31
+ ## Feature Support (Server)
32
+
33
+ | Status | Feature |
34
+ |--------|---------|
35
+ | ✅ | [Prompts](https://modelcontextprotocol.io/specification/2025-06-18/server/prompts) |
36
+ | ✅ | [Resources](https://modelcontextprotocol.io/specification/2025-06-18/server/resources) |
37
+ | ✅ | [Resource Templates](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#resource-templates) |
38
+ | ✅ | [Tools](https://modelcontextprotocol.io/specification/2025-06-18/server/tools) |
39
+ | ✅ | [Completion](https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/completion) |
40
+ | ✅ | [Logging](https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/logging) |
41
+ | ✅ | [Pagination](https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/pagination) |
42
+ | ✅ | [Environment Variables](https://modelcontextprotocol.io/legacy/tools/debugging#environment-variables) |
43
+ | ✅ | [STDIO Transport](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#stdio) |
44
+ | ✅ | [Streamable HTTP Transport](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#streamable-http) |
45
+ | ❌ | [List Changed Notification (Prompts)](https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#list-changed-notification) |
46
+ | ❌ | [List Changed Notification (Resources)](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#list-changed-notification) |
47
+ | ❌ | [Subscriptions (Resources)](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#subscriptions) |
48
+ | ❌ | [List Changed Notification (Tools)](https://modelcontextprotocol.io/specification/2025-06-18/server/tools#list-changed-notification) |
49
+ | ✅ | [Cancellation](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/cancellation) |
50
+ | ✅ | [Ping](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/ping) |
51
+ | ✅ | [Progress](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/progress) |
16
52
 
17
53
  ## Usage
18
54
 
@@ -24,27 +60,69 @@ require 'model_context_protocol'
24
60
 
25
61
  ### Building an MCP Server
26
62
 
27
- Build a simple MCP server by registering your prompts, resources, resource templates, and tools. Then, configure and run the server.
63
+ 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.
28
64
 
29
65
  ```ruby
30
66
  server = ModelContextProtocol::Server.new do |config|
31
- config.name = "MCP Development Server"
67
+ # Name of the MCP server (intended for programmatic use)
68
+ config.name = "MCPDevelopmentServer"
69
+
70
+ # Version of the MCP server
32
71
  config.version = "1.0.0"
72
+
73
+ # Optional: human-readable display name for the MCP server
74
+ config.title = "My Awesome Server"
75
+
76
+ # Optional: instuctions for how the MCP server should be used by LLMs
77
+ config.instructions = <<~INSTRUCTIONS
78
+ This server provides file system access and development tools.
79
+
80
+ Key capabilities:
81
+ - Read and write files in the project directory
82
+ - Execute shell commands for development tasks
83
+ - Analyze code structure and dependencies
84
+
85
+ Use this server when you need to interact with the local development environment.
86
+ INSTRUCTIONS
87
+
88
+ # Enable or disable MCP server logging
33
89
  config.logging_enabled = true
34
90
 
35
- # Environment Variables - https://modelcontextprotocol.io/docs/tools/debugging#environment-variables
36
- # Require specific environment variables to be set
91
+ # Configure pagination options for the following methods:
92
+ # prompts/list, resources/list, resource_template/list, tools/list
93
+ config.pagination = {
94
+ default_page_size: 50, # Default items per page
95
+ max_page_size: 500, # Maximum allowed page size
96
+ cursor_ttl: 1800 # Cursor expiry in seconds (30 minutes)
97
+ }
98
+
99
+ # Disable pagination support (enabled by default)
100
+ # config.pagination = false
101
+
102
+ # Optional: require specific environment variables to be set
37
103
  config.require_environment_variable("API_KEY")
38
104
 
39
- # Set environment variables programmatically
105
+ # Optional: set environment variables programmatically
40
106
  config.set_environment_variable("DEBUG_MODE", "true")
41
107
 
42
- # Provide prompts, resources, and tools with contextual variables
108
+ # Optional: provide prompts, resources, and tools with contextual variables
43
109
  config.context = {
44
110
  user_id: "123456",
45
111
  request_id: SecureRandom.uuid
46
112
  }
47
113
 
114
+ # Optional: explicitly specify STDIO as the transport
115
+ # This is not necessary as STDIO is the default transport
116
+ # config.transport = { type: :stdio }
117
+
118
+ # Optional: configure streamable HTTP transport if required
119
+ # config.transport = {
120
+ # type: :streamable_http,
121
+ # env: request.env,
122
+ # session_ttl: 3600 # Optional: session timeout in seconds (default: 3600)
123
+ # }
124
+
125
+ # Register prompts, resources, resource templates, and tools
48
126
  config.registry = ModelContextProtocol::Server::Registry.new do
49
127
  prompts list_changed: true do
50
128
  register TestPrompt
@@ -64,204 +142,439 @@ server = ModelContextProtocol::Server.new do |config|
64
142
  end
65
143
  end
66
144
 
145
+ # Start the MCP server
67
146
  server.start
68
147
  ```
69
148
 
70
- ### Transport Configuration
149
+ #### Server Configuration Options
150
+
151
+ The following table details all available configuration options for the MCP server:
152
+
153
+ | Option | Type | Required | Default | Description |
154
+ |--------|------|----------|---------|-------------|
155
+ | `name` | String | Yes | - | Name of the MCP server for programmatic use |
156
+ | `version` | String | Yes | - | Version of the MCP server |
157
+ | `title` | String | No | - | Human-readable display name for the MCP server |
158
+ | `instructions` | String | No | - | Instructions for how the MCP server should be used by LLMs |
159
+ | `logging_enabled` | Boolean | No | `true` | Enable or disable MCP server logging |
160
+ | `pagination` | Hash/Boolean | No | See pagination table | Pagination configuration (or `false` to disable) |
161
+ | `context` | Hash | No | `{}` | Contextual variables available to prompts, resources, and tools |
162
+ | `transport` | Hash | No | `{ type: :stdio }` | Transport configuration |
163
+ | `registry` | Registry | Yes | - | Registry containing prompts, resources, and tools |
71
164
 
72
- The MCP server supports different transport mechanisms for communication with clients. By default, it uses stdio (standard input/output), but you can also configure it to use streamable HTTP transport for distributed deployments.
165
+ #### Pagination Configuration Options
73
166
 
74
- #### Stdio Transport (Default)
167
+ When `pagination` is set to a Hash, the following options are available:
75
168
 
76
- When no transport is specified, the server uses stdio transport, which is suitable for single-process communication:
169
+ | Option | Type | Required | Default | Description |
170
+ |--------|------|----------|---------|-------------|
171
+ | `default_page_size` | Integer | No | `50` | Default number of items per page |
172
+ | `max_page_size` | Integer | No | `500` | Maximum allowed page size |
173
+ | `cursor_ttl` | Integer | No | `1800` | Cursor expiry time in seconds (30 minutes) |
77
174
 
175
+ **Note:** Set `config.pagination = false` to completely disable pagination support.
176
+
177
+ #### Transport Configuration Options
178
+
179
+ The transport configuration supports two types: `:stdio` (default) and `:streamable_http`.
180
+
181
+ ##### STDIO Transport
78
182
  ```ruby
79
- server = ModelContextProtocol::Server.new do |config|
80
- config.name = "MCP Development Server"
81
- config.version = "1.0.0"
82
- # No transport specified - uses stdio by default
83
- config.registry = ModelContextProtocol::Server::Registry.new
84
- end
183
+ config.transport = { type: :stdio } # This is the default, can be omitted
184
+ ```
85
185
 
86
- server.start
186
+ ##### Streamable HTTP Transport
187
+ The `:streamable_http` transport requires Redis to be configured globally before use:
188
+
189
+ ```ruby
190
+ ModelContextProtocol::Server.configure_redis do |config|
191
+ config.redis_url = ENV.fetch('REDIS_URL')
192
+ config.pool_size = 20
193
+ config.pool_timeout = 5
194
+ config.enable_reaper = true
195
+ config.reaper_interval = 60
196
+ config.idle_timeout = 300
197
+ end
87
198
  ```
88
199
 
89
- #### Streamable HTTP Transport
200
+ | Option | Type | Required | Default | Description |
201
+ |--------|------|----------|---------|-------------|
202
+ | `redis_url` | String | Yes | - | Redis connection URL |
203
+ | `pool_size` | Integer | No | `20` | Connection pool size |
204
+ | `pool_timeout` | Integer | No | `5` | Pool checkout timeout in seconds |
205
+ | `enable_reaper` | Boolean | No | `true` | Enable connection reaping |
206
+ | `reaper_interval` | Integer | No | `60` | Reaper check interval in seconds |
207
+ | `idle_timeout` | Integer | No | `300` | Idle connection timeout in seconds |
90
208
 
91
- For distributed deployments with load balancers and multiple server instances, use the streamable HTTP transport with Redis-backed session management:
209
+ When using `:streamable_http` transport, the following options are available:
92
210
 
93
- ```ruby
94
- require 'redis'
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) |
95
216
 
96
- server = ModelContextProtocol::Server.new do |config|
97
- config.name = "MCP Development Server"
98
- config.version = "1.0.0"
217
+ #### Registry Configuration Options
99
218
 
100
- # Configure streamable HTTP transport
101
- config.transport = {
102
- type: :streamable_http,
103
- redis_client: Redis.new(url: ENV['REDIS_URL']),
104
- session_ttl: 3600 # Optional: session timeout in seconds (default: 3600)
105
- }
219
+ The registry is configured using `ModelContextProtocol::Server::Registry.new` and supports the following block types:
220
+
221
+ | Block Type | Options | Description |
222
+ |------------|---------|-------------|
223
+ | `prompts` | `list_changed: Boolean` | Register prompt handlers with optional list change notifications |
224
+ | `resources` | `list_changed: Boolean`, `subscribe: Boolean` | Register resource handlers with optional list change notifications and subscriptions |
225
+ | `resource_templates` | - | Register resource template handlers |
226
+ | `tools` | `list_changed: Boolean` | Register tool handlers with optional list change notifications |
227
+
228
+ Within each block, use `register ClassName` to register your handlers.
106
229
 
107
- config.registry = ModelContextProtocol::Server::Registry.new
230
+ **Example:**
231
+ ```ruby
232
+ config.registry = ModelContextProtocol::Server::Registry.new do
233
+ prompts list_changed: true do
234
+ register MyPrompt
235
+ register AnotherPrompt
236
+ end
237
+
238
+ resources list_changed: true, subscribe: true do
239
+ register MyResource
240
+ end
241
+
242
+ tools do
243
+ register MyTool
244
+ end
108
245
  end
246
+ ```
247
+
248
+ #### Integration with Rails
249
+
250
+ The streamable HTTP transport works with any valid Rack request. Here's an example of how you can integrate with Rails.
109
251
 
110
- # For HTTP frameworks, handle the request and return the response
111
- result = server.start
112
- # result will be a hash like: {json: {...}, status: 200, headers: {...}}
252
+ First, configure Redis in an initializer:
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
113
264
  ```
114
265
 
115
- **Key Features:**
116
- - **Distributed Sessions**: Redis-backed session storage enables multiple server instances
117
- - **Load Balancer Support**: Sessions persist across different server instances
118
- - **HTTP Methods**: Supports POST (requests), GET (Server-Sent Events), DELETE (cleanup)
119
- - **Cross-Server Routing**: Messages are routed between servers via Redis pub/sub
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
+ ```
120
275
 
121
- **Integration Example (Rails):**
276
+ Then, implement a controller endpoint to handle the requests.
122
277
 
123
278
  ```ruby
124
- class McpController < ApplicationController
279
+ require 'model_context_protocol'
280
+
281
+ class ModelContextProtocolController < ApplicationController
282
+ before_action :authenticate_user
283
+
125
284
  def handle
126
285
  server = ModelContextProtocol::Server.new do |config|
127
- config.name = "Rails MCP Server"
286
+ config.name = "MyMCPServer"
287
+ config.title = "My MCP Server"
128
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
129
295
  config.transport = {
130
296
  type: :streamable_http,
131
- redis_client: Redis.new(url: ENV['REDIS_URL']),
132
- request: request,
133
- response: response
297
+ env: request.env
134
298
  }
135
- config.registry = build_registry
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
136
309
  end
137
310
 
138
311
  result = server.start
139
- render json: result[:json], status: result[:status], headers: result[:headers]
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
140
333
  end
141
334
  end
142
335
  ```
143
336
 
144
- 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.
337
+ ### Prompts
145
338
 
146
- ### Server features
339
+ The `ModelContextProtocol::Server::Prompt` base class allows subclasses to define a prompt that the MCP client can use.
147
340
 
148
- #### Prompts
341
+ Define the prompt properties and then implement the `call` method to build your prompt. 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.
149
342
 
150
- The `ModelContextProtocol::Server::Prompt` base class allows subclasses to define a prompt that the MCP client can use. Define the [appropriate metadata](https://spec.modelcontextprotocol.io/specification/2024-11-05/server/prompts/) in the `with_metadata` block.
343
+ You can also log from within your prompt by calling a valid logger level method on the `logger` and passing a string message.
151
344
 
152
- Define any arguments using the `with_argument` block. You can mark an argument as required, and you can optionally provide a completion class. See [Completions](#completions) for more information.
345
+ #### Prompt Definition
153
346
 
154
- Then implement the `call` method to build your prompt. 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.
347
+ Use the `define` block to set [prompt properties](https://spec.modelcontextprotocol.io/specification/2025-06-18/server/prompts/) and configure arguments.
155
348
 
156
- You can also log from within your prompt by calling a valid logger level method on the `logger` and passing a string message.
349
+ | Property | Description |
350
+ |----------|-------------|
351
+ | `name` | The programmatic name of the prompt |
352
+ | `title` | Human-readable display name |
353
+ | `description` | Short description of what the prompt does |
354
+ | `argument` | Define an argument block with name, description, required flag, and completion |
355
+
356
+ #### Argument Definition
357
+
358
+ 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
+
360
+ | Property | Description |
361
+ |----------|-------------|
362
+ | `name` | The name of the argument |
363
+ | `description` | A short description of the argument |
364
+ | `required` | Whether the argument is required (boolean) |
365
+ | `completion` | Available hints for completions (array or completion class) |
366
+
367
+ #### Prompt Methods
368
+
369
+ 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
+
371
+ | Method | Context | Description |
372
+ |--------|---------|-------------|
373
+ | `define` | Class definition | Block for defining prompt metadata and arguments |
374
+ | `call` | Instance method | Main method to implement prompt logic and build response |
375
+ | `cancellable` | Within `call` | Wrap long-running operations to allow client cancellation (e.g., `cancellable { slow_operation }`) |
376
+ | `progressable` | Within `call` | Wrap long-running operations to send clients progress notifications (e.g., `progressable { slow_operation }`) |
377
+ | `message_history` | Within `call` | DSL method to build an array of user and assistant messages |
378
+ | `respond_with` | Within `call` | Return properly formatted response data (e.g., `respond_with messages:`) |
379
+
380
+ #### Message History DSL
381
+
382
+ 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
+
384
+ | Method | Context | Description |
385
+ |--------|---------|-------------|
386
+ | `user_message` | Within `message_history` | Create a message with user role |
387
+ | `assistant_message` | Within `message_history` | Create a message with assistant role |
388
+
389
+ #### Content Blocks
390
+
391
+ Use content blocks to properly format the content included in messages.
392
+
393
+ | Method | Context | Description |
394
+ |--------|---------|-------------|
395
+ | `text_content` | Within message blocks | Create text content block |
396
+ | `image_content` | Within message blocks | Create image content block (requires `data:` and `mime_type:`) |
397
+ | `audio_content` | Within message blocks | Create audio content block (requires `data:` and `mime_type:`) |
398
+ | `embedded_resource_content` | Within message blocks | Create embedded resource content block (requires `resource:`) |
399
+ | `resource_link` | Within message blocks | Create resource link content block (requires `name:` and `uri:`) |
400
+
401
+ #### Available Instance Variables
402
+
403
+ The `arguments` passed from an MCP client are available, as well as the `context` values passed in at server initialization.
404
+
405
+ | Variable | Context | Description |
406
+ |----------|---------|-------------|
407
+ | `arguments` | Within `call` | Hash containing client-provided arguments (symbol keys) |
408
+ | `context` | Within `call` | Hash containing server configuration context values |
409
+ | `logger` | Within `call` | Logger instance for logging (e.g., `logger.info("message")`) |
410
+
411
+ #### Examples
157
412
 
158
413
  This is an example prompt that returns a properly formatted response:
159
414
 
160
415
  ```ruby
161
416
  class TestPrompt < ModelContextProtocol::Server::Prompt
162
- ToneCompletion = ModelContextProtocol::Server::Completion.define do
163
- hints = ["whiny", "angry", "callous", "desperate", "nervous", "sneaky"]
164
- values = hints.grep(/#{argument_value}/)
165
-
166
- respond_with values:
167
- end
168
-
169
- with_metadata do
417
+ define do
418
+ # The name of the prompt for programmatic use
170
419
  name "brainstorm_excuses"
420
+ # The human-readable prompt name for display in UI
421
+ title "Brainstorm Excuses"
422
+ # A short description of what the tool does
171
423
  description "A prompt for brainstorming excuses to get out of something"
172
- end
173
424
 
174
- with_argument do
175
- name "undesirable_activity"
176
- description "The thing to get out of"
177
- required true
178
- end
425
+ # Define arguments to be used with your prompt
426
+ argument do
427
+ # The name of the argument
428
+ name "tone"
429
+ # A short description of the argument
430
+ description "The general tone to be used in the generated excuses"
431
+ # If the argument is required
432
+ required false
433
+ # Available hints for completions
434
+ completion ["whiny", "angry", "callous", "desperate", "nervous", "sneaky"]
435
+ end
179
436
 
180
- with_argument do
181
- name "tone"
182
- description "The general tone to be used in the generated excuses"
183
- required false
184
- completion ToneCompletion
437
+ argument do
438
+ name "undesirable_activity"
439
+ description "The thing to get out of"
440
+ required true
441
+ end
185
442
  end
186
443
 
444
+ # You can optionally define a custom completion for an argument and pass it to completions.
445
+ # ToneCompletion = ModelContextProtocol::Server::Completion.define do
446
+ # hints = ["whiny", "angry", "callous", "desperate", "nervous", "sneaky"]
447
+ # values = hints.grep(/#{argument_value}/)
448
+ # respond_with values:
449
+ # end
450
+ # ...
451
+ # define do
452
+ # argument do
453
+ # name "tone"
454
+ # description "The general tone to be used in the generated excuses"
455
+ # required false
456
+ # completion ToneCompletion
457
+ # end
458
+ # end
459
+
460
+ # The call method is invoked by the MCP Server to generate a response to resource/read requests
187
461
  def call
462
+ # You can use the logger
188
463
  logger.info("Brainstorming excuses...")
189
- messages = [
190
- {
191
- role: "user",
192
- content: {
193
- type: "text",
194
- text: "My wife wants me to: #{arguments[:undesirable_activity]}... Can you believe it?"
195
- }
196
- },
197
- {
198
- role: "assistant",
199
- content: {
200
- type: "text",
201
- text: "Oh, that's just downright awful. What are you going to do?"
202
- }
203
- },
204
- {
205
- role: "user",
206
- content: {
207
- type: "text",
208
- text: "Well, I'd like to get out of it, but I'm going to need your help."
209
- }
210
- },
211
- {
212
- role: "assistant",
213
- content: {
214
- type: "text",
215
- text: "Anything for you."
216
- }
217
- },
218
- {
219
- role: "user",
220
- content: {
221
- type: "text",
222
- text: "Can you generate some excuses for me?" + (arguments[:tone] ? "Make them as #{arguments[:tone]} as possible." : "")
223
- }
224
- }
225
- ]
226
464
 
227
- respond_with messages: messages
465
+ # Build an array of user and assistant messages
466
+ messages = message_history do
467
+ # Create a message with the user role
468
+ user_message do
469
+ # Use any type of content block in a message (text, image, audio, embedded_resource, or resource_link)
470
+ text_content(text: "My wife wants me to: #{arguments[:undesirable_activity]}... Can you believe it?")
471
+ end
472
+
473
+ # You can also create messages with the assistant role
474
+ assistant_message do
475
+ text_content(text: "Oh, that's just downright awful. How can I help?")
476
+ end
477
+
478
+ user_message do
479
+ # Reference any inputs from the client by accessing the appropriate key in the arguments hash
480
+ text_content(text: "Can you generate some excuses for me?" + (arguments[:tone] ? " Make them as #{arguments[:tone]} as possible." : ""))
481
+ end
482
+ end
483
+
484
+ # Respond with the messages
485
+ respond_with messages:
228
486
  end
229
487
  end
230
488
  ```
231
489
 
232
- #### Resources
490
+ ### Resources
491
+
492
+ The `ModelContextProtocol::Server::Resource` base class allows subclasses to define a resource that the MCP client can use.
493
+
494
+ 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
+
496
+ #### Resource Definition
497
+
498
+ Use the `define` block to set [resource properties](https://spec.modelcontextprotocol.io/specification/2025-06-18/server/resources/) and configure annotations.
499
+
500
+ | Property | Description |
501
+ |----------|-------------|
502
+ | `name` | The name of the resource |
503
+ | `title` | Human-readable display name |
504
+ | `description` | Short description of what the resource contains |
505
+ | `mime_type` | MIME type of the resource content |
506
+ | `uri` | URI identifier for the resource |
507
+ | `annotations` | Block for defining resource annotations |
508
+
509
+ #### Annotation Definition
233
510
 
234
- The `ModelContextProtocol::Server::Resource` base class allows subclasses to define a resource that the MCP client can use. Define the [appropriate metadata](https://spec.modelcontextprotocol.io/specification/2024-11-05/server/resources/) in the `with_metadata` block.
511
+ Define any [resource annotations](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#annotations) using an `annotations` block nested within the `define` block.
235
512
 
236
- Then, implement the `call` method to build your resource. Any context values provided in the server configuration will be available in the `context` hash. Use the `respond_with` instance method to ensure your resource responds with appropriately formatted response data.
513
+ | Property | Description |
514
+ |----------|-------------|
515
+ | `audience` | Target audience for the resource (array of symbols like `[:user, :assistant]`) |
516
+ | `priority` | Priority level (numeric value, e.g., `0.9`) |
517
+ | `last_modified` | Last modified timestamp (ISO 8601 string) |
237
518
 
238
- You can also log from within your resource by calling a valid logger level method on the `logger` and passing a string message.
519
+ #### Resource Methods
520
+
521
+ 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
+
523
+ | Method | Context | Description |
524
+ |--------|---------|-------------|
525
+ | `define` | Class definition | Block for defining resource metadata and annotations |
526
+ | `call` | Instance method | Main method to implement resource logic and build response |
527
+ | `cancellable` | Within `call` | Wrap long-running operations to allow client cancellation (e.g., `cancellable { slow_operation }`) |
528
+ | `progressable` | Within `call` | Wrap long-running operations to send clients progress notifications (e.g., `progressable { slow_operation }`) |
529
+ | `respond_with` | Within `call` | Return properly formatted response data (e.g., `respond_with text:` or `respond_with binary:`) |
530
+
531
+ #### Available Instance Variables
532
+
533
+ Resources are stateless and only have access to their configured properties.
534
+
535
+ | Variable | Context | Description |
536
+ |----------|---------|-------------|
537
+ | `mime_type` | Within `call` | The configured MIME type for this resource |
538
+ | `uri` | Within `call` | The configured URI identifier for this resource |
539
+
540
+ #### Examples
239
541
 
240
542
  This is an example resource that returns a text response:
241
543
 
242
544
  ```ruby
243
545
  class TestResource < ModelContextProtocol::Server::Resource
244
- with_metadata do
546
+ define do
245
547
  name "top-secret-plans.txt"
548
+ title "Top Secret Plans"
246
549
  description "Top secret plans to do top secret things"
247
550
  mime_type "text/plain"
248
551
  uri "file:///top-secret-plans.txt"
249
552
  end
250
553
 
251
554
  def call
252
- unless authorized?(context[:user_id])
253
- logger.info("This fool thinks he can get my top secret plans...")
254
- return respond_with :text, text: "Nothing to see here, move along."
255
- end
256
-
257
- respond_with :text, text: "I'm finna eat all my wife's leftovers."
555
+ respond_with text: "I'm finna eat all my wife's leftovers."
258
556
  end
557
+ end
558
+ ```
259
559
 
260
- private
560
+ This is an example resource with annotations:
561
+
562
+ ```ruby
563
+ class TestAnnotatedResource < ModelContextProtocol::Server::Resource
564
+ define do
565
+ name "annotated-document.md"
566
+ description "A document with annotations showing priority and audience"
567
+ mime_type "text/markdown"
568
+ uri "file:///docs/annotated-document.md"
569
+ annotations do
570
+ audience [:user, :assistant]
571
+ priority 0.9
572
+ last_modified "2025-01-12T15:00:58Z"
573
+ end
574
+ end
261
575
 
262
- def authorized?(user_id)
263
- authorized_users = ["42", "123456"]
264
- authorized_users.any?(user_id)
576
+ def call
577
+ respond_with text: "# Annotated Document\n\nThis document has annotations."
265
578
  end
266
579
  end
267
580
  ```
@@ -270,7 +583,7 @@ This is an example resource that returns binary data:
270
583
 
271
584
  ```ruby
272
585
  class TestBinaryResource < ModelContextProtocol::Server::Resource
273
- with_metadata do
586
+ define do
274
587
  name "project-logo.png"
275
588
  description "The logo for the project"
276
589
  mime_type "image/png"
@@ -281,53 +594,228 @@ class TestBinaryResource < ModelContextProtocol::Server::Resource
281
594
  # In a real implementation, we would retrieve the binary resource
282
595
  # This is a small valid base64 encoded string (represents "test")
283
596
  data = "dGVzdA=="
284
- respond_with :binary, blob: data
597
+ respond_with binary: data
285
598
  end
286
599
  end
287
600
  ```
288
601
 
289
- #### Resource Templates
602
+ ### Resource Templates
603
+
604
+ The `ModelContextProtocol::Server::ResourceTemplate` base class allows subclasses to define a resource template that the MCP client can use.
605
+
606
+ 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
+
608
+ #### Resource Template Definition
290
609
 
291
- The `ModelContextProtocol::Server::ResourceTemplate` base class allows subclasses to define a resource template that the MCP client can use. Define the [appropriate metadata](https://modelcontextprotocol.io/specification/2024-11-05/server/resources#resource-templates) in the `with_metadata` block.
610
+ Use the `define` block to set [resource template properties](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#resource-templates).
611
+
612
+ | Property | Description |
613
+ |----------|-------------|
614
+ | `name` | The name of the resource template |
615
+ | `description` | Short description of what the template provides |
616
+ | `mime_type` | MIME type of resources created from this template |
617
+ | `uri_template` | URI template with parameters (e.g., `"file:///{name}"`) |
618
+
619
+ #### URI Template Configuration
620
+
621
+ Define the URI template and configure parameter completions within the `uri_template` block.
622
+
623
+ | Method | Context | Description |
624
+ |--------|---------|-------------|
625
+ | `completion` | Within `uri_template` block | Define completion for a URI parameter (e.g., `completion :name, ["value1", "value2"]`) |
626
+
627
+ #### Resource Template Methods
628
+
629
+ Resource templates only use the `define` method to configure their properties - they don't have a `call` method.
630
+
631
+ | Method | Context | Description |
632
+ |--------|---------|-------------|
633
+ | `define` | Class definition | Block for defining resource template metadata and URI template |
634
+
635
+ #### Examples
292
636
 
293
637
  This is an example resource template that provides a completion for a parameter of the URI template:
294
638
 
295
639
  ```ruby
296
640
  class TestResourceTemplate < ModelContextProtocol::Server::ResourceTemplate
297
- Completion = ModelContextProtocol::Server::Completion.define do
298
- hints = {
299
- "name" => ["top-secret-plans.txt"]
300
- }
301
- values = hints[argument_name].grep(/#{argument_value}/)
302
-
303
- respond_with values:
304
- end
305
-
306
- with_metadata do
641
+ define do
307
642
  name "project-document-resource-template"
308
643
  description "A resource template for retrieving project documents"
309
644
  mime_type "text/plain"
310
645
  uri_template "file:///{name}" do
311
- completion :name, Completion
646
+ completion :name, ["top-secret-plans.txt"]
312
647
  end
313
648
  end
649
+
650
+ # You can optionally define a custom completion for an argument and pass it to completions.
651
+ # Completion = ModelContextProtocol::Server::Completion.define do
652
+ # hints = {
653
+ # "name" => ["top-secret-plans.txt"]
654
+ # }
655
+ # values = hints[argument_name].grep(/#{argument_value}/)
656
+
657
+ # respond_with values:
658
+ # end
659
+
660
+ # define do
661
+ # name "project-document-resource-template"
662
+ # description "A resource template for retrieving project documents"
663
+ # mime_type "text/plain"
664
+ # uri_template "file:///{name}" do
665
+ # completion :name, Completion
666
+ # end
667
+ # end
314
668
  end
315
669
  ```
316
670
 
317
- #### Tools
671
+ ### Tools
672
+
673
+ The `ModelContextProtocol::Server::Tool` base class allows subclasses to define a tool that the MCP client can use.
674
+
675
+ 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
+
677
+ #### Tool Definition
678
+
679
+ Use the `define` block to set [tool properties](https://spec.modelcontextprotocol.io/specification/2025-06-18/server/tools/) and configure schemas.
680
+
681
+ | Property | Description |
682
+ |----------|-------------|
683
+ | `name` | The programmatic name of the tool |
684
+ | `title` | Human-readable display name |
685
+ | `description` | Short description of what the tool does |
686
+ | `input_schema` | JSON schema block for validating tool inputs |
687
+ | `output_schema` | JSON schema block for validating structured content outputs |
688
+
689
+ #### Tool Methods
690
+
691
+ 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
+
693
+ | Method | Context | Description |
694
+ |--------|---------|-------------|
695
+ | `define` | Class definition | Block for defining tool metadata and schemas |
696
+ | `call` | Instance method | Main method to implement tool logic and build response |
697
+ | `cancellable` | Within `call` | Wrap long-running operations to allow client cancellation (e.g., `cancellable { slow_operation }`) |
698
+ | `progressable` | Within `call` | Wrap long-running operations to send clients progress notifications (e.g., `progressable { slow_operation }`) |
699
+ | `respond_with` | Within `call` | Return properly formatted response data with various content types |
700
+
701
+ #### Content Blocks
702
+
703
+ Use content blocks to properly format the content included in tool responses.
704
+
705
+ | Method | Context | Description |
706
+ |--------|---------|-------------|
707
+ | `text_content` | Within `call` | Create text content block |
708
+ | `image_content` | Within `call` | Create image content block (requires `data:` and `mime_type:`) |
709
+ | `audio_content` | Within `call` | Create audio content block (requires `data:` and `mime_type:`) |
710
+ | `embedded_resource_content` | Within `call` | Create embedded resource content block (requires `resource:`) |
711
+ | `resource_link` | Within `call` | Create resource link content block (requires `name:` and `uri:`) |
318
712
 
319
- The `ModelContextProtocol::Server::Tool` base class allows subclasses to define a tool that the MCP client can use. Define the [appropriate metadata](https://spec.modelcontextprotocol.io/specification/2024-11-05/server/tools/) in the `with_metadata` block.
713
+ #### Response Types
320
714
 
321
- 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 tool responds with appropriately formatted response data.
715
+ Tools can return different types of responses using `respond_with`.
322
716
 
323
- You can also log from within your tool by calling a valid logger level method on the `logger` and passing a string message.
717
+ | Response Type | Usage | Description |
718
+ |---------------|-------|-------------|
719
+ | `structured_content:` | `respond_with structured_content: data` | Return structured data validated against output schema |
720
+ | `content:` | `respond_with content: content_block` | Return single content block |
721
+ | `content:` | `respond_with content: [content_blocks]` | Return array of mixed content blocks |
722
+ | `error:` | `respond_with error: "message"` | Return tool error response |
723
+
724
+ #### Available Instance Variables
725
+
726
+ Arguments from MCP clients and server context are available, along with logging capabilities.
727
+
728
+ | Variable | Context | Description |
729
+ |----------|---------|-------------|
730
+ | `arguments` | Within `call` | Hash containing client-provided arguments (symbol keys) |
731
+ | `context` | Within `call` | Hash containing server configuration context values |
732
+ | `logger` | Within `call` | Logger instance for logging (e.g., `logger.info("message")`) |
733
+
734
+ #### Examples
735
+
736
+ This is an example of a tool that returns structured content validated by an output schema:
737
+
738
+ ```ruby
739
+ class TestToolWithStructuredContentResponse < ModelContextProtocol::Server::Tool
740
+ define do
741
+ # The name of the tool for programmatic use
742
+ name "get_weather_data"
743
+ # The human-readable tool name for display in UI
744
+ title "Weather Data Retriever"
745
+ # A short description of what the tool does
746
+ description "Get current weather data for a location"
747
+ # The JSON schema for validating tool inputs
748
+ input_schema do
749
+ {
750
+ type: "object",
751
+ properties: {
752
+ location: {
753
+ type: "string",
754
+ description: "City name or zip code"
755
+ }
756
+ },
757
+ required: ["location"]
758
+ }
759
+ end
760
+ # The JSON schema for validating structured content
761
+ output_schema do
762
+ {
763
+ type: "object",
764
+ properties: {
765
+ temperature: {
766
+ type: "number",
767
+ description: "Temperature in celsius"
768
+ },
769
+ conditions: {
770
+ type: "string",
771
+ description: "Weather conditions description"
772
+ },
773
+ humidity: {
774
+ type: "number",
775
+ description: "Humidity percentage"
776
+ }
777
+ },
778
+ required: ["temperature", "conditions", "humidity"]
779
+ }
780
+ end
781
+ end
782
+
783
+ def call
784
+ # Use values provided by the server as context
785
+ user_id = context[:user_id]
786
+ logger.info("Initiating request for user #{user_id}...")
787
+
788
+ # Use values provided by clients as tool arguments
789
+ location = arguments[:location]
790
+ logger.info("Getting weather data for #{location}...")
791
+
792
+ # Returns a hash that validates against the output schema
793
+ weather_data = get_weather_data(location)
794
+
795
+ # Respond with structured content
796
+ respond_with structured_content: weather_data
797
+ end
798
+
799
+ private
800
+
801
+ # Simulate calling an external API to get weather data for the provided input
802
+ def get_weather_data(location)
803
+ {
804
+ temperature: 22.5,
805
+ conditions: "Partly cloudy",
806
+ humidity: 65
807
+ }
808
+ end
809
+ end
810
+ ```
324
811
 
325
812
  This is an example tool that returns a text response:
326
813
 
327
814
  ```ruby
328
815
  class TestToolWithTextResponse < ModelContextProtocol::Server::Tool
329
- with_metadata do
816
+ define do
330
817
  name "double"
818
+ title "Number Doubler"
331
819
  description "Doubles the provided number"
332
820
  input_schema do
333
821
  {
@@ -343,12 +831,15 @@ class TestToolWithTextResponse < ModelContextProtocol::Server::Tool
343
831
  end
344
832
 
345
833
  def call
346
- user_id = context[:user_id]
347
- number = arguments[:number].to_i
348
834
  logger.info("Silly user doesn't know how to double a number")
835
+ number = arguments[:number].to_i
349
836
  calculation = number * 2
837
+
838
+ user_id = context[:user_id]
350
839
  salutation = user_id ? "User #{user_id}, " : ""
351
- respond_with :text, text: salutation << "#{number} doubled is #{calculation}"
840
+ text_content = text_content(text: salutation << "#{number} doubled is #{calculation}")
841
+
842
+ respond_with content: text_content
352
843
  end
353
844
  end
354
845
  ```
@@ -357,7 +848,7 @@ This is an example of a tool that returns an image:
357
848
 
358
849
  ```ruby
359
850
  class TestToolWithImageResponse < ModelContextProtocol::Server::Tool
360
- with_metadata do
851
+ define do
361
852
  name "custom-chart-generator"
362
853
  description "Generates a chart in various formats"
363
854
  input_schema do
@@ -391,68 +882,216 @@ class TestToolWithImageResponse < ModelContextProtocol::Server::Tool
391
882
 
392
883
  # In a real implementation, we would generate an actual chart
393
884
  # This is a small valid base64 encoded string (represents "test")
394
- chart_data = "dGVzdA=="
395
- respond_with :image, data: chart_data, mime_type:
885
+ data = "dGVzdA=="
886
+ image_content = image_content(data:, mime_type:)
887
+ respond_with content: image_content
396
888
  end
397
889
  end
398
890
  ```
399
891
 
400
- If you don't provide a mime type, it will default to `image/png`.
892
+ This is an example of a tool that returns an embedded resource response:
401
893
 
402
894
  ```ruby
403
- class TestToolWithImageResponseDefaultMimeType < ModelContextProtocol::Server::Tool
404
- with_metadata do
405
- name "other-custom-chart-generator"
406
- description "Generates a chart"
895
+ class TestToolWithResourceResponse < ModelContextProtocol::Server::Tool
896
+ define do
897
+ name "resource-finder"
898
+ description "Finds a resource given a name"
407
899
  input_schema do
408
900
  {
409
901
  type: "object",
410
902
  properties: {
411
- chart_type: {
903
+ name: {
412
904
  type: "string",
413
- description: "Type of chart (pie, bar, line)"
905
+ description: "The name of the resource"
414
906
  }
415
907
  },
416
- required: ["chart_type"]
908
+ required: ["name"]
417
909
  }
418
910
  end
419
911
  end
420
912
 
913
+ RESOURCE_MAPPINGS = {
914
+ test_annotated_resource: TestAnnotatedResource,
915
+ test_binary_resource: TestBinaryResource,
916
+ test_resource: TestResource
917
+ }.freeze
918
+
421
919
  def call
422
- # In a real implementation, we would generate an actual chart
423
- # This is a small valid base64 encoded string (represents "test")
424
- chart_data = "dGVzdA=="
425
- respond_with :image, data: chart_data
920
+ name = arguments[:name]
921
+ resource_klass = RESOURCE_MAPPINGS[name.downcase.to_sym]
922
+ unless resource_klass
923
+ return respond_with :error, text: "Resource `#{name}` not found"
924
+ end
925
+
926
+ resource_data = resource_klass.call
927
+
928
+ respond_with content: embedded_resource_content(resource: resource_data)
426
929
  end
427
930
  end
428
931
  ```
429
932
 
430
- This is an example of a tool that returns a resource response:
933
+ This is an example of a tool that returns mixed content:
431
934
 
432
935
  ```ruby
433
- class TestToolWithResourceResponse < ModelContextProtocol::Server::Tool
434
- with_metadata do
435
- name "document-finder"
436
- description "Finds a the document with the given title"
936
+ class TestToolWithMixedContentResponse < ModelContextProtocol::Server::Tool
937
+ define do
938
+ name "get_temperature_history"
939
+ description "Gets comprehensive temperature history for a zip code"
940
+ input_schema do
941
+ {
942
+ type: "object",
943
+ properties: {
944
+ zip: {
945
+ type: "string"
946
+ }
947
+ },
948
+ required: ["zip"]
949
+ }
950
+ end
951
+ end
952
+
953
+ def call
954
+ logger.info("Getting comprehensive temperature history data")
955
+
956
+ zip = arguments[:zip]
957
+ temperature_history = retrieve_temperature_history(zip:)
958
+ temperature_history_block = text_content(text: temperature_history.join(", "))
959
+
960
+ temperature_chart = generate_weather_history_chart(temperature_history)
961
+ temperature_chart_block = image_content(
962
+ data: temperature_chart[:base64_chart_data],
963
+ mime_type: temperature_chart[:mime_type]
964
+ )
965
+
966
+ respond_with content: [temperature_history_block, temperature_chart_block]
967
+ end
968
+
969
+ private
970
+
971
+ def retrieve_temperature_history(zip:)
972
+ # Simulates a call to an API or DB to retrieve weather history
973
+ [85.2, 87.4, 89.0, 95.3, 96.0]
974
+ end
975
+
976
+ def generate_weather_history_chart(history)
977
+ # SImulate a call to generate a chart given the weather history
978
+ {
979
+ base64_chart_data: "dGVzdA==",
980
+ mime_type: "image/png"
981
+ }
982
+ end
983
+ end
984
+ ```
985
+
986
+ This is an example of a tool that returns a tool error response:
987
+
988
+ ```ruby
989
+ class TestToolWithToolErrorResponse < ModelContextProtocol::Server::Tool
990
+ define do
991
+ name "api-caller"
992
+ description "Makes calls to external APIs"
437
993
  input_schema do
438
994
  {
439
995
  type: "object",
440
996
  properties: {
441
- title: {
997
+ api_endpoint: {
998
+ type: "string",
999
+ description: "API endpoint URL"
1000
+ },
1001
+ method: {
442
1002
  type: "string",
443
- description: "The title of the document"
1003
+ description: "HTTP method (GET, POST, etc)"
444
1004
  }
445
1005
  },
446
- required: ["title"]
1006
+ required: ["api_endpoint", "method"]
1007
+ }
1008
+ end
1009
+ end
1010
+
1011
+ def call
1012
+ # Simulate an API call failure
1013
+ respond_with error: "Failed to call API at #{arguments[:api_endpoint]}: Connection timed out"
1014
+ end
1015
+ end
1016
+ ```
1017
+
1018
+ This is an example of a tool that allows a client to cancel a long-running operation:
1019
+
1020
+ ```ruby
1021
+ class TestToolWithCancellableSleep < ModelContextProtocol::Server::Tool
1022
+ define do
1023
+ name "cancellable_sleep"
1024
+ title "Cancellable Sleep Tool"
1025
+ description "Sleep for 3 seconds with cancellation support"
1026
+ input_schema do
1027
+ {
1028
+ type: "object",
1029
+ properties: {},
1030
+ additionalProperties: false
447
1031
  }
448
1032
  end
449
1033
  end
450
1034
 
451
1035
  def call
452
- title = arguments[:title].downcase
453
- # In a real implementation, we would do a lookup to get the document data
454
- document = "richtextdata"
455
- respond_with :resource, uri: "resource://document/#{title}", text: document, mime_type: "application/rtf"
1036
+ logger.info("Starting 3 second sleep operation")
1037
+
1038
+ result = cancellable do
1039
+ sleep 3
1040
+ "Sleep completed successfully"
1041
+ end
1042
+
1043
+ respond_with content: text_content(text: result)
1044
+ end
1045
+ end
1046
+ ```
1047
+
1048
+ This is an example of a tool that automatically sends progress notifications to the client and allows the client to cancel the operation:
1049
+
1050
+ ```ruby
1051
+ class TestToolWithProgressableAndCancellable < ModelContextProtocol::Server::Tool
1052
+ define do
1053
+ name "test_tool_with_progressable_and_cancellable"
1054
+ description "A test tool that demonstrates combined progressable and cancellable functionality"
1055
+
1056
+ input_schema do
1057
+ {
1058
+ type: "object",
1059
+ properties: {
1060
+ max_duration: {
1061
+ type: "number",
1062
+ description: "Expected maximum duration in seconds"
1063
+ },
1064
+ work_steps: {
1065
+ type: "number",
1066
+ description: "Number of work steps to perform"
1067
+ }
1068
+ },
1069
+ required: ["max_duration"]
1070
+ }
1071
+ end
1072
+ end
1073
+
1074
+ def call
1075
+ max_duration = arguments[:max_duration] || 10
1076
+ work_steps = arguments[:work_steps] || 10
1077
+ logger.info("Starting progressable call with max_duration=#{max_duration}, work_steps=#{work_steps}")
1078
+
1079
+ result = progressable(max_duration:, message: "Processing #{work_steps} items") do
1080
+ cancellable do
1081
+ processed_items = []
1082
+
1083
+ work_steps.times do |i|
1084
+ sleep(max_duration / work_steps.to_f)
1085
+ processed_items << "item_#{i + 1}"
1086
+ end
1087
+
1088
+ processed_items
1089
+ end
1090
+ end
1091
+
1092
+ response = text_content(text: "Successfully processed #{result.length} items: #{result.join(", ")}")
1093
+
1094
+ respond_with content: response
456
1095
  end
457
1096
  end
458
1097
  ```
@@ -461,7 +1100,27 @@ end
461
1100
 
462
1101
  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.
463
1102
 
464
- implement the `call` method to build your completion. Use the `respond_with` instance method to ensure your completion responds with appropriately formatted response data.
1103
+ 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
+
1105
+ #### Completion Methods
1106
+
1107
+ Completions only implement the `call` method to provide completion logic.
1108
+
1109
+ | Method | Context | Description |
1110
+ |--------|---------|-------------|
1111
+ | `call` | Instance method | Main method to implement completion logic and build response |
1112
+ | `respond_with` | Within `call` | Return properly formatted completion response (e.g., `respond_with values:`) |
1113
+
1114
+ #### Available Instance Variables
1115
+
1116
+ Completions receive the argument name and current value being completed.
1117
+
1118
+ | Variable | Context | Description |
1119
+ |----------|---------|-------------|
1120
+ | `argument_name` | Within `call` | String name of the argument being completed |
1121
+ | `argument_value` | Within `call` | Current partial value being typed by the user |
1122
+
1123
+ #### Examples
465
1124
 
466
1125
  This is an example completion that returns an array of values in the response:
467
1126
 
@@ -500,19 +1159,50 @@ gem install model-context-protocol-rb
500
1159
 
501
1160
  ## Development
502
1161
 
503
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
1162
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests.
1163
+
1164
+ ### Generate Development Servers
1165
+
1166
+ Generate executables that you can use for testing:
1167
+
1168
+ ```bash
1169
+ # generates bin/dev for STDIO transport
1170
+ bundle exec rake mcp:generate_stdio_server
1171
+
1172
+ # generates bin/dev-http for streamable HTTP transport
1173
+ bundle exec rake mcp:generate_streamable_http_server
1174
+ ```
504
1175
 
505
- Generate an executable that you can use for testing:
1176
+ If you need to test with HTTPS (e.g., for clients that require SSL), generate self-signed certificates:
506
1177
 
507
1178
  ```bash
508
- bundle exec rake mcp:generate_executable
1179
+ # Create SSL directory and generate certificates
1180
+ mkdir -p tmp/ssl
1181
+ openssl req -x509 -newkey rsa:4096 -keyout tmp/ssl/server.key -out tmp/ssl/server.crt -days 365 -nodes -subj "/C=US/ST=Dev/L=Dev/O=Dev/CN=localhost"
509
1182
  ```
510
1183
 
511
- This will generate a `bin/dev` executable you can provide to MCP clients.
1184
+ The HTTP server supports both HTTP and HTTPS:
1185
+
1186
+ ```bash
1187
+ # Run HTTP server (default)
1188
+ bin/dev-http
1189
+
1190
+ # Run HTTPS server (requires SSL certificates in tmp/ssl/)
1191
+ SSL=true bin/dev-http
1192
+ ```
1193
+
1194
+ You can also run `bin/console` for an interactive prompt that will allow you to experiment. Execute command `rp` to reload the project.
1195
+
1196
+ To install this gem onto your local machine, run `bundle exec rake install`.
1197
+
1198
+ ### Releases
512
1199
 
513
- You can also run `bin/console` for an interactive prompt that will allow you to experiment.
1200
+ To release a new version, update the version number in `version.rb`, and submit a PR. After the PR has been merged to main, run `bundle exec rake release`, which will:
1201
+ * create a git tag for the version,
1202
+ * push the created tag,
1203
+ * and push the `.gem` file to [rubygems.org](https://rubygems.org).
514
1204
 
515
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
1205
+ Then, draft and publish release notes in Github.
516
1206
 
517
1207
  ## Contributing
518
1208