mcp 0.1.0 → 0.3.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.
- checksums.yaml +4 -4
- data/.cursor/rules/release-changelogs.mdc +11 -24
- data/.github/workflows/release.yml +25 -0
- data/.rubocop.yml +5 -3
- data/CHANGELOG.md +57 -0
- data/Gemfile +16 -6
- data/README.md +439 -61
- data/examples/README.md +197 -0
- data/examples/http_client.rb +184 -0
- data/examples/http_server.rb +171 -0
- data/examples/stdio_server.rb +6 -6
- data/examples/streamable_http_client.rb +203 -0
- data/examples/streamable_http_server.rb +173 -0
- data/lib/mcp/client/http.rb +88 -0
- data/lib/mcp/client/tool.rb +16 -0
- data/lib/mcp/client.rb +88 -0
- data/lib/mcp/configuration.rb +22 -3
- data/lib/mcp/methods.rb +55 -33
- data/lib/mcp/prompt.rb +15 -4
- data/lib/mcp/resource.rb +8 -6
- data/lib/mcp/resource_template.rb +8 -6
- data/lib/mcp/server/capabilities.rb +96 -0
- data/lib/mcp/server/transports/stdio_transport.rb +57 -0
- data/lib/mcp/server/transports/streamable_http_transport.rb +301 -0
- data/lib/mcp/server.rb +116 -52
- data/lib/mcp/tool/annotations.rb +4 -4
- data/lib/mcp/tool/input_schema.rb +49 -1
- data/lib/mcp/tool/output_schema.rb +66 -0
- data/lib/mcp/tool/response.rb +15 -4
- data/lib/mcp/tool.rb +38 -7
- data/lib/mcp/transport.rb +16 -4
- data/lib/mcp/transports/stdio.rb +8 -28
- data/lib/mcp/version.rb +1 -1
- data/lib/mcp.rb +20 -12
- data/mcp.gemspec +1 -2
- metadata +21 -24
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
#
|
1
|
+
# MCP Ruby SDK [](https://rubygems.org/gems/mcp) [](https://github.com/modelcontextprotocol/ruby-sdk/blob/main/LICENSE.txt) [](https://github.com/modelcontextprotocol/ruby-sdk/actions/workflows/ci.yml)
|
2
2
|
|
3
|
-
|
3
|
+
The official Ruby SDK for Model Context Protocol servers and clients.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -12,29 +12,35 @@ gem 'mcp'
|
|
12
12
|
|
13
13
|
And then execute:
|
14
14
|
|
15
|
-
```
|
15
|
+
```console
|
16
16
|
$ bundle install
|
17
17
|
```
|
18
18
|
|
19
19
|
Or install it yourself as:
|
20
20
|
|
21
|
-
```
|
21
|
+
```console
|
22
22
|
$ gem install mcp
|
23
23
|
```
|
24
24
|
|
25
|
-
|
25
|
+
You may need to add additional dependencies depending on which features you wish to access.
|
26
|
+
|
27
|
+
## Building an MCP Server
|
26
28
|
|
27
29
|
The `MCP::Server` class is the core component that handles JSON-RPC requests and responses.
|
28
30
|
It implements the Model Context Protocol specification, handling model context requests and responses.
|
29
31
|
|
30
32
|
### Key Features
|
33
|
+
|
31
34
|
- Implements JSON-RPC 2.0 message handling
|
32
35
|
- Supports protocol initialization and capability negotiation
|
33
36
|
- Manages tool registration and invocation
|
34
37
|
- Supports prompt registration and execution
|
35
38
|
- Supports resource registration and retrieval
|
39
|
+
- Supports stdio & Streamable HTTP (including SSE) transports
|
40
|
+
- Supports notifications for list changes (tools, prompts, resources)
|
36
41
|
|
37
42
|
### Supported Methods
|
43
|
+
|
38
44
|
- `initialize` - Initializes the protocol and returns server capabilities
|
39
45
|
- `ping` - Simple health check
|
40
46
|
- `tools/list` - Lists all registered tools and their schemas
|
@@ -45,31 +51,118 @@ It implements the Model Context Protocol specification, handling model context r
|
|
45
51
|
- `resources/read` - Retrieves a specific resource by name
|
46
52
|
- `resources/templates/list` - Lists all registered resource templates and their schemas
|
47
53
|
|
54
|
+
### Custom Methods
|
55
|
+
|
56
|
+
The server allows you to define custom JSON-RPC methods beyond the standard MCP protocol methods using the `define_custom_method` method:
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
server = MCP::Server.new(name: "my_server")
|
60
|
+
|
61
|
+
# Define a custom method that returns a result
|
62
|
+
server.define_custom_method(method_name: "add") do |params|
|
63
|
+
params[:a] + params[:b]
|
64
|
+
end
|
65
|
+
|
66
|
+
# Define a custom notification method (returns nil)
|
67
|
+
server.define_custom_method(method_name: "notify") do |params|
|
68
|
+
# Process notification
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
**Key Features:**
|
74
|
+
|
75
|
+
- Accepts any method name as a string
|
76
|
+
- Block receives the request parameters as a hash
|
77
|
+
- Can handle both regular methods (with responses) and notifications
|
78
|
+
- Prevents overriding existing MCP protocol methods
|
79
|
+
- Supports instrumentation callbacks for monitoring
|
80
|
+
|
81
|
+
**Usage Example:**
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
# Client request
|
85
|
+
{
|
86
|
+
"jsonrpc": "2.0",
|
87
|
+
"id": 1,
|
88
|
+
"method": "add",
|
89
|
+
"params": { "a": 5, "b": 3 }
|
90
|
+
}
|
91
|
+
|
92
|
+
# Server response
|
93
|
+
{
|
94
|
+
"jsonrpc": "2.0",
|
95
|
+
"id": 1,
|
96
|
+
"result": 8
|
97
|
+
}
|
98
|
+
```
|
99
|
+
|
100
|
+
**Error Handling:**
|
101
|
+
|
102
|
+
- Raises `MCP::Server::MethodAlreadyDefinedError` if trying to override an existing method
|
103
|
+
- Supports the same exception reporting and instrumentation as standard methods
|
104
|
+
|
105
|
+
### Notifications
|
106
|
+
|
107
|
+
The server supports sending notifications to clients when lists of tools, prompts, or resources change. This enables real-time updates without polling.
|
108
|
+
|
109
|
+
#### Notification Methods
|
110
|
+
|
111
|
+
The server provides three notification methods:
|
112
|
+
|
113
|
+
- `notify_tools_list_changed` - Send a notification when the tools list changes
|
114
|
+
- `notify_prompts_list_changed` - Send a notification when the prompts list changes
|
115
|
+
- `notify_resources_list_changed` - Send a notification when the resources list changes
|
116
|
+
|
117
|
+
#### Notification Format
|
118
|
+
|
119
|
+
Notifications follow the JSON-RPC 2.0 specification and use these method names:
|
120
|
+
|
121
|
+
- `notifications/tools/list_changed`
|
122
|
+
- `notifications/prompts/list_changed`
|
123
|
+
- `notifications/resources/list_changed`
|
124
|
+
|
125
|
+
#### Transport Support
|
126
|
+
|
127
|
+
- **stdio**: Notifications are sent as JSON-RPC 2.0 messages to stdout
|
128
|
+
- **Streamable HTTP**: Notifications are sent as JSON-RPC 2.0 messages over HTTP with streaming (chunked transfer or SSE)
|
129
|
+
|
130
|
+
#### Usage Example
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
server = MCP::Server.new(name: "my_server")
|
134
|
+
transport = MCP::Transports::HTTP.new(server)
|
135
|
+
server.transport = transport
|
136
|
+
|
137
|
+
# When tools change, notify clients
|
138
|
+
server.define_tool(name: "new_tool") { |**args| { result: "ok" } }
|
139
|
+
server.notify_tools_list_changed
|
140
|
+
```
|
141
|
+
|
48
142
|
### Unsupported Features ( to be implemented in future versions )
|
49
143
|
|
50
|
-
- Notifications
|
51
144
|
- Log Level
|
52
145
|
- Resource subscriptions
|
53
146
|
- Completions
|
54
|
-
- Complete StreamableHTTP implementation with streaming responses
|
55
147
|
|
56
148
|
### Usage
|
57
149
|
|
58
150
|
#### Rails Controller
|
59
151
|
|
60
152
|
When added to a Rails controller on a route that handles POST requests, your server will be compliant with non-streaming
|
61
|
-
[
|
153
|
+
[Streamable HTTP](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#streamable-http) transport
|
62
154
|
requests.
|
63
155
|
|
64
156
|
You can use the `Server#handle_json` method to handle requests.
|
65
157
|
|
66
158
|
```ruby
|
67
159
|
class ApplicationController < ActionController::Base
|
68
|
-
|
69
160
|
def index
|
70
161
|
server = MCP::Server.new(
|
71
162
|
name: "my_server",
|
163
|
+
title: "Example Server Display Name", # WARNING: This is a `Draft` and is not supported in the `Version 2025-06-18 (latest)` specification.
|
72
164
|
version: "1.0.0",
|
165
|
+
instructions: "Use the tools of this server as a last resort",
|
73
166
|
tools: [SomeTool, AnotherTool],
|
74
167
|
prompts: [MyPrompt],
|
75
168
|
server_context: { user_id: current_user.id },
|
@@ -84,9 +177,8 @@ end
|
|
84
177
|
If you want to build a local command-line application, you can use the stdio transport:
|
85
178
|
|
86
179
|
```ruby
|
87
|
-
#!/usr/bin/env ruby
|
88
180
|
require "mcp"
|
89
|
-
require "mcp/transports/
|
181
|
+
require "mcp/server/transports/stdio_transport"
|
90
182
|
|
91
183
|
# Create a simple tool
|
92
184
|
class ExampleTool < MCP::Tool
|
@@ -115,20 +207,20 @@ server = MCP::Server.new(
|
|
115
207
|
)
|
116
208
|
|
117
209
|
# Create and start the transport
|
118
|
-
transport = MCP::Transports::StdioTransport.new(server)
|
210
|
+
transport = MCP::Server::Transports::StdioTransport.new(server)
|
119
211
|
transport.open
|
120
212
|
```
|
121
213
|
|
122
214
|
You can run this script and then type in requests to the server at the command line.
|
123
215
|
|
124
|
-
```
|
125
|
-
$
|
126
|
-
{"jsonrpc":"2.0","id":"1","
|
127
|
-
{"jsonrpc":"2.0","id":"2","
|
128
|
-
{"jsonrpc":"2.0","id":"3","
|
216
|
+
```console
|
217
|
+
$ ruby examples/stdio_server.rb
|
218
|
+
{"jsonrpc":"2.0","id":"1","method":"ping"}
|
219
|
+
{"jsonrpc":"2.0","id":"2","method":"tools/list"}
|
220
|
+
{"jsonrpc":"2.0","id":"3","method":"tools/call","params":{"name":"example_tool","arguments":{"message":"Hello"}}}
|
129
221
|
```
|
130
222
|
|
131
|
-
|
223
|
+
### Configuration
|
132
224
|
|
133
225
|
The gem can be configured using the `MCP.configure` block:
|
134
226
|
|
@@ -179,11 +271,13 @@ server = MCP::Server.new(
|
|
179
271
|
The `server_context` is a user-defined hash that is passed into the server instance and made available to tools, prompts, and exception/instrumentation callbacks. It can be used to provide contextual information such as authentication state, user IDs, or request-specific data.
|
180
272
|
|
181
273
|
**Type:**
|
274
|
+
|
182
275
|
```ruby
|
183
276
|
server_context: { [String, Symbol] => Any }
|
184
277
|
```
|
185
278
|
|
186
279
|
**Example:**
|
280
|
+
|
187
281
|
```ruby
|
188
282
|
server = MCP::Server.new(
|
189
283
|
name: "my_server",
|
@@ -203,6 +297,7 @@ The exception reporter receives:
|
|
203
297
|
- `server_context`: The context hash provided to the server
|
204
298
|
|
205
299
|
**Signature:**
|
300
|
+
|
206
301
|
```ruby
|
207
302
|
exception_reporter = ->(exception, server_context) { ... }
|
208
303
|
```
|
@@ -219,12 +314,14 @@ The instrumentation callback receives a hash with the following possible keys:
|
|
219
314
|
- `duration`: (Float) Duration of the call in seconds
|
220
315
|
|
221
316
|
**Type:**
|
317
|
+
|
222
318
|
```ruby
|
223
319
|
instrumentation_callback = ->(data) { ... }
|
224
320
|
# where data is a Hash with keys as described above
|
225
321
|
```
|
226
322
|
|
227
323
|
**Example:**
|
324
|
+
|
228
325
|
```ruby
|
229
326
|
config.instrumentation_callback = ->(data) {
|
230
327
|
puts "Instrumentation: #{data.inspect}"
|
@@ -233,19 +330,22 @@ config.instrumentation_callback = ->(data) {
|
|
233
330
|
|
234
331
|
### Server Protocol Version
|
235
332
|
|
236
|
-
The server's protocol version can be overridden using the `protocol_version`
|
333
|
+
The server's protocol version can be overridden using the `protocol_version` keyword argument:
|
237
334
|
|
238
335
|
```ruby
|
239
|
-
MCP::
|
336
|
+
configuration = MCP::Configuration.new(protocol_version: "2024-11-05")
|
337
|
+
MCP::Server.new(name: "test_server", configuration: configuration)
|
240
338
|
```
|
241
339
|
|
242
340
|
This will make all new server instances use the specified protocol version instead of the default version. The protocol version can be reset to the default by setting it to `nil`:
|
243
341
|
|
244
342
|
```ruby
|
245
|
-
MCP::
|
343
|
+
MCP::Configuration.new(protocol_version: nil)
|
246
344
|
```
|
247
345
|
|
248
|
-
|
346
|
+
If an invalid `protocol_version` value is set, an `ArgumentError` is raised.
|
347
|
+
|
348
|
+
Be sure to check the [MCP spec](https://modelcontextprotocol.io/specification/versioning) for the protocol version to understand the supported features for the version being set.
|
249
349
|
|
250
350
|
### Exception Reporting
|
251
351
|
|
@@ -267,16 +367,17 @@ When an exception occurs:
|
|
267
367
|
|
268
368
|
If no exception reporter is configured, a default no-op reporter is used that silently ignores exceptions.
|
269
369
|
|
270
|
-
|
370
|
+
### Tools
|
271
371
|
|
272
|
-
MCP spec includes [Tools](https://modelcontextprotocol.io/
|
372
|
+
MCP spec includes [Tools](https://modelcontextprotocol.io/specification/2025-06-18/server/tools) which provide functionality to LLM apps.
|
273
373
|
|
274
|
-
This gem provides a `MCP::Tool` class that can be used to create tools in
|
374
|
+
This gem provides a `MCP::Tool` class that can be used to create tools in three ways:
|
275
375
|
|
276
376
|
1. As a class definition:
|
277
377
|
|
278
378
|
```ruby
|
279
379
|
class MyTool < MCP::Tool
|
380
|
+
title "My Tool" # WARNING: This is a `Draft` and is not supported in the `Version 2025-06-18 (latest)` specification.
|
280
381
|
description "This tool performs specific functionality..."
|
281
382
|
input_schema(
|
282
383
|
properties: {
|
@@ -284,12 +385,20 @@ class MyTool < MCP::Tool
|
|
284
385
|
},
|
285
386
|
required: ["message"]
|
286
387
|
)
|
388
|
+
output_schema(
|
389
|
+
properties: {
|
390
|
+
result: { type: "string" },
|
391
|
+
success: { type: "boolean" },
|
392
|
+
timestamp: { type: "string", format: "date-time" }
|
393
|
+
},
|
394
|
+
required: ["result", "success", "timestamp"]
|
395
|
+
)
|
287
396
|
annotations(
|
288
|
-
title: "My Tool",
|
289
397
|
read_only_hint: true,
|
290
398
|
destructive_hint: false,
|
291
399
|
idempotent_hint: true,
|
292
|
-
open_world_hint: false
|
400
|
+
open_world_hint: false,
|
401
|
+
title: "My Tool"
|
293
402
|
)
|
294
403
|
|
295
404
|
def self.call(message:, server_context:)
|
@@ -304,6 +413,23 @@ tool = MyTool
|
|
304
413
|
|
305
414
|
```ruby
|
306
415
|
tool = MCP::Tool.define(
|
416
|
+
name: "my_tool",
|
417
|
+
title: "My Tool", # WARNING: This is a `Draft` and is not supported in the `Version 2025-06-18 (latest)` specification.
|
418
|
+
description: "This tool performs specific functionality...",
|
419
|
+
annotations: {
|
420
|
+
read_only_hint: true,
|
421
|
+
title: "My Tool"
|
422
|
+
}
|
423
|
+
) do |args, server_context|
|
424
|
+
MCP::Tool::Response.new([{ type: "text", text: "OK" }])
|
425
|
+
end
|
426
|
+
```
|
427
|
+
|
428
|
+
3. By using the `ModelContextProtocol::Server#define_tool` method with a block:
|
429
|
+
|
430
|
+
```ruby
|
431
|
+
server = ModelContextProtocol::Server.new
|
432
|
+
server.define_tool(
|
307
433
|
name: "my_tool",
|
308
434
|
description: "This tool performs specific functionality...",
|
309
435
|
annotations: {
|
@@ -322,28 +448,128 @@ e.g. around authentication state.
|
|
322
448
|
|
323
449
|
Tools can include annotations that provide additional metadata about their behavior. The following annotations are supported:
|
324
450
|
|
451
|
+
- `destructive_hint`: Indicates if the tool performs destructive operations. Defaults to true
|
452
|
+
- `idempotent_hint`: Indicates if the tool's operations are idempotent. Defaults to false
|
453
|
+
- `open_world_hint`: Indicates if the tool operates in an open world context. Defaults to true
|
454
|
+
- `read_only_hint`: Indicates if the tool only reads data (doesn't modify state). Defaults to false
|
325
455
|
- `title`: A human-readable title for the tool
|
326
|
-
- `read_only_hint`: Indicates if the tool only reads data (doesn't modify state)
|
327
|
-
- `destructive_hint`: Indicates if the tool performs destructive operations
|
328
|
-
- `idempotent_hint`: Indicates if the tool's operations are idempotent
|
329
|
-
- `open_world_hint`: Indicates if the tool operates in an open world context
|
330
456
|
|
331
457
|
Annotations can be set either through the class definition using the `annotations` class method or when defining a tool using the `define` method.
|
332
458
|
|
333
|
-
|
459
|
+
> [!NOTE]
|
460
|
+
> This **Tool Annotations** feature is supported starting from `protocol_version: '2025-03-26'`.
|
461
|
+
|
462
|
+
### Tool Output Schemas
|
463
|
+
|
464
|
+
Tools can optionally define an `output_schema` to specify the expected structure of their results. This works similarly to how `input_schema` is defined and can be used in three ways:
|
465
|
+
|
466
|
+
1. **Class definition with output_schema:**
|
467
|
+
|
468
|
+
```ruby
|
469
|
+
class WeatherTool < MCP::Tool
|
470
|
+
tool_name "get_weather"
|
471
|
+
description "Get current weather for a location"
|
472
|
+
|
473
|
+
input_schema(
|
474
|
+
properties: {
|
475
|
+
location: { type: "string" },
|
476
|
+
units: { type: "string", enum: ["celsius", "fahrenheit"] }
|
477
|
+
},
|
478
|
+
required: ["location"]
|
479
|
+
)
|
480
|
+
|
481
|
+
output_schema(
|
482
|
+
properties: {
|
483
|
+
temperature: { type: "number" },
|
484
|
+
condition: { type: "string" },
|
485
|
+
humidity: { type: "integer" }
|
486
|
+
},
|
487
|
+
required: ["temperature", "condition", "humidity"]
|
488
|
+
)
|
489
|
+
|
490
|
+
def self.call(location:, units: "celsius", server_context:)
|
491
|
+
# Call weather API and structure the response
|
492
|
+
api_response = WeatherAPI.fetch(location, units)
|
493
|
+
weather_data = {
|
494
|
+
temperature: api_response.temp,
|
495
|
+
condition: api_response.description,
|
496
|
+
humidity: api_response.humidity_percent
|
497
|
+
}
|
498
|
+
|
499
|
+
output_schema.validate_result(weather_data)
|
500
|
+
|
501
|
+
MCP::Tool::Response.new([{
|
502
|
+
type: "text",
|
503
|
+
text: weather_data.to_json
|
504
|
+
}])
|
505
|
+
end
|
506
|
+
end
|
507
|
+
```
|
508
|
+
|
509
|
+
2. **Using Tool.define with output_schema:**
|
510
|
+
|
511
|
+
```ruby
|
512
|
+
tool = MCP::Tool.define(
|
513
|
+
name: "calculate_stats",
|
514
|
+
description: "Calculate statistics for a dataset",
|
515
|
+
input_schema: {
|
516
|
+
properties: {
|
517
|
+
numbers: { type: "array", items: { type: "number" } }
|
518
|
+
},
|
519
|
+
required: ["numbers"]
|
520
|
+
},
|
521
|
+
output_schema: {
|
522
|
+
properties: {
|
523
|
+
mean: { type: "number" },
|
524
|
+
median: { type: "number" },
|
525
|
+
count: { type: "integer" }
|
526
|
+
},
|
527
|
+
required: ["mean", "median", "count"]
|
528
|
+
}
|
529
|
+
) do |args, server_context|
|
530
|
+
# Calculate statistics and validate against schema
|
531
|
+
MCP::Tool::Response.new([{ type: "text", text: "Statistics calculated" }])
|
532
|
+
end
|
533
|
+
```
|
534
|
+
|
535
|
+
3. **Using OutputSchema objects:**
|
536
|
+
|
537
|
+
```ruby
|
538
|
+
class DataTool < MCP::Tool
|
539
|
+
output_schema MCP::Tool::OutputSchema.new(
|
540
|
+
properties: {
|
541
|
+
success: { type: "boolean" },
|
542
|
+
data: { type: "object" }
|
543
|
+
},
|
544
|
+
required: ["success"]
|
545
|
+
)
|
546
|
+
end
|
547
|
+
```
|
548
|
+
|
549
|
+
MCP spec for the [Output Schema](https://modelcontextprotocol.io/specification/2025-06-18/server/tools#output-schema) specifies that:
|
550
|
+
|
551
|
+
- **Server Validation**: Servers MUST provide structured results that conform to the output schema
|
552
|
+
- **Client Validation**: Clients SHOULD validate structured results against the output schema
|
553
|
+
- **Better Integration**: Enables strict schema validation, type information, and improved developer experience
|
554
|
+
- **Backward Compatibility**: Tools returning structured content SHOULD also include serialized JSON in a TextContent block
|
555
|
+
|
556
|
+
The output schema follows standard JSON Schema format and helps ensure consistent data exchange between MCP servers and clients.
|
334
557
|
|
335
|
-
|
558
|
+
### Prompts
|
336
559
|
|
337
|
-
|
560
|
+
MCP spec includes [Prompts](https://modelcontextprotocol.io/specification/2025-06-18/server/prompts), which enable servers to define reusable prompt templates and workflows that clients can easily surface to users and LLMs.
|
561
|
+
|
562
|
+
The `MCP::Prompt` class provides three ways to create prompts:
|
338
563
|
|
339
564
|
1. As a class definition with metadata:
|
340
565
|
|
341
566
|
```ruby
|
342
567
|
class MyPrompt < MCP::Prompt
|
343
568
|
prompt_name "my_prompt" # Optional - defaults to underscored class name
|
569
|
+
title "My Prompt" # WARNING: This is a `Draft` and is not supported in the `Version 2025-06-18 (latest)` specification.
|
344
570
|
description "This prompt performs specific functionality..."
|
345
571
|
arguments [
|
346
|
-
Prompt::Argument.new(
|
572
|
+
MCP::Prompt::Argument.new(
|
347
573
|
name: "message",
|
348
574
|
description: "Input message",
|
349
575
|
required: true
|
@@ -352,16 +578,16 @@ class MyPrompt < MCP::Prompt
|
|
352
578
|
|
353
579
|
class << self
|
354
580
|
def template(args, server_context:)
|
355
|
-
Prompt::Result.new(
|
581
|
+
MCP::Prompt::Result.new(
|
356
582
|
description: "Response description",
|
357
583
|
messages: [
|
358
|
-
Prompt::Message.new(
|
584
|
+
MCP::Prompt::Message.new(
|
359
585
|
role: "user",
|
360
|
-
content: Content::Text.new("User message")
|
586
|
+
content: MCP::Content::Text.new("User message")
|
361
587
|
),
|
362
|
-
Prompt::Message.new(
|
588
|
+
MCP::Prompt::Message.new(
|
363
589
|
role: "assistant",
|
364
|
-
content: Content::Text.new(args["message"])
|
590
|
+
content: MCP::Content::Text.new(args["message"])
|
365
591
|
)
|
366
592
|
]
|
367
593
|
)
|
@@ -376,6 +602,38 @@ prompt = MyPrompt
|
|
376
602
|
|
377
603
|
```ruby
|
378
604
|
prompt = MCP::Prompt.define(
|
605
|
+
name: "my_prompt",
|
606
|
+
title: "My Prompt", # WARNING: This is a `Draft` and is not supported in the `Version 2025-06-18 (latest)` specification.
|
607
|
+
description: "This prompt performs specific functionality...",
|
608
|
+
arguments: [
|
609
|
+
MCP::Prompt::Argument.new(
|
610
|
+
name: "message",
|
611
|
+
description: "Input message",
|
612
|
+
required: true
|
613
|
+
)
|
614
|
+
]
|
615
|
+
) do |args, server_context:|
|
616
|
+
MCP::Prompt::Result.new(
|
617
|
+
description: "Response description",
|
618
|
+
messages: [
|
619
|
+
MCP::Prompt::Message.new(
|
620
|
+
role: "user",
|
621
|
+
content: MCP::Content::Text.new("User message")
|
622
|
+
),
|
623
|
+
MCP::Prompt::Message.new(
|
624
|
+
role: "assistant",
|
625
|
+
content: MCP::Content::Text.new(args["message"])
|
626
|
+
)
|
627
|
+
]
|
628
|
+
)
|
629
|
+
end
|
630
|
+
```
|
631
|
+
|
632
|
+
3. Using the `ModelContextProtocol::Server#define_protocol` method:
|
633
|
+
|
634
|
+
```ruby
|
635
|
+
server = ModelContextProtocol::Server.new
|
636
|
+
server.define_protocol(
|
379
637
|
name: "my_prompt",
|
380
638
|
description: "This prompt performs specific functionality...",
|
381
639
|
arguments: [
|
@@ -407,10 +665,10 @@ e.g. around authentication state or user preferences.
|
|
407
665
|
|
408
666
|
### Key Components
|
409
667
|
|
410
|
-
- `Prompt::Argument` - Defines input parameters for the prompt template
|
411
|
-
- `Prompt::Message` - Represents a message in the conversation with a role and content
|
412
|
-
- `Prompt::Result` - The output of a prompt template containing description and messages
|
413
|
-
- `Content::Text` - Text content for messages
|
668
|
+
- `MCP::Prompt::Argument` - Defines input parameters for the prompt template
|
669
|
+
- `MCP::Prompt::Message` - Represents a message in the conversation with a role and content
|
670
|
+
- `MCP::Prompt::Result` - The output of a prompt template containing description and messages
|
671
|
+
- `MCP::Content::Text` - Text content for messages
|
414
672
|
|
415
673
|
### Usage
|
416
674
|
|
@@ -438,32 +696,37 @@ To register a handler pass a proc/lambda to as `instrumentation_callback` into t
|
|
438
696
|
MCP.configure do |config|
|
439
697
|
config.instrumentation_callback = ->(data) {
|
440
698
|
puts "Got instrumentation data #{data.inspect}"
|
441
|
-
|
442
|
-
|
699
|
+
}
|
700
|
+
end
|
443
701
|
```
|
444
702
|
|
445
703
|
The data contains the following keys:
|
446
|
-
|
447
|
-
`
|
448
|
-
`
|
449
|
-
`
|
450
|
-
`
|
451
|
-
`
|
704
|
+
|
705
|
+
- `method`: the method called, e.g. `ping`, `tools/list`, `tools/call` etc
|
706
|
+
- `tool_name`: the name of the tool called
|
707
|
+
- `prompt_name`: the name of the prompt called
|
708
|
+
- `resource_uri`: the uri of the resource called
|
709
|
+
- `error`: if looking up tools/prompts etc failed, e.g. `tool_not_found`
|
710
|
+
- `duration`: the duration of the call in seconds
|
452
711
|
|
453
712
|
`tool_name`, `prompt_name` and `resource_uri` are only populated if a matching handler is registered.
|
454
713
|
This is to avoid potential issues with metric cardinality
|
455
714
|
|
456
|
-
|
715
|
+
### Resources
|
716
|
+
|
717
|
+
MCP spec includes [Resources](https://modelcontextprotocol.io/specification/2025-06-18/server/resources).
|
457
718
|
|
458
|
-
|
719
|
+
### Reading Resources
|
459
720
|
|
460
721
|
The `MCP::Resource` class provides a way to register resources with the server.
|
461
722
|
|
462
723
|
```ruby
|
463
724
|
resource = MCP::Resource.new(
|
464
|
-
uri: "example.com/my_resource",
|
465
|
-
|
466
|
-
|
725
|
+
uri: "https://example.com/my_resource",
|
726
|
+
name: "my-resource",
|
727
|
+
title: "My Resource", # WARNING: This is a `Draft` and is not supported in the `Version 2025-06-18 (latest)` specification.
|
728
|
+
description: "Lorem ipsum dolor sit amet",
|
729
|
+
mime_type: "text/html",
|
467
730
|
)
|
468
731
|
|
469
732
|
server = MCP::Server.new(
|
@@ -479,13 +742,127 @@ server.resources_read_handler do |params|
|
|
479
742
|
[{
|
480
743
|
uri: params[:uri],
|
481
744
|
mimeType: "text/plain",
|
482
|
-
text: "Hello
|
745
|
+
text: "Hello from example resource! URI: #{params[:uri]}"
|
483
746
|
}]
|
484
747
|
end
|
748
|
+
```
|
749
|
+
|
750
|
+
otherwise `resources/read` requests will be a no-op.
|
751
|
+
|
752
|
+
### Resource Templates
|
753
|
+
|
754
|
+
The `MCP::ResourceTemplate` class provides a way to register resource templates with the server.
|
755
|
+
|
756
|
+
```ruby
|
757
|
+
resource_template = MCP::ResourceTemplate.new(
|
758
|
+
uri_template: "https://example.com/my_resource_template",
|
759
|
+
name: "my-resource-template",
|
760
|
+
title: "My Resource Template", # WARNING: This is a `Draft` and is not supported in the `Version 2025-06-18 (latest)` specification.
|
761
|
+
description: "Lorem ipsum dolor sit amet",
|
762
|
+
mime_type: "text/html",
|
763
|
+
)
|
764
|
+
|
765
|
+
server = MCP::Server.new(
|
766
|
+
name: "my_server",
|
767
|
+
resource_templates: [resource_template],
|
768
|
+
)
|
769
|
+
```
|
770
|
+
|
771
|
+
## Building an MCP Client
|
772
|
+
|
773
|
+
The `MCP::Client` class provides an interface for interacting with MCP servers.
|
774
|
+
|
775
|
+
This class supports:
|
776
|
+
|
777
|
+
- Tool listing via the `tools/list` method
|
778
|
+
- Tool invocation via the `tools/call` method
|
779
|
+
- Automatic JSON-RPC 2.0 message formatting
|
780
|
+
- UUID request ID generation
|
781
|
+
|
782
|
+
Clients are initialized with a transport layer instance that handles the low-level communication mechanics.
|
783
|
+
Authorization is handled by the transport layer.
|
784
|
+
|
785
|
+
## Transport Layer Interface
|
786
|
+
|
787
|
+
If the transport layer you need is not included in the gem, you can build and pass your own instances so long as they conform to the following interface:
|
788
|
+
|
789
|
+
```ruby
|
790
|
+
class CustomTransport
|
791
|
+
# Sends a JSON-RPC request to the server and returns the raw response.
|
792
|
+
#
|
793
|
+
# @param request [Hash] A complete JSON-RPC request object.
|
794
|
+
# https://www.jsonrpc.org/specification#request_object
|
795
|
+
# @return [Hash] A hash modeling a JSON-RPC response object.
|
796
|
+
# https://www.jsonrpc.org/specification#response_object
|
797
|
+
def send_request(request:)
|
798
|
+
# Your transport-specific logic here
|
799
|
+
# - HTTP: POST to endpoint with JSON body
|
800
|
+
# - WebSocket: Send message over WebSocket
|
801
|
+
# - stdio: Write to stdout, read from stdin
|
802
|
+
# - etc.
|
803
|
+
end
|
804
|
+
end
|
805
|
+
```
|
806
|
+
|
807
|
+
### HTTP Transport Layer
|
808
|
+
|
809
|
+
Use the `MCP::Client::HTTP` transport to interact with MCP servers using simple HTTP requests.
|
485
810
|
|
811
|
+
You'll need to add `faraday` as a dependency in order to use the HTTP transport layer:
|
812
|
+
|
813
|
+
```ruby
|
814
|
+
gem 'mcp'
|
815
|
+
gem 'faraday', '>= 2.0'
|
816
|
+
```
|
817
|
+
|
818
|
+
Example usage:
|
819
|
+
|
820
|
+
```ruby
|
821
|
+
http_transport = MCP::Client::HTTP.new(url: "https://api.example.com/mcp")
|
822
|
+
client = MCP::Client.new(transport: http_transport)
|
823
|
+
|
824
|
+
# List available tools
|
825
|
+
tools = client.tools
|
826
|
+
tools.each do |tool|
|
827
|
+
puts <<~TOOL_INFORMATION
|
828
|
+
Tool: #{tool.name}
|
829
|
+
Description: #{tool.description}
|
830
|
+
Input Schema: #{tool.input_schema}
|
831
|
+
TOOL_INFORMATION
|
832
|
+
end
|
833
|
+
|
834
|
+
# Call a specific tool
|
835
|
+
response = client.call_tool(
|
836
|
+
tool: tools.first,
|
837
|
+
arguments: { message: "Hello, world!" }
|
838
|
+
)
|
839
|
+
```
|
840
|
+
|
841
|
+
#### HTTP Authorization
|
842
|
+
|
843
|
+
By default, the HTTP transport layer provides no authentication to the server, but you can provide custom headers if you need authentication. For example, to use Bearer token authentication:
|
844
|
+
|
845
|
+
```ruby
|
846
|
+
http_transport = MCP::Client::HTTP.new(
|
847
|
+
url: "https://api.example.com/mcp",
|
848
|
+
headers: {
|
849
|
+
"Authorization" => "Bearer my_token"
|
850
|
+
}
|
851
|
+
)
|
852
|
+
|
853
|
+
client = MCP::Client.new(transport: http_transport)
|
854
|
+
client.tools # will make the call using Bearer auth
|
486
855
|
```
|
487
856
|
|
488
|
-
|
857
|
+
You can add any custom headers needed for your authentication scheme, or for any other purpose. The client will include these headers on every request.
|
858
|
+
|
859
|
+
### Tool Objects
|
860
|
+
|
861
|
+
The client provides a wrapper class for tools returned by the server:
|
862
|
+
|
863
|
+
- `MCP::Client::Tool` - Represents a single tool with its metadata
|
864
|
+
|
865
|
+
This class provides easy access to tool properties like name, description, input schema, and output schema.
|
489
866
|
|
490
867
|
## Releases
|
491
868
|
|
@@ -494,7 +871,8 @@ This gem is published to [RubyGems.org](https://rubygems.org/gems/mcp)
|
|
494
871
|
Releases are triggered by PRs to the `main` branch updating the version number in `lib/mcp/version.rb`.
|
495
872
|
|
496
873
|
1. **Update the version number** in `lib/mcp/version.rb`, following [semver](https://semver.org/)
|
497
|
-
|
498
|
-
|
874
|
+
1. **Update CHANGELOG.md**, backfilling the changes since the last release if necessary, and adding a new section for the new version, clearing out the Unreleased section
|
875
|
+
1. **Create a PR and get approval from a maintainer**
|
876
|
+
1. **Merge your PR to the main branch** - This will automatically trigger the release workflow via GitHub Actions
|
499
877
|
|
500
878
|
When changes are merged to the `main` branch, the GitHub Actions workflow (`.github/workflows/release.yml`) is triggered and the gem is published to RubyGems.
|