model-context-protocol-rb 0.5.1 → 0.7.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -1
  3. data/README.md +181 -950
  4. data/lib/model_context_protocol/rspec/helpers.rb +54 -0
  5. data/lib/model_context_protocol/rspec/matchers/be_mcp_error_response.rb +123 -0
  6. data/lib/model_context_protocol/rspec/matchers/be_valid_mcp_class.rb +103 -0
  7. data/lib/model_context_protocol/rspec/matchers/be_valid_mcp_prompt_response.rb +126 -0
  8. data/lib/model_context_protocol/rspec/matchers/be_valid_mcp_resource_response.rb +121 -0
  9. data/lib/model_context_protocol/rspec/matchers/be_valid_mcp_tool_response.rb +135 -0
  10. data/lib/model_context_protocol/rspec/matchers/have_audio_content.rb +109 -0
  11. data/lib/model_context_protocol/rspec/matchers/have_embedded_resource_content.rb +150 -0
  12. data/lib/model_context_protocol/rspec/matchers/have_image_content.rb +109 -0
  13. data/lib/model_context_protocol/rspec/matchers/have_message_count.rb +87 -0
  14. data/lib/model_context_protocol/rspec/matchers/have_message_with_role.rb +152 -0
  15. data/lib/model_context_protocol/rspec/matchers/have_resource_annotations.rb +135 -0
  16. data/lib/model_context_protocol/rspec/matchers/have_resource_blob.rb +108 -0
  17. data/lib/model_context_protocol/rspec/matchers/have_resource_link_content.rb +138 -0
  18. data/lib/model_context_protocol/rspec/matchers/have_resource_mime_type.rb +103 -0
  19. data/lib/model_context_protocol/rspec/matchers/have_resource_text.rb +112 -0
  20. data/lib/model_context_protocol/rspec/matchers/have_structured_content.rb +88 -0
  21. data/lib/model_context_protocol/rspec/matchers/have_text_content.rb +113 -0
  22. data/lib/model_context_protocol/rspec/matchers.rb +31 -0
  23. data/lib/model_context_protocol/rspec.rb +23 -0
  24. data/lib/model_context_protocol/server/cancellable.rb +5 -5
  25. data/lib/model_context_protocol/server/{mcp_logger.rb → client_logger.rb} +8 -11
  26. data/lib/model_context_protocol/server/configuration.rb +196 -109
  27. data/lib/model_context_protocol/server/content_helpers.rb +1 -1
  28. data/lib/model_context_protocol/server/global_config/server_logging.rb +78 -0
  29. data/lib/model_context_protocol/server/progressable.rb +43 -21
  30. data/lib/model_context_protocol/server/prompt.rb +12 -21
  31. data/lib/model_context_protocol/server/redis_client_proxy.rb +2 -14
  32. data/lib/model_context_protocol/server/redis_config.rb +5 -7
  33. data/lib/model_context_protocol/server/redis_pool_manager.rb +11 -14
  34. data/lib/model_context_protocol/server/registry.rb +8 -0
  35. data/lib/model_context_protocol/server/resource.rb +7 -4
  36. data/lib/model_context_protocol/server/router.rb +285 -9
  37. data/lib/model_context_protocol/server/server_logger.rb +31 -0
  38. data/lib/model_context_protocol/server/stdio_configuration.rb +114 -0
  39. data/lib/model_context_protocol/server/stdio_transport/request_store.rb +12 -53
  40. data/lib/model_context_protocol/server/stdio_transport.rb +18 -12
  41. data/lib/model_context_protocol/server/streamable_http_configuration.rb +218 -0
  42. data/lib/model_context_protocol/server/streamable_http_transport/event_counter.rb +0 -13
  43. data/lib/model_context_protocol/server/streamable_http_transport/message_poller.rb +9 -9
  44. data/lib/model_context_protocol/server/streamable_http_transport/notification_queue.rb +0 -41
  45. data/lib/model_context_protocol/server/streamable_http_transport/request_store.rb +21 -124
  46. data/lib/model_context_protocol/server/streamable_http_transport/server_request_store.rb +167 -0
  47. data/lib/model_context_protocol/server/streamable_http_transport/session_message_queue.rb +0 -58
  48. data/lib/model_context_protocol/server/streamable_http_transport/session_store.rb +17 -31
  49. data/lib/model_context_protocol/server/streamable_http_transport/stream_registry.rb +0 -34
  50. data/lib/model_context_protocol/server/streamable_http_transport.rb +589 -215
  51. data/lib/model_context_protocol/server/tool.rb +73 -6
  52. data/lib/model_context_protocol/server.rb +204 -261
  53. data/lib/model_context_protocol/version.rb +1 -1
  54. data/lib/model_context_protocol.rb +4 -1
  55. data/lib/puma/plugin/mcp.rb +39 -0
  56. data/tasks/mcp.rake +26 -0
  57. data/tasks/templates/dev-http-puma.erb +251 -0
  58. data/tasks/templates/dev-http.erb +166 -184
  59. data/tasks/templates/dev.erb +29 -7
  60. metadata +33 -6
data/README.md CHANGED
@@ -4,26 +4,6 @@ An implementation of the [Model Context Protocol (MCP)](https://spec.modelcontex
4
4
 
5
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
6
 
7
- ## Table of Contents
8
-
9
- - [Feature Support (Server)](#feature-support-server)
10
- - [Quick Start with Rails](#quick-start-with-rails)
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)
23
- - [Development](#development)
24
- - [Contributing](#contributing)
25
- - [License](#license)
26
-
27
7
  ## Feature Support (Server)
28
8
 
29
9
  | Status | Feature |
@@ -38,131 +18,14 @@ Provides simple abstractions that allow you to serve prompts, resources, resourc
38
18
  | ✅ | [Environment Variables](https://modelcontextprotocol.io/legacy/tools/debugging#environment-variables) |
39
19
  | ✅ | [STDIO Transport](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#stdio) |
40
20
  | ✅ | [Streamable HTTP Transport](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#streamable-http) |
41
- | | [List Changed Notification (Prompts)](https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#list-changed-notification) |
42
- | | [List Changed Notification (Resources)](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#list-changed-notification) |
21
+ | | [List Changed Notification (Prompts)](https://modelcontextprotocol.io/specification/2025-06-18/server/prompts#list-changed-notification) ¹ |
22
+ | | [List Changed Notification (Resources)](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#list-changed-notification) ¹ |
43
23
  | ❌ | [Subscriptions (Resources)](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#subscriptions) |
44
- | | [List Changed Notification (Tools)](https://modelcontextprotocol.io/specification/2025-06-18/server/tools#list-changed-notification) |
24
+ | | [List Changed Notification (Tools)](https://modelcontextprotocol.io/specification/2025-06-18/server/tools#list-changed-notification) ¹ |
45
25
  | ✅ | [Cancellation](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/cancellation) |
46
26
  | ✅ | [Ping](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/ping) |
47
27
  | ✅ | [Progress](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/progress) |
48
28
 
49
- ## Quick Start with Rails
50
-
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
-
57
- ```ruby
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
69
- ```
70
-
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
29
  ## Installation
167
30
 
168
31
  Add this line to your application's Gemfile:
@@ -183,609 +46,249 @@ Or install it yourself as:
183
46
  gem install model-context-protocol-rb
184
47
  ```
185
48
 
49
+ For detailed installation instructions, see the [Installation wiki page](https://github.com/dickdavis/model-context-protocol-rb/wiki/Installation).
50
+
186
51
  ## Building an MCP Server
187
52
 
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.
53
+ Build a simple MCP server by registering your prompts, resources, resource templates, and tools. Messages from the MCP client will be routed to the appropriate custom handler.
189
54
 
190
- ```ruby
191
- server = ModelContextProtocol::Server.new do |config|
192
- # Name of the MCP server (intended for programmatic use)
193
- config.name = "MCPDevelopmentServer"
55
+ ### STDIO Transport (Default)
194
56
 
195
- # Version of the MCP server
196
- config.version = "1.0.0"
57
+ For command-line MCP servers that communicate via standard input/output:
197
58
 
198
- # Optional: human-readable display name for the MCP server
199
- config.title = "My Awesome Server"
200
-
201
- # Optional: instuctions for how the MCP server should be used by LLMs
59
+ ```ruby
60
+ server = ModelContextProtocol::Server.with_stdio_transport do |config|
61
+ config.name = "MyMCPServer"
62
+ config.title = "My Server"
63
+ config.version = "1.0.0"
202
64
  config.instructions = <<~INSTRUCTIONS
203
- This server provides file system access and development tools.
65
+ This server provides prompts, tools, and resources for interacting with my app.
204
66
 
205
67
  Key capabilities:
206
- - Read and write files in the project directory
207
- - Execute shell commands for development tasks
208
- - Analyze code structure and dependencies
68
+ - Does something
69
+ - Does something else
70
+ - Oh, and it does this other thing, too
209
71
 
210
- Use this server when you need to interact with the local development environment.
72
+ Use this server when you need to do things.
211
73
  INSTRUCTIONS
212
74
 
213
- # Enable or disable MCP server logging
214
- config.logging_enabled = true
215
-
216
- # Configure pagination options for the following methods:
217
- # prompts/list, resources/list, resource_template/list, tools/list
218
- config.pagination = {
219
- default_page_size: 50, # Default items per page
220
- max_page_size: 500, # Maximum allowed page size
221
- cursor_ttl: 1800 # Cursor expiry in seconds (30 minutes)
222
- }
223
-
224
- # Disable pagination support (enabled by default)
225
- # config.pagination = false
226
-
227
- # Optional: require specific environment variables to be set
228
- config.require_environment_variable("API_KEY")
229
-
230
- # Optional: set environment variables programmatically
231
- config.set_environment_variable("DEBUG_MODE", "true")
232
-
233
- # Optional: provide prompts, resources, and tools with contextual variables
234
- config.context = {
235
- user_id: "123456",
236
- request_id: SecureRandom.uuid
237
- }
238
-
239
- # Optional: explicitly specify STDIO as the transport
240
- # This is not necessary as STDIO is the default transport
241
- # config.transport = { type: :stdio }
242
-
243
- # Optional: configure streamable HTTP transport if required
244
- # config.transport = {
245
- # type: :streamable_http,
246
- # env: request.env,
247
- # session_ttl: 3600 # Optional: session timeout in seconds (default: 3600)
248
- # }
249
-
250
- # Register prompts, resources, resource templates, and tools
251
- config.registry = ModelContextProtocol::Server::Registry.new do
252
- prompts list_changed: true do
253
- register TestPrompt
75
+ config.registry do
76
+ prompts do
77
+ register MyPrompt
254
78
  end
255
79
 
256
- resources list_changed: true, subscribe: true do
257
- register TestResource
80
+ resources do
81
+ register MyResource
258
82
  end
259
83
 
260
- resource_templates do
261
- register TestResourceTemplate
262
- end
263
-
264
- tools list_changed: true do
265
- register TestTool
84
+ tools do
85
+ register MyTool
266
86
  end
267
87
  end
268
88
  end
269
89
 
270
- # Start the MCP server
271
90
  server.start
272
91
  ```
273
92
 
274
- ### Server Configuration Options
275
-
276
- The following table details all available configuration options for the MCP server:
93
+ ### Streamable HTTP Transport
277
94
 
278
- | Option | Type | Required | Default | Description |
279
- |--------|------|----------|---------|-------------|
280
- | `name` | String | Yes | - | Name of the MCP server for programmatic use |
281
- | `version` | String | Yes | - | Version of the MCP server |
282
- | `title` | String | No | - | Human-readable display name for the MCP server |
283
- | `instructions` | String | No | - | Instructions for how the MCP server should be used by LLMs |
284
- | `logging_enabled` | Boolean | No | `true` | Enable or disable MCP server logging |
285
- | `pagination` | Hash/Boolean | No | See pagination table | Pagination configuration (or `false` to disable) |
286
- | `context` | Hash | No | `{}` | Contextual variables available to prompts, resources, and tools |
287
- | `transport` | Hash | No | `{ type: :stdio }` | Transport configuration |
288
- | `registry` | Registry | Yes | - | Registry containing prompts, resources, and tools |
95
+ For HTTP-based MCP servers (e.g., Rails applications), you'll need to setup your MCP server in an initializer with Redis configuration. Redis enables coordination across multiple server processes in production deployments (e.g., Puma workers), where no single process can hold all state in memory.
289
96
 
290
- ### Pagination Configuration Options
97
+ ```ruby
98
+ # config/initializers/model_context_protocol.rb
99
+ require "model_context_protocol"
291
100
 
292
- When `pagination` is set to a Hash, the following options are available:
101
+ # Assuming your handlers will be organized in the app/mcp folder.
102
+ Dir[Rails.root.join("app/mcp/**/*.rb")].sort.each { |f| require f }
293
103
 
294
- | Option | Type | Required | Default | Description |
295
- |--------|------|----------|---------|-------------|
296
- | `default_page_size` | Integer | No | `50` | Default number of items per page |
297
- | `max_page_size` | Integer | No | `500` | Maximum allowed page size |
298
- | `cursor_ttl` | Integer | No | `1800` | Cursor expiry time in seconds (30 minutes) |
104
+ ModelContextProtocol::Server.with_streamable_http_transport do |config|
105
+ config.name = "MyServer"
106
+ config.title = "My Server"
107
+ config.version = "1.0.0"
108
+ config.instructions = <<~INSTRUCTIONS
109
+ This server provides prompts, tools, and resources for interacting with my app.
299
110
 
300
- **Note:** Set `config.pagination = false` to completely disable pagination support.
111
+ Key capabilities:
112
+ - Does something
113
+ - Does something else
114
+ - Oh, and it does this other thing, too
301
115
 
302
- ### Transport Configuration Options
116
+ Use this server when you need to do things.
117
+ INSTRUCTIONS
303
118
 
304
- The transport configuration supports two types: `:stdio` (default) and `:streamable_http`.
119
+ config.redis_url = ENV.fetch("REDIS_URL", "redis://localhost:6379/0")
120
+ config.redis_pool_size = 20
121
+ config.redis_pool_timeout = 5
122
+ config.redis_enable_reaper = true
123
+ config.redis_reaper_interval = 60
124
+ config.redis_idle_timeout = 300
125
+ config.redis_ssl_params = {verify_mode: OpenSSL::SSL::VERIFY_NONE}
126
+
127
+ config.registry do
128
+ prompts do
129
+ register MyPrompt
130
+ end
305
131
 
306
- #### STDIO Transport
132
+ resources do
133
+ register MyResource
134
+ end
307
135
 
308
- ```ruby
309
- config.transport = { type: :stdio } # This is the default, can be omitted
136
+ tools do
137
+ register MyTool
138
+ end
139
+ end
140
+ end
310
141
  ```
311
142
 
312
- #### Streamable HTTP Transport
143
+ If your app uses Puma (>= 7.0) as the web server, use the provided plugin to start the MCP server.
313
144
 
314
145
  ```ruby
315
- config.transport = { type: :streamable_http, env: request.env }
146
+ # config/puma.rb
147
+ plugin :mcp
316
148
  ```
317
149
 
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
-
328
- The `:streamable_http` transport requires Redis to be configured globally before use:
150
+ For Puma 6.x or other web servers, you'll need to manually implement logic for starting and shutting the MCP server appropriately. This example shows how to do so for Puma 6.x; you can adapt it for your server of choice:
329
151
 
330
152
  ```ruby
331
- ModelContextProtocol::Server.configure_redis do |config|
332
- config.redis_url = ENV.fetch('REDIS_URL')
333
- config.pool_size = 20
334
- config.pool_timeout = 5
335
- config.enable_reaper = true
336
- config.reaper_interval = 60
337
- config.idle_timeout = 300
338
- end
339
- ```
153
+ # config/puma.rb
340
154
 
341
- | Option | Type | Required | Default | Description |
342
- |--------|------|----------|---------|-------------|
343
- | `redis_url` | String | Yes | - | Redis connection URL |
344
- | `pool_size` | Integer | No | `20` | Connection pool size |
345
- | `pool_timeout` | Integer | No | `5` | Pool checkout timeout in seconds |
346
- | `enable_reaper` | Boolean | No | `true` | Enable connection reaping |
347
- | `reaper_interval` | Integer | No | `60` | Reaper check interval in seconds |
348
- | `idle_timeout` | Integer | No | `300` | Idle connection timeout in seconds |
155
+ workers ENV.fetch("WEB_CONCURRENCY", 0).to_i
349
156
 
350
- ### Registry Configuration Options
157
+ mcp_start = ->(*) { ModelContextProtocol::Server.start unless ModelContextProtocol::Server.running? }
158
+ mcp_shutdown = ->(*) { ModelContextProtocol::Server.shutdown }
351
159
 
352
- The registry is configured using `ModelContextProtocol::Server::Registry.new` and supports the following block types:
160
+ # Clustered mode: workers fork, so start threads after fork
161
+ on_worker_boot(&mcp_start)
162
+ on_worker_shutdown(&mcp_shutdown)
353
163
 
354
- | Block Type | Options | Description |
355
- |------------|---------|-------------|
356
- | `prompts` | `list_changed: Boolean` | Register prompt handlers with optional list change notifications |
357
- | `resources` | `list_changed: Boolean`, `subscribe: Boolean` | Register resource handlers with optional list change notifications and subscriptions |
358
- | `resource_templates` | - | Register resource template handlers |
359
- | `tools` | `list_changed: Boolean` | Register tool handlers with optional list change notifications |
164
+ # Single mode: start directly (no forking)
165
+ if ENV.fetch("WEB_CONCURRENCY", 0).to_i.zero?
166
+ mcp_start.call
167
+ at_exit(&mcp_shutdown)
168
+ end
169
+ ```
360
170
 
361
- Within each block, use `register ClassName` to register your handlers.
171
+ Then, implement a controller action to handle the requests to the MCP server.
362
172
 
363
- **Example:**
364
173
  ```ruby
365
- config.registry = ModelContextProtocol::Server::Registry.new do
366
- prompts list_changed: true do
367
- register MyPrompt
368
- register AnotherPrompt
369
- end
174
+ class ModelContextProtocolController < ActionController::API
175
+ include ActionController::Live
370
176
 
371
- resources list_changed: true, subscribe: true do
372
- register MyResource
373
- end
177
+ def handle
178
+ result = ModelContextProtocol::Server.serve(
179
+ env: request.env,
180
+ session_context: { user_id: current_user.id }
181
+ )
182
+
183
+ response.headers.merge!(result[:headers]) if result[:headers]
374
184
 
375
- tools do
376
- register MyTool
185
+ if result[:stream]
186
+ result[:stream_proc]&.call(response.stream)
187
+ else
188
+ render json: result[:json], status: result[:status] || 200
189
+ end
190
+ ensure
191
+ response.stream.close rescue nil
377
192
  end
378
193
  end
379
194
  ```
380
195
 
381
- ---
382
-
383
- ## Prompts
384
-
385
- The `ModelContextProtocol::Server::Prompt` base class allows subclasses to define a prompt that the MCP client can use.
386
-
387
- 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.
388
-
389
- You can also log from within your prompt by calling a valid logger level method on the `logger` and passing a string message.
390
-
391
- ### Prompt Definition
392
-
393
- Use the `define` block to set [prompt properties](https://spec.modelcontextprotocol.io/specification/2025-06-18/server/prompts/) and configure arguments.
394
-
395
- | Property | Description |
396
- |----------|-------------|
397
- | `name` | The programmatic name of the prompt |
398
- | `title` | Human-readable display name |
399
- | `description` | Short description of what the prompt does |
400
- | `argument` | Define an argument block with name, description, required flag, and completion |
196
+ Lastly, add the necessary routes:
401
197
 
402
- ### Argument Definition
403
-
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.
405
-
406
- | Property | Description |
407
- |----------|-------------|
408
- | `name` | The name of the argument |
409
- | `description` | A short description of the argument |
410
- | `required` | Whether the argument is required (boolean) |
411
- | `completion` | Available hints for completions (array or completion class) |
412
-
413
- ### Prompt Methods
414
-
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.
416
-
417
- | Method | Context | Description |
418
- |--------|---------|-------------|
419
- | `define` | Class definition | Block for defining prompt metadata and arguments |
420
- | `call` | Instance method | Main method to implement prompt logic and build response |
421
- | `cancellable` | Within `call` | Wrap long-running operations to allow client cancellation (e.g., `cancellable { slow_operation }`) |
422
- | `progressable` | Within `call` | Wrap long-running operations to send clients progress notifications (e.g., `progressable { slow_operation }`) |
423
- | `message_history` | Within `call` | DSL method to build an array of user and assistant messages |
424
- | `respond_with` | Within `call` | Return properly formatted response data (e.g., `respond_with messages:`) |
425
-
426
- ### Message History DSL
427
-
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.
429
-
430
- | Method | Context | Description |
431
- |--------|---------|-------------|
432
- | `user_message` | Within `message_history` | Create a message with user role |
433
- | `assistant_message` | Within `message_history` | Create a message with assistant role |
434
-
435
- ### Content Blocks
436
-
437
- Use content blocks to properly format the content included in messages.
198
+ ```ruby
199
+ get "/", to: "model_context_protocol#handle", as: :mcp_get
200
+ post "/", to: "model_context_protocol#handle", as: :mcp_post
201
+ delete "/", to: "model_context_protocol#handle", as: :mcp_delete
202
+ ```
438
203
 
439
- | Method | Context | Description |
440
- |--------|---------|-------------|
441
- | `text_content` | Within message blocks | Create text content block |
442
- | `image_content` | Within message blocks | Create image content block (requires `data:` and `mime_type:`) |
443
- | `audio_content` | Within message blocks | Create audio content block (requires `data:` and `mime_type:`) |
444
- | `embedded_resource_content` | Within message blocks | Create embedded resource content block (requires `resource:`) |
445
- | `resource_link` | Within message blocks | Create resource link content block (requires `name:` and `uri:`) |
204
+ For a complete Rails integration example, see the [Quick Start with Rails](https://github.com/dickdavis/model-context-protocol-rb/wiki/Quick-Start-with-Rails) guide.
446
205
 
447
- ### Available Instance Variables
206
+ For complete configuration details including all server options, Redis configuration, and logging options, see the [Building an MCP Server](https://github.com/dickdavis/model-context-protocol-rb/wiki/Building-an-MCP-Server) wiki page.
448
207
 
449
- The `arguments` passed from an MCP client are available, as well as the `context` values passed in at server initialization.
208
+ ## Claude Code Plugin
450
209
 
451
- | Variable | Context | Description |
452
- |----------|---------|-------------|
453
- | `arguments` | Within `call` | Hash containing client-provided arguments (symbol keys) |
454
- | `context` | Within `call` | Hash containing server configuration context values |
455
- | `logger` | Within `call` | Logger instance for logging (e.g., `logger.info("message")`) |
210
+ The [mcp-rb](https://github.com/dickdavis/model-context-protocol-rb-plugin) Claude Code plugin teaches Claude how to build MCP handlers and configure servers using this gem's DSL. It provides skills for creating tools, prompts, resources, and server configurations.
456
211
 
457
- ### Examples
212
+ ## Prompts
458
213
 
459
- This is an example prompt that returns a properly formatted response:
214
+ Define prompts that MCP clients can use to generate contextual message sequences.
460
215
 
461
216
  ```ruby
462
217
  class TestPrompt < ModelContextProtocol::Server::Prompt
463
218
  define do
464
- # The name of the prompt for programmatic use
465
219
  name "brainstorm_excuses"
466
- # The human-readable prompt name for display in UI
467
- title "Brainstorm Excuses"
468
- # A short description of what the tool does
469
- description "A prompt for brainstorming excuses to get out of something"
470
-
471
- # Define arguments to be used with your prompt
472
- argument do
473
- # The name of the argument
474
- name "tone"
475
- # A short description of the argument
476
- description "The general tone to be used in the generated excuses"
477
- # If the argument is required
478
- required false
479
- # Available hints for completions
480
- completion ["whiny", "angry", "callous", "desperate", "nervous", "sneaky"]
481
- end
482
-
483
- argument do
484
- name "undesirable_activity"
485
- description "The thing to get out of"
486
- required true
487
- end
220
+ description "A prompt for brainstorming excuses"
221
+ argument { name "tone"; required false }
488
222
  end
489
223
 
490
- # You can optionally define a custom completion for an argument and pass it to completions.
491
- # ToneCompletion = ModelContextProtocol::Server::Completion.define do
492
- # hints = ["whiny", "angry", "callous", "desperate", "nervous", "sneaky"]
493
- # values = hints.grep(/#{argument_value}/)
494
- # respond_with values:
495
- # end
496
- # ...
497
- # define do
498
- # argument do
499
- # name "tone"
500
- # description "The general tone to be used in the generated excuses"
501
- # required false
502
- # completion ToneCompletion
503
- # end
504
- # end
505
-
506
- # The call method is invoked by the MCP Server to generate a response to resource/read requests
507
224
  def call
508
- # You can use the logger
509
- logger.info("Brainstorming excuses...")
510
-
511
- # Build an array of user and assistant messages
512
225
  messages = message_history do
513
- # Create a message with the user role
514
- user_message do
515
- # Use any type of content block in a message (text, image, audio, embedded_resource, or resource_link)
516
- text_content(text: "My wife wants me to: #{arguments[:undesirable_activity]}... Can you believe it?")
517
- end
518
-
519
- # You can also create messages with the assistant role
520
- assistant_message do
521
- text_content(text: "Oh, that's just downright awful. How can I help?")
522
- end
523
-
524
- user_message do
525
- # Reference any inputs from the client by accessing the appropriate key in the arguments hash
526
- text_content(text: "Can you generate some excuses for me?" + (arguments[:tone] ? " Make them as #{arguments[:tone]} as possible." : ""))
527
- end
226
+ user_message { text_content(text: "Generate excuses with #{arguments[:tone]} tone") }
528
227
  end
529
-
530
- # Respond with the messages
531
228
  respond_with messages:
532
229
  end
533
230
  end
534
231
  ```
535
232
 
536
- ---
537
-
538
- ## Resources
539
-
540
- The `ModelContextProtocol::Server::Resource` base class allows subclasses to define a resource that the MCP client can use.
541
-
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.
543
-
544
- ### Resource Definition
545
-
546
- Use the `define` block to set [resource properties](https://spec.modelcontextprotocol.io/specification/2025-06-18/server/resources/) and configure annotations.
547
-
548
- | Property | Description |
549
- |----------|-------------|
550
- | `name` | The name of the resource |
551
- | `title` | Human-readable display name |
552
- | `description` | Short description of what the resource contains |
553
- | `mime_type` | MIME type of the resource content |
554
- | `uri` | URI identifier for the resource |
555
- | `annotations` | Block for defining resource annotations |
556
-
557
- ### Annotation Definition
558
-
559
- Define any [resource annotations](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#annotations) using an `annotations` block nested within the `define` block.
560
-
561
- | Property | Description |
562
- |----------|-------------|
563
- | `audience` | Target audience for the resource (array of symbols like `[:user, :assistant]`) |
564
- | `priority` | Priority level (numeric value, e.g., `0.9`) |
565
- | `last_modified` | Last modified timestamp (ISO 8601 string) |
566
-
567
- ### Resource Methods
568
-
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.
570
-
571
- | Method | Context | Description |
572
- |--------|---------|-------------|
573
- | `define` | Class definition | Block for defining resource metadata and annotations |
574
- | `call` | Instance method | Main method to implement resource logic and build response |
575
- | `cancellable` | Within `call` | Wrap long-running operations to allow client cancellation (e.g., `cancellable { slow_operation }`) |
576
- | `progressable` | Within `call` | Wrap long-running operations to send clients progress notifications (e.g., `progressable { slow_operation }`) |
577
- | `respond_with` | Within `call` | Return properly formatted response data (e.g., `respond_with text:` or `respond_with binary:`) |
578
-
579
- ### Available Instance Variables
580
-
581
- Resources are stateless and only have access to their configured properties.
233
+ Key features:
234
+ - Define arguments with validation and completion hints
235
+ - Build message histories with user and assistant messages
236
+ - Support for text, image, audio, and embedded resource content
582
237
 
583
- | Variable | Context | Description |
584
- |----------|---------|-------------|
585
- | `mime_type` | Within `call` | The configured MIME type for this resource |
586
- | `uri` | Within `call` | The configured URI identifier for this resource |
238
+ For complete documentation and examples, see the [Prompts](https://github.com/dickdavis/model-context-protocol-rb/wiki/Prompts) wiki page.
587
239
 
588
- ### Examples
240
+ ## Resources
589
241
 
590
- This is an example resource that returns a text response:
242
+ Expose data and content to MCP clients through defined resources.
591
243
 
592
244
  ```ruby
593
245
  class TestResource < ModelContextProtocol::Server::Resource
594
246
  define do
595
- name "top-secret-plans.txt"
596
- title "Top Secret Plans"
597
- description "Top secret plans to do top secret things"
598
- mime_type "text/plain"
599
- uri "file:///top-secret-plans.txt"
247
+ name "config.json"
248
+ description "Application configuration"
249
+ mime_type "application/json"
250
+ uri "file:///config.json"
600
251
  end
601
252
 
602
253
  def call
603
- respond_with text: "I'm finna eat all my wife's leftovers."
254
+ respond_with text: { setting: "value" }.to_json
604
255
  end
605
256
  end
606
257
  ```
607
258
 
608
- This is an example resource with annotations:
609
-
610
- ```ruby
611
- class TestAnnotatedResource < ModelContextProtocol::Server::Resource
612
- define do
613
- name "annotated-document.md"
614
- description "A document with annotations showing priority and audience"
615
- mime_type "text/markdown"
616
- uri "file:///docs/annotated-document.md"
617
- annotations do
618
- audience [:user, :assistant]
619
- priority 0.9
620
- last_modified "2025-01-12T15:00:58Z"
621
- end
622
- end
623
-
624
- def call
625
- respond_with text: "# Annotated Document\n\nThis document has annotations."
626
- end
627
- end
628
- ```
259
+ Key features:
260
+ - Define metadata including MIME type and URI
261
+ - Return text or binary content
262
+ - Add annotations for audience and priority
629
263
 
630
- This is an example resource that returns binary data:
631
-
632
- ```ruby
633
- class TestBinaryResource < ModelContextProtocol::Server::Resource
634
- define do
635
- name "project-logo.png"
636
- description "The logo for the project"
637
- mime_type "image/png"
638
- uri "file:///project-logo.png"
639
- end
640
-
641
- def call
642
- # In a real implementation, we would retrieve the binary resource
643
- # This is a small valid base64 encoded string (represents "test")
644
- data = "dGVzdA=="
645
- respond_with binary: data
646
- end
647
- end
648
- ```
649
-
650
- ---
264
+ For complete documentation and examples, see the [Resources](https://github.com/dickdavis/model-context-protocol-rb/wiki/Resources) wiki page.
651
265
 
652
266
  ## Resource Templates
653
267
 
654
- The `ModelContextProtocol::Server::ResourceTemplate` base class allows subclasses to define a resource template that the MCP client can use.
655
-
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.
657
-
658
- ### Resource Template Definition
659
-
660
- Use the `define` block to set [resource template properties](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#resource-templates).
661
-
662
- | Property | Description |
663
- |----------|-------------|
664
- | `name` | The name of the resource template |
665
- | `description` | Short description of what the template provides |
666
- | `mime_type` | MIME type of resources created from this template |
667
- | `uri_template` | URI template with parameters (e.g., `"file:///{name}"`) |
668
-
669
- ### URI Template Configuration
670
-
671
- Define the URI template and configure parameter completions within the `uri_template` block.
672
-
673
- | Method | Context | Description |
674
- |--------|---------|-------------|
675
- | `completion` | Within `uri_template` block | Define completion for a URI parameter (e.g., `completion :name, ["value1", "value2"]`) |
676
-
677
- ### Resource Template Methods
678
-
679
- Resource templates only use the `define` method to configure their properties - they don't have a `call` method.
680
-
681
- | Method | Context | Description |
682
- |--------|---------|-------------|
683
- | `define` | Class definition | Block for defining resource template metadata and URI template |
684
-
685
- ### Examples
686
-
687
- This is an example resource template that provides a completion for a parameter of the URI template:
268
+ Define parameterized resources with URI templates that clients can instantiate.
688
269
 
689
270
  ```ruby
690
271
  class TestResourceTemplate < ModelContextProtocol::Server::ResourceTemplate
691
272
  define do
692
- name "project-document-resource-template"
693
- description "A resource template for retrieving project documents"
273
+ name "document-template"
274
+ description "Template for retrieving documents"
694
275
  mime_type "text/plain"
695
276
  uri_template "file:///{name}" do
696
- completion :name, ["top-secret-plans.txt"]
277
+ completion :name, ["readme.txt", "config.json"]
697
278
  end
698
279
  end
699
-
700
- # You can optionally define a custom completion for an argument and pass it to completions.
701
- # Completion = ModelContextProtocol::Server::Completion.define do
702
- # hints = {
703
- # "name" => ["top-secret-plans.txt"]
704
- # }
705
- # values = hints[argument_name].grep(/#{argument_value}/)
706
-
707
- # respond_with values:
708
- # end
709
-
710
- # define do
711
- # name "project-document-resource-template"
712
- # description "A resource template for retrieving project documents"
713
- # mime_type "text/plain"
714
- # uri_template "file:///{name}" do
715
- # completion :name, Completion
716
- # end
717
- # end
718
280
  end
719
281
  ```
720
282
 
721
- ---
722
-
723
- ## Tools
724
-
725
- The `ModelContextProtocol::Server::Tool` base class allows subclasses to define a tool that the MCP client can use.
726
-
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.
728
-
729
- ### Tool Definition
730
-
731
- Use the `define` block to set [tool properties](https://spec.modelcontextprotocol.io/specification/2025-06-18/server/tools/) and configure schemas.
732
-
733
- | Property | Description |
734
- |----------|-------------|
735
- | `name` | The programmatic name of the tool |
736
- | `title` | Human-readable display name |
737
- | `description` | Short description of what the tool does |
738
- | `input_schema` | JSON schema block for validating tool inputs |
739
- | `output_schema` | JSON schema block for validating structured content outputs |
740
-
741
- ### Tool Methods
742
-
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.
744
-
745
- | Method | Context | Description |
746
- |--------|---------|-------------|
747
- | `define` | Class definition | Block for defining tool metadata and schemas |
748
- | `call` | Instance method | Main method to implement tool logic and build response |
749
- | `cancellable` | Within `call` | Wrap long-running operations to allow client cancellation (e.g., `cancellable { slow_operation }`) |
750
- | `progressable` | Within `call` | Wrap long-running operations to send clients progress notifications (e.g., `progressable { slow_operation }`) |
751
- | `respond_with` | Within `call` | Return properly formatted response data with various content types |
752
-
753
- ### Content Blocks
283
+ Key features:
284
+ - Define URI templates with parameters
285
+ - Provide completion hints for template parameters
754
286
 
755
- Use content blocks to properly format the content included in tool responses.
287
+ For complete documentation and examples, see the [Resource Templates](https://github.com/dickdavis/model-context-protocol-rb/wiki/Resource-Templates) wiki page.
756
288
 
757
- | Method | Context | Description |
758
- |--------|---------|-------------|
759
- | `text_content` | Within `call` | Create text content block |
760
- | `image_content` | Within `call` | Create image content block (requires `data:` and `mime_type:`) |
761
- | `audio_content` | Within `call` | Create audio content block (requires `data:` and `mime_type:`) |
762
- | `embedded_resource_content` | Within `call` | Create embedded resource content block (requires `resource:`) |
763
- | `resource_link` | Within `call` | Create resource link content block (requires `name:` and `uri:`) |
764
-
765
- ### Response Types
766
-
767
- Tools can return different types of responses using `respond_with`.
768
-
769
- | Response Type | Usage | Description |
770
- |---------------|-------|-------------|
771
- | `structured_content:` | `respond_with structured_content: data` | Return structured data validated against output schema |
772
- | `content:` | `respond_with content: content_block` | Return single content block |
773
- | `content:` | `respond_with content: [content_blocks]` | Return array of mixed content blocks |
774
- | `error:` | `respond_with error: "message"` | Return tool error response |
775
-
776
- ### Available Instance Variables
777
-
778
- Arguments from MCP clients and server context are available, along with logging capabilities.
779
-
780
- | Variable | Context | Description |
781
- |----------|---------|-------------|
782
- | `arguments` | Within `call` | Hash containing client-provided arguments (symbol keys) |
783
- | `context` | Within `call` | Hash containing server configuration context values |
784
- | `logger` | Within `call` | Logger instance for logging (e.g., `logger.info("message")`) |
785
-
786
- ### Examples
289
+ ## Tools
787
290
 
788
- This is an example of a tool that returns structured content validated by an output schema:
291
+ Create callable functions that MCP clients can invoke with validated inputs.
789
292
 
790
293
  ```ruby
791
294
  class TestToolWithStructuredContentResponse < ModelContextProtocol::Server::Tool
@@ -830,16 +333,23 @@ class TestToolWithStructuredContentResponse < ModelContextProtocol::Server::Tool
830
333
  required: ["temperature", "conditions", "humidity"]
831
334
  }
832
335
  end
336
+ # Optional security requirements for the tool
337
+ security_schemes do
338
+ [
339
+ {type: "noauth"},
340
+ {type: "oauth2", scopes: ["search.read"]}
341
+ ]
342
+ end
833
343
  end
834
344
 
835
345
  def call
836
346
  # Use values provided by the server as context
837
347
  user_id = context[:user_id]
838
- logger.info("Initiating request for user #{user_id}...")
348
+ client_logger.info("Initiating request for user #{user_id}...")
839
349
 
840
350
  # Use values provided by clients as tool arguments
841
351
  location = arguments[:location]
842
- logger.info("Getting weather data for #{location}...")
352
+ client_logger.info("Getting weather data for #{location}...")
843
353
 
844
354
  # Returns a hash that validates against the output schema
845
355
  weather_data = get_weather_data(location)
@@ -861,337 +371,55 @@ class TestToolWithStructuredContentResponse < ModelContextProtocol::Server::Tool
861
371
  end
862
372
  ```
863
373
 
864
- This is an example tool that returns a text response:
865
-
866
- ```ruby
867
- class TestToolWithTextResponse < ModelContextProtocol::Server::Tool
868
- define do
869
- name "double"
870
- title "Number Doubler"
871
- description "Doubles the provided number"
872
- input_schema do
873
- {
874
- type: "object",
875
- properties: {
876
- number: {
877
- type: "string"
878
- }
879
- },
880
- required: ["number"]
881
- }
882
- end
883
- end
884
-
885
- def call
886
- logger.info("Silly user doesn't know how to double a number")
887
- number = arguments[:number].to_i
888
- calculation = number * 2
889
-
890
- user_id = context[:user_id]
891
- salutation = user_id ? "User #{user_id}, " : ""
892
- text_content = text_content(text: salutation << "#{number} doubled is #{calculation}")
893
-
894
- respond_with content: text_content
895
- end
896
- end
897
- ```
898
-
899
- This is an example of a tool that returns an image:
900
-
901
- ```ruby
902
- class TestToolWithImageResponse < ModelContextProtocol::Server::Tool
903
- define do
904
- name "custom-chart-generator"
905
- description "Generates a chart in various formats"
906
- input_schema do
907
- {
908
- type: "object",
909
- properties: {
910
- chart_type: {
911
- type: "string",
912
- description: "Type of chart (pie, bar, line)"
913
- },
914
- format: {
915
- type: "string",
916
- description: "Image format (jpg, svg, etc)"
917
- }
918
- },
919
- required: ["chart_type", "format"]
920
- }
921
- end
922
- end
923
-
924
- def call
925
- # Map format to mime type
926
- mime_type = case arguments[:format].downcase
927
- when "svg"
928
- "image/svg+xml"
929
- when "jpg", "jpeg"
930
- "image/jpeg"
931
- else
932
- "image/png"
933
- end
934
-
935
- # In a real implementation, we would generate an actual chart
936
- # This is a small valid base64 encoded string (represents "test")
937
- data = "dGVzdA=="
938
- image_content = image_content(data:, mime_type:)
939
- respond_with content: image_content
940
- end
941
- end
942
- ```
943
-
944
- This is an example of a tool that returns an embedded resource response:
945
-
946
- ```ruby
947
- class TestToolWithResourceResponse < ModelContextProtocol::Server::Tool
948
- define do
949
- name "resource-finder"
950
- description "Finds a resource given a name"
951
- input_schema do
952
- {
953
- type: "object",
954
- properties: {
955
- name: {
956
- type: "string",
957
- description: "The name of the resource"
958
- }
959
- },
960
- required: ["name"]
961
- }
962
- end
963
- end
964
-
965
- RESOURCE_MAPPINGS = {
966
- test_annotated_resource: TestAnnotatedResource,
967
- test_binary_resource: TestBinaryResource,
968
- test_resource: TestResource
969
- }.freeze
970
-
971
- def call
972
- name = arguments[:name]
973
- resource_klass = RESOURCE_MAPPINGS[name.downcase.to_sym]
974
- unless resource_klass
975
- return respond_with :error, text: "Resource `#{name}` not found"
976
- end
977
-
978
- resource_data = resource_klass.call
979
-
980
- respond_with content: embedded_resource_content(resource: resource_data)
981
- end
982
- end
983
- ```
984
-
985
- This is an example of a tool that returns mixed content:
374
+ Key features:
375
+ - Define input and output JSON schemas
376
+ - Declare tool security schemes (e.g., noauth, oauth2 scopes)
377
+ - Return text, image, audio, or embedded resource content
378
+ - Support for structured content responses
379
+ - Cancellable and progressable operations
986
380
 
987
- ```ruby
988
- class TestToolWithMixedContentResponse < ModelContextProtocol::Server::Tool
989
- define do
990
- name "get_temperature_history"
991
- description "Gets comprehensive temperature history for a zip code"
992
- input_schema do
993
- {
994
- type: "object",
995
- properties: {
996
- zip: {
997
- type: "string"
998
- }
999
- },
1000
- required: ["zip"]
1001
- }
1002
- end
1003
- end
1004
-
1005
- def call
1006
- logger.info("Getting comprehensive temperature history data")
381
+ For complete documentation and 7 detailed examples, see the [Tools](https://github.com/dickdavis/model-context-protocol-rb/wiki/Tools) wiki page.
1007
382
 
1008
- zip = arguments[:zip]
1009
- temperature_history = retrieve_temperature_history(zip:)
1010
- temperature_history_block = text_content(text: temperature_history.join(", "))
1011
-
1012
- temperature_chart = generate_weather_history_chart(temperature_history)
1013
- temperature_chart_block = image_content(
1014
- data: temperature_chart[:base64_chart_data],
1015
- mime_type: temperature_chart[:mime_type]
1016
- )
1017
-
1018
- respond_with content: [temperature_history_block, temperature_chart_block]
1019
- end
1020
-
1021
- private
1022
-
1023
- def retrieve_temperature_history(zip:)
1024
- # Simulates a call to an API or DB to retrieve weather history
1025
- [85.2, 87.4, 89.0, 95.3, 96.0]
1026
- end
1027
-
1028
- def generate_weather_history_chart(history)
1029
- # SImulate a call to generate a chart given the weather history
1030
- {
1031
- base64_chart_data: "dGVzdA==",
1032
- mime_type: "image/png"
1033
- }
1034
- end
1035
- end
1036
- ```
383
+ ## Completions
1037
384
 
1038
- This is an example of a tool that returns a tool error response:
385
+ Provide argument completion hints for prompts and resource templates.
1039
386
 
1040
387
  ```ruby
1041
- class TestToolWithToolErrorResponse < ModelContextProtocol::Server::Tool
1042
- define do
1043
- name "api-caller"
1044
- description "Makes calls to external APIs"
1045
- input_schema do
1046
- {
1047
- type: "object",
1048
- properties: {
1049
- api_endpoint: {
1050
- type: "string",
1051
- description: "API endpoint URL"
1052
- },
1053
- method: {
1054
- type: "string",
1055
- description: "HTTP method (GET, POST, etc)"
1056
- }
1057
- },
1058
- required: ["api_endpoint", "method"]
1059
- }
1060
- end
1061
- end
1062
-
388
+ class TestCompletion < ModelContextProtocol::Server::Completion
1063
389
  def call
1064
- # Simulate an API call failure
1065
- respond_with error: "Failed to call API at #{arguments[:api_endpoint]}: Connection timed out"
390
+ hints = { "tone" => ["whiny", "angry", "nervous"] }
391
+ values = hints[argument_name].grep(/#{argument_value}/)
392
+ respond_with values:
1066
393
  end
1067
394
  end
1068
395
  ```
1069
396
 
1070
- This is an example of a tool that allows a client to cancel a long-running operation:
397
+ For complete documentation, see the [Completions](https://github.com/dickdavis/model-context-protocol-rb/wiki/Completions) wiki page.
1071
398
 
1072
- ```ruby
1073
- class TestToolWithCancellableSleep < ModelContextProtocol::Server::Tool
1074
- define do
1075
- name "cancellable_sleep"
1076
- title "Cancellable Sleep Tool"
1077
- description "Sleep for 3 seconds with cancellation support"
1078
- input_schema do
1079
- {
1080
- type: "object",
1081
- properties: {},
1082
- additionalProperties: false
1083
- }
1084
- end
1085
- end
1086
-
1087
- def call
1088
- logger.info("Starting 3 second sleep operation")
1089
-
1090
- result = cancellable do
1091
- sleep 3
1092
- "Sleep completed successfully"
1093
- end
1094
-
1095
- respond_with content: text_content(text: result)
1096
- end
1097
- end
1098
- ```
399
+ ## Testing with RSpec
1099
400
 
1100
- This is an example of a tool that automatically sends progress notifications to the client and allows the client to cancel the operation:
401
+ This gem provides custom RSpec matchers and helpers for testing your MCP handlers.
1101
402
 
1102
403
  ```ruby
1103
- class TestToolWithProgressableAndCancellable < ModelContextProtocol::Server::Tool
1104
- define do
1105
- name "test_tool_with_progressable_and_cancellable"
1106
- description "A test tool that demonstrates combined progressable and cancellable functionality"
404
+ require "model_context_protocol/rspec"
405
+ ModelContextProtocol::RSpec.configure!
1107
406
 
1108
- input_schema do
1109
- {
1110
- type: "object",
1111
- properties: {
1112
- max_duration: {
1113
- type: "number",
1114
- description: "Expected maximum duration in seconds"
1115
- },
1116
- work_steps: {
1117
- type: "number",
1118
- description: "Number of work steps to perform"
1119
- }
1120
- },
1121
- required: ["max_duration"]
1122
- }
1123
- end
1124
- end
1125
-
1126
- def call
1127
- max_duration = arguments[:max_duration] || 10
1128
- work_steps = arguments[:work_steps] || 10
1129
- logger.info("Starting progressable call with max_duration=#{max_duration}, work_steps=#{work_steps}")
1130
-
1131
- result = progressable(max_duration:, message: "Processing #{work_steps} items") do
1132
- cancellable do
1133
- processed_items = []
1134
-
1135
- work_steps.times do |i|
1136
- sleep(max_duration / work_steps.to_f)
1137
- processed_items << "item_#{i + 1}"
1138
- end
1139
-
1140
- processed_items
1141
- end
1142
- end
1143
-
1144
- response = text_content(text: "Successfully processed #{result.length} items: #{result.join(", ")}")
1145
-
1146
- respond_with content: response
407
+ RSpec.describe WeatherTool, type: :mcp do
408
+ it "returns weather data" do
409
+ response = call_mcp_tool(WeatherTool, { location: "New York" })
410
+ expect(response).to be_valid_mcp_tool_response
411
+ expect(response).to have_text_content(/temperature/)
1147
412
  end
1148
413
  end
1149
414
  ```
1150
415
 
1151
- ---
1152
-
1153
- ## Completions
416
+ Key features:
417
+ - Helper methods: `call_mcp_tool`, `call_mcp_prompt`, `call_mcp_resource`
418
+ - Class definition matchers: `be_valid_mcp_class(:tool)`
419
+ - Response matchers for text, image, audio, and structured content
420
+ - Prompt and resource-specific matchers
1154
421
 
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.
1156
-
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.
1158
-
1159
- ### Completion Methods
1160
-
1161
- Completions only implement the `call` method to provide completion logic.
1162
-
1163
- | Method | Context | Description |
1164
- |--------|---------|-------------|
1165
- | `call` | Instance method | Main method to implement completion logic and build response |
1166
- | `respond_with` | Within `call` | Return properly formatted completion response (e.g., `respond_with values:`) |
1167
-
1168
- ### Available Instance Variables
1169
-
1170
- Completions receive the argument name and current value being completed.
1171
-
1172
- | Variable | Context | Description |
1173
- |----------|---------|-------------|
1174
- | `argument_name` | Within `call` | String name of the argument being completed |
1175
- | `argument_value` | Within `call` | Current partial value being typed by the user |
1176
-
1177
- ### Examples
1178
-
1179
- This is an example completion that returns an array of values in the response:
1180
-
1181
- ```ruby
1182
- class TestCompletion < ModelContextProtocol::Server::Completion
1183
- def call
1184
- hints = {
1185
- "message" => ["hello", "world", "foo", "bar"]
1186
- }
1187
- values = hints[argument_name].grep(/#{argument_value}/)
1188
-
1189
- respond_with values:
1190
- end
1191
- end
1192
- ```
1193
-
1194
- ---
422
+ For complete matcher documentation and examples, see the [Testing with RSpec](https://github.com/dickdavis/model-context-protocol-rb/wiki/Testing-with-RSpec) wiki page.
1195
423
 
1196
424
  ## Development
1197
425
 
@@ -1207,6 +435,9 @@ bundle exec rake mcp:generate_stdio_server
1207
435
 
1208
436
  # generates bin/dev-http for streamable HTTP transport
1209
437
  bundle exec rake mcp:generate_streamable_http_server
438
+
439
+ # generates bin/dev-http-puma for streamable HTTP transport with Puma web server
440
+ bundle exec rake mcp:generate_puma_server
1210
441
  ```
1211
442
 
1212
443
  If you need to test with HTTPS (e.g., for clients that require SSL), generate self-signed certificates: