mcp 0.2.0 → 0.4.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/.github/dependabot.yml +6 -0
- data/.github/workflows/ci.yml +2 -2
- data/.github/workflows/release.yml +1 -1
- data/.rubocop.yml +3 -0
- data/AGENTS.md +119 -0
- data/CHANGELOG.md +56 -0
- data/Gemfile +7 -4
- data/README.md +393 -50
- data/examples/http_client.rb +13 -13
- data/examples/http_server.rb +14 -12
- data/examples/stdio_server.rb +4 -4
- data/examples/streamable_http_client.rb +10 -12
- data/examples/streamable_http_server.rb +32 -32
- data/lib/mcp/client/http.rb +88 -0
- data/lib/mcp/client/tool.rb +16 -0
- data/lib/mcp/client.rb +116 -0
- data/lib/mcp/configuration.rb +31 -4
- data/lib/mcp/content.rb +0 -1
- data/lib/mcp/prompt/argument.rb +9 -5
- data/lib/mcp/prompt/message.rb +0 -1
- data/lib/mcp/prompt/result.rb +0 -1
- data/lib/mcp/prompt.rb +30 -3
- data/lib/mcp/resource/contents.rb +0 -1
- data/lib/mcp/resource/embedded.rb +0 -1
- data/lib/mcp/resource.rb +8 -7
- data/lib/mcp/resource_template.rb +8 -7
- data/lib/mcp/server/transports/streamable_http_transport.rb +21 -1
- data/lib/mcp/server.rb +43 -14
- data/lib/mcp/string_utils.rb +0 -1
- data/lib/mcp/tool/annotations.rb +4 -4
- data/lib/mcp/tool/input_schema.rb +10 -2
- data/lib/mcp/tool/output_schema.rb +69 -0
- data/lib/mcp/tool/response.rb +17 -5
- data/lib/mcp/tool.rb +47 -6
- data/lib/mcp/version.rb +1 -1
- data/lib/mcp.rb +4 -0
- metadata +8 -3
- data/.cursor/rules/release-changelogs.mdc +0 -17
data/README.md
CHANGED
@@ -22,7 +22,9 @@ Or install it yourself as:
|
|
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.
|
@@ -108,9 +110,9 @@ The server supports sending notifications to clients when lists of tools, prompt
|
|
108
110
|
|
109
111
|
The server provides three notification methods:
|
110
112
|
|
111
|
-
- `notify_tools_list_changed
|
112
|
-
- `notify_prompts_list_changed
|
113
|
-
- `notify_resources_list_changed
|
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
|
114
116
|
|
115
117
|
#### Notification Format
|
116
118
|
|
@@ -122,19 +124,19 @@ Notifications follow the JSON-RPC 2.0 specification and use these method names:
|
|
122
124
|
|
123
125
|
#### Transport Support
|
124
126
|
|
125
|
-
- **
|
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)
|
127
129
|
|
128
130
|
#### Usage Example
|
129
131
|
|
130
132
|
```ruby
|
131
133
|
server = MCP::Server.new(name: "my_server")
|
132
|
-
transport = MCP::Transports::
|
134
|
+
transport = MCP::Server::Transports::StreamableHTTPTransport.new(server)
|
133
135
|
server.transport = transport
|
134
136
|
|
135
137
|
# When tools change, notify clients
|
136
138
|
server.define_tool(name: "new_tool") { |**args| { result: "ok" } }
|
137
|
-
server.notify_tools_list_changed
|
139
|
+
server.notify_tools_list_changed
|
138
140
|
```
|
139
141
|
|
140
142
|
### Unsupported Features ( to be implemented in future versions )
|
@@ -148,18 +150,19 @@ server.notify_tools_list_changed()
|
|
148
150
|
#### Rails Controller
|
149
151
|
|
150
152
|
When added to a Rails controller on a route that handles POST requests, your server will be compliant with non-streaming
|
151
|
-
[Streamable HTTP](https://modelcontextprotocol.io/specification/2025-
|
153
|
+
[Streamable HTTP](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#streamable-http) transport
|
152
154
|
requests.
|
153
155
|
|
154
156
|
You can use the `Server#handle_json` method to handle requests.
|
155
157
|
|
156
158
|
```ruby
|
157
159
|
class ApplicationController < ActionController::Base
|
158
|
-
|
159
160
|
def index
|
160
161
|
server = MCP::Server.new(
|
161
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.
|
162
164
|
version: "1.0.0",
|
165
|
+
instructions: "Use the tools of this server as a last resort",
|
163
166
|
tools: [SomeTool, AnotherTool],
|
164
167
|
prompts: [MyPrompt],
|
165
168
|
server_context: { user_id: current_user.id },
|
@@ -175,7 +178,6 @@ If you want to build a local command-line application, you can use the stdio tra
|
|
175
178
|
|
176
179
|
```ruby
|
177
180
|
require "mcp"
|
178
|
-
require "mcp/server/transports/stdio_transport"
|
179
181
|
|
180
182
|
# Create a simple tool
|
181
183
|
class ExampleTool < MCP::Tool
|
@@ -214,9 +216,10 @@ You can run this script and then type in requests to the server at the command l
|
|
214
216
|
$ ruby examples/stdio_server.rb
|
215
217
|
{"jsonrpc":"2.0","id":"1","method":"ping"}
|
216
218
|
{"jsonrpc":"2.0","id":"2","method":"tools/list"}
|
219
|
+
{"jsonrpc":"2.0","id":"3","method":"tools/call","params":{"name":"example_tool","arguments":{"message":"Hello"}}}
|
217
220
|
```
|
218
221
|
|
219
|
-
|
222
|
+
### Configuration
|
220
223
|
|
221
224
|
The gem can be configured using the `MCP.configure` block:
|
222
225
|
|
@@ -326,19 +329,22 @@ config.instrumentation_callback = ->(data) {
|
|
326
329
|
|
327
330
|
### Server Protocol Version
|
328
331
|
|
329
|
-
The server's protocol version can be overridden using the `protocol_version`
|
332
|
+
The server's protocol version can be overridden using the `protocol_version` keyword argument:
|
330
333
|
|
331
334
|
```ruby
|
332
|
-
MCP::
|
335
|
+
configuration = MCP::Configuration.new(protocol_version: "2024-11-05")
|
336
|
+
MCP::Server.new(name: "test_server", configuration: configuration)
|
333
337
|
```
|
334
338
|
|
335
339
|
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`:
|
336
340
|
|
337
341
|
```ruby
|
338
|
-
MCP::
|
342
|
+
MCP::Configuration.new(protocol_version: nil)
|
339
343
|
```
|
340
344
|
|
341
|
-
|
345
|
+
If an invalid `protocol_version` value is set, an `ArgumentError` is raised.
|
346
|
+
|
347
|
+
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.
|
342
348
|
|
343
349
|
### Exception Reporting
|
344
350
|
|
@@ -360,16 +366,17 @@ When an exception occurs:
|
|
360
366
|
|
361
367
|
If no exception reporter is configured, a default no-op reporter is used that silently ignores exceptions.
|
362
368
|
|
363
|
-
|
369
|
+
### Tools
|
364
370
|
|
365
|
-
MCP spec includes [Tools](https://modelcontextprotocol.io/
|
371
|
+
MCP spec includes [Tools](https://modelcontextprotocol.io/specification/2025-06-18/server/tools) which provide functionality to LLM apps.
|
366
372
|
|
367
|
-
This gem provides a `MCP::Tool` class that can be used to create tools in
|
373
|
+
This gem provides a `MCP::Tool` class that can be used to create tools in three ways:
|
368
374
|
|
369
375
|
1. As a class definition:
|
370
376
|
|
371
377
|
```ruby
|
372
378
|
class MyTool < MCP::Tool
|
379
|
+
title "My Tool" # WARNING: This is a `Draft` and is not supported in the `Version 2025-06-18 (latest)` specification.
|
373
380
|
description "This tool performs specific functionality..."
|
374
381
|
input_schema(
|
375
382
|
properties: {
|
@@ -377,12 +384,20 @@ class MyTool < MCP::Tool
|
|
377
384
|
},
|
378
385
|
required: ["message"]
|
379
386
|
)
|
387
|
+
output_schema(
|
388
|
+
properties: {
|
389
|
+
result: { type: "string" },
|
390
|
+
success: { type: "boolean" },
|
391
|
+
timestamp: { type: "string", format: "date-time" }
|
392
|
+
},
|
393
|
+
required: ["result", "success", "timestamp"]
|
394
|
+
)
|
380
395
|
annotations(
|
381
|
-
title: "My Tool",
|
382
396
|
read_only_hint: true,
|
383
397
|
destructive_hint: false,
|
384
398
|
idempotent_hint: true,
|
385
|
-
open_world_hint: false
|
399
|
+
open_world_hint: false,
|
400
|
+
title: "My Tool"
|
386
401
|
)
|
387
402
|
|
388
403
|
def self.call(message:, server_context:)
|
@@ -397,6 +412,23 @@ tool = MyTool
|
|
397
412
|
|
398
413
|
```ruby
|
399
414
|
tool = MCP::Tool.define(
|
415
|
+
name: "my_tool",
|
416
|
+
title: "My Tool", # WARNING: This is a `Draft` and is not supported in the `Version 2025-06-18 (latest)` specification.
|
417
|
+
description: "This tool performs specific functionality...",
|
418
|
+
annotations: {
|
419
|
+
read_only_hint: true,
|
420
|
+
title: "My Tool"
|
421
|
+
}
|
422
|
+
) do |args, server_context|
|
423
|
+
MCP::Tool::Response.new([{ type: "text", text: "OK" }])
|
424
|
+
end
|
425
|
+
```
|
426
|
+
|
427
|
+
3. By using the `MCP::Server#define_tool` method with a block:
|
428
|
+
|
429
|
+
```ruby
|
430
|
+
server = MCP::Server.new
|
431
|
+
server.define_tool(
|
400
432
|
name: "my_tool",
|
401
433
|
description: "This tool performs specific functionality...",
|
402
434
|
annotations: {
|
@@ -415,46 +447,201 @@ e.g. around authentication state.
|
|
415
447
|
|
416
448
|
Tools can include annotations that provide additional metadata about their behavior. The following annotations are supported:
|
417
449
|
|
450
|
+
- `destructive_hint`: Indicates if the tool performs destructive operations. Defaults to true
|
451
|
+
- `idempotent_hint`: Indicates if the tool's operations are idempotent. Defaults to false
|
452
|
+
- `open_world_hint`: Indicates if the tool operates in an open world context. Defaults to true
|
453
|
+
- `read_only_hint`: Indicates if the tool only reads data (doesn't modify state). Defaults to false
|
418
454
|
- `title`: A human-readable title for the tool
|
419
|
-
- `read_only_hint`: Indicates if the tool only reads data (doesn't modify state)
|
420
|
-
- `destructive_hint`: Indicates if the tool performs destructive operations
|
421
|
-
- `idempotent_hint`: Indicates if the tool's operations are idempotent
|
422
|
-
- `open_world_hint`: Indicates if the tool operates in an open world context
|
423
455
|
|
424
456
|
Annotations can be set either through the class definition using the `annotations` class method or when defining a tool using the `define` method.
|
425
457
|
|
426
|
-
|
458
|
+
> [!NOTE]
|
459
|
+
> This **Tool Annotations** feature is supported starting from `protocol_version: '2025-03-26'`.
|
460
|
+
|
461
|
+
### Tool Output Schemas
|
462
|
+
|
463
|
+
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:
|
464
|
+
|
465
|
+
1. **Class definition with output_schema:**
|
466
|
+
|
467
|
+
```ruby
|
468
|
+
class WeatherTool < MCP::Tool
|
469
|
+
tool_name "get_weather"
|
470
|
+
description "Get current weather for a location"
|
471
|
+
|
472
|
+
input_schema(
|
473
|
+
properties: {
|
474
|
+
location: { type: "string" },
|
475
|
+
units: { type: "string", enum: ["celsius", "fahrenheit"] }
|
476
|
+
},
|
477
|
+
required: ["location"]
|
478
|
+
)
|
479
|
+
|
480
|
+
output_schema(
|
481
|
+
properties: {
|
482
|
+
temperature: { type: "number" },
|
483
|
+
condition: { type: "string" },
|
484
|
+
humidity: { type: "integer" }
|
485
|
+
},
|
486
|
+
required: ["temperature", "condition", "humidity"]
|
487
|
+
)
|
488
|
+
|
489
|
+
def self.call(location:, units: "celsius", server_context:)
|
490
|
+
# Call weather API and structure the response
|
491
|
+
api_response = WeatherAPI.fetch(location, units)
|
492
|
+
weather_data = {
|
493
|
+
temperature: api_response.temp,
|
494
|
+
condition: api_response.description,
|
495
|
+
humidity: api_response.humidity_percent
|
496
|
+
}
|
497
|
+
|
498
|
+
output_schema.validate_result(weather_data)
|
499
|
+
|
500
|
+
MCP::Tool::Response.new([{
|
501
|
+
type: "text",
|
502
|
+
text: weather_data.to_json
|
503
|
+
}])
|
504
|
+
end
|
505
|
+
end
|
506
|
+
```
|
507
|
+
|
508
|
+
2. **Using Tool.define with output_schema:**
|
509
|
+
|
510
|
+
```ruby
|
511
|
+
tool = MCP::Tool.define(
|
512
|
+
name: "calculate_stats",
|
513
|
+
description: "Calculate statistics for a dataset",
|
514
|
+
input_schema: {
|
515
|
+
properties: {
|
516
|
+
numbers: { type: "array", items: { type: "number" } }
|
517
|
+
},
|
518
|
+
required: ["numbers"]
|
519
|
+
},
|
520
|
+
output_schema: {
|
521
|
+
properties: {
|
522
|
+
mean: { type: "number" },
|
523
|
+
median: { type: "number" },
|
524
|
+
count: { type: "integer" }
|
525
|
+
},
|
526
|
+
required: ["mean", "median", "count"]
|
527
|
+
}
|
528
|
+
) do |args, server_context|
|
529
|
+
# Calculate statistics and validate against schema
|
530
|
+
MCP::Tool::Response.new([{ type: "text", text: "Statistics calculated" }])
|
531
|
+
end
|
532
|
+
```
|
533
|
+
|
534
|
+
3. **Using OutputSchema objects:**
|
535
|
+
|
536
|
+
```ruby
|
537
|
+
class DataTool < MCP::Tool
|
538
|
+
output_schema MCP::Tool::OutputSchema.new(
|
539
|
+
properties: {
|
540
|
+
success: { type: "boolean" },
|
541
|
+
data: { type: "object" }
|
542
|
+
},
|
543
|
+
required: ["success"]
|
544
|
+
)
|
545
|
+
end
|
546
|
+
```
|
547
|
+
|
548
|
+
Output schema may also describe an array of objects:
|
549
|
+
|
550
|
+
```ruby
|
551
|
+
class WeatherTool < MCP::Tool
|
552
|
+
output_schema(
|
553
|
+
type: "array",
|
554
|
+
item: {
|
555
|
+
properties: {
|
556
|
+
temperature: { type: "number" },
|
557
|
+
condition: { type: "string" },
|
558
|
+
humidity: { type: "integer" }
|
559
|
+
},
|
560
|
+
required: ["temperature", "condition", "humidity"]
|
561
|
+
}
|
562
|
+
)
|
563
|
+
end
|
564
|
+
```
|
565
|
+
|
566
|
+
Please note: in this case, you must provide `type: "array"`. The default type
|
567
|
+
for output schemas is `object`.
|
568
|
+
|
569
|
+
MCP spec for the [Output Schema](https://modelcontextprotocol.io/specification/2025-06-18/server/tools#output-schema) specifies that:
|
570
|
+
|
571
|
+
- **Server Validation**: Servers MUST provide structured results that conform to the output schema
|
572
|
+
- **Client Validation**: Clients SHOULD validate structured results against the output schema
|
573
|
+
- **Better Integration**: Enables strict schema validation, type information, and improved developer experience
|
574
|
+
- **Backward Compatibility**: Tools returning structured content SHOULD also include serialized JSON in a TextContent block
|
575
|
+
|
576
|
+
The output schema follows standard JSON Schema format and helps ensure consistent data exchange between MCP servers and clients.
|
577
|
+
|
578
|
+
### Tool Responses with Structured Content
|
579
|
+
|
580
|
+
Tools can return structured data alongside text content using the `structured_content` parameter.
|
581
|
+
|
582
|
+
The structured content will be included in the JSON-RPC response as the `structuredContent` field.
|
583
|
+
|
584
|
+
```ruby
|
585
|
+
class APITool < MCP::Tool
|
586
|
+
description "Get current weather and return structured data"
|
587
|
+
|
588
|
+
def self.call(endpoint:, server_context:)
|
589
|
+
# Call weather API and structure the response
|
590
|
+
api_response = WeatherAPI.fetch(location, units)
|
591
|
+
weather_data = {
|
592
|
+
temperature: api_response.temp,
|
593
|
+
condition: api_response.description,
|
594
|
+
humidity: api_response.humidity_percent
|
595
|
+
}
|
596
|
+
|
597
|
+
output_schema.validate_result(weather_data)
|
598
|
+
|
599
|
+
MCP::Tool::Response.new(
|
600
|
+
[{
|
601
|
+
type: "text",
|
602
|
+
text: weather_data.to_json
|
603
|
+
}],
|
604
|
+
structured_content: weather_data
|
605
|
+
)
|
606
|
+
end
|
607
|
+
end
|
608
|
+
```
|
609
|
+
|
610
|
+
### Prompts
|
427
611
|
|
428
|
-
MCP spec includes [Prompts](https://modelcontextprotocol.io/
|
612
|
+
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.
|
429
613
|
|
430
|
-
The `MCP::Prompt` class provides
|
614
|
+
The `MCP::Prompt` class provides three ways to create prompts:
|
431
615
|
|
432
616
|
1. As a class definition with metadata:
|
433
617
|
|
434
618
|
```ruby
|
435
619
|
class MyPrompt < MCP::Prompt
|
436
620
|
prompt_name "my_prompt" # Optional - defaults to underscored class name
|
621
|
+
title "My Prompt" # WARNING: This is a `Draft` and is not supported in the `Version 2025-06-18 (latest)` specification.
|
437
622
|
description "This prompt performs specific functionality..."
|
438
623
|
arguments [
|
439
|
-
Prompt::Argument.new(
|
624
|
+
MCP::Prompt::Argument.new(
|
440
625
|
name: "message",
|
626
|
+
title: "Message Title",
|
441
627
|
description: "Input message",
|
442
628
|
required: true
|
443
629
|
)
|
444
630
|
]
|
631
|
+
meta({ version: "1.0", category: "example" })
|
445
632
|
|
446
633
|
class << self
|
447
634
|
def template(args, server_context:)
|
448
|
-
Prompt::Result.new(
|
635
|
+
MCP::Prompt::Result.new(
|
449
636
|
description: "Response description",
|
450
637
|
messages: [
|
451
|
-
Prompt::Message.new(
|
638
|
+
MCP::Prompt::Message.new(
|
452
639
|
role: "user",
|
453
|
-
content: Content::Text.new("User message")
|
640
|
+
content: MCP::Content::Text.new("User message")
|
454
641
|
),
|
455
|
-
Prompt::Message.new(
|
642
|
+
MCP::Prompt::Message.new(
|
456
643
|
role: "assistant",
|
457
|
-
content: Content::Text.new(args["message"])
|
644
|
+
content: MCP::Content::Text.new(args["message"])
|
458
645
|
)
|
459
646
|
]
|
460
647
|
)
|
@@ -469,15 +656,51 @@ prompt = MyPrompt
|
|
469
656
|
|
470
657
|
```ruby
|
471
658
|
prompt = MCP::Prompt.define(
|
659
|
+
name: "my_prompt",
|
660
|
+
title: "My Prompt", # WARNING: This is a `Draft` and is not supported in the `Version 2025-06-18 (latest)` specification.
|
661
|
+
description: "This prompt performs specific functionality...",
|
662
|
+
arguments: [
|
663
|
+
MCP::Prompt::Argument.new(
|
664
|
+
name: "message",
|
665
|
+
title: "Message Title",
|
666
|
+
description: "Input message",
|
667
|
+
required: true
|
668
|
+
)
|
669
|
+
],
|
670
|
+
meta: { version: "1.0", category: "example" }
|
671
|
+
) do |args, server_context:|
|
672
|
+
MCP::Prompt::Result.new(
|
673
|
+
description: "Response description",
|
674
|
+
messages: [
|
675
|
+
MCP::Prompt::Message.new(
|
676
|
+
role: "user",
|
677
|
+
content: MCP::Content::Text.new("User message")
|
678
|
+
),
|
679
|
+
MCP::Prompt::Message.new(
|
680
|
+
role: "assistant",
|
681
|
+
content: MCP::Content::Text.new(args["message"])
|
682
|
+
)
|
683
|
+
]
|
684
|
+
)
|
685
|
+
end
|
686
|
+
```
|
687
|
+
|
688
|
+
3. Using the `MCP::Server#define_prompt` method:
|
689
|
+
|
690
|
+
```ruby
|
691
|
+
server = MCP::Server.new
|
692
|
+
server.define_prompt(
|
472
693
|
name: "my_prompt",
|
473
694
|
description: "This prompt performs specific functionality...",
|
474
695
|
arguments: [
|
475
696
|
Prompt::Argument.new(
|
476
697
|
name: "message",
|
698
|
+
title: "Message Title",
|
477
699
|
description: "Input message",
|
478
700
|
required: true
|
479
701
|
)
|
480
|
-
]
|
702
|
+
],
|
703
|
+
meta: { version: "1.0", category: "example" }
|
481
704
|
) do |args, server_context:|
|
482
705
|
Prompt::Result.new(
|
483
706
|
description: "Response description",
|
@@ -500,10 +723,10 @@ e.g. around authentication state or user preferences.
|
|
500
723
|
|
501
724
|
### Key Components
|
502
725
|
|
503
|
-
- `Prompt::Argument` - Defines input parameters for the prompt template
|
504
|
-
- `Prompt::Message` - Represents a message in the conversation with a role and content
|
505
|
-
- `Prompt::Result` - The output of a prompt template containing description and messages
|
506
|
-
- `Content::Text` - Text content for messages
|
726
|
+
- `MCP::Prompt::Argument` - Defines input parameters for the prompt template with name, title, description, and required flag
|
727
|
+
- `MCP::Prompt::Message` - Represents a message in the conversation with a role and content
|
728
|
+
- `MCP::Prompt::Result` - The output of a prompt template containing description and messages
|
729
|
+
- `MCP::Content::Text` - Text content for messages
|
507
730
|
|
508
731
|
### Usage
|
509
732
|
|
@@ -536,26 +759,30 @@ end
|
|
536
759
|
```
|
537
760
|
|
538
761
|
The data contains the following keys:
|
539
|
-
|
540
|
-
`
|
541
|
-
`
|
542
|
-
`
|
543
|
-
`
|
544
|
-
`
|
762
|
+
|
763
|
+
- `method`: the method called, e.g. `ping`, `tools/list`, `tools/call` etc
|
764
|
+
- `tool_name`: the name of the tool called
|
765
|
+
- `prompt_name`: the name of the prompt called
|
766
|
+
- `resource_uri`: the uri of the resource called
|
767
|
+
- `error`: if looking up tools/prompts etc failed, e.g. `tool_not_found`
|
768
|
+
- `duration`: the duration of the call in seconds
|
545
769
|
|
546
770
|
`tool_name`, `prompt_name` and `resource_uri` are only populated if a matching handler is registered.
|
547
771
|
This is to avoid potential issues with metric cardinality
|
548
772
|
|
549
|
-
|
773
|
+
### Resources
|
774
|
+
|
775
|
+
MCP spec includes [Resources](https://modelcontextprotocol.io/specification/2025-06-18/server/resources).
|
550
776
|
|
551
|
-
|
777
|
+
### Reading Resources
|
552
778
|
|
553
779
|
The `MCP::Resource` class provides a way to register resources with the server.
|
554
780
|
|
555
781
|
```ruby
|
556
782
|
resource = MCP::Resource.new(
|
557
783
|
uri: "https://example.com/my_resource",
|
558
|
-
name: "
|
784
|
+
name: "my-resource",
|
785
|
+
title: "My Resource", # WARNING: This is a `Draft` and is not supported in the `Version 2025-06-18 (latest)` specification.
|
559
786
|
description: "Lorem ipsum dolor sit amet",
|
560
787
|
mime_type: "text/html",
|
561
788
|
)
|
@@ -573,14 +800,130 @@ server.resources_read_handler do |params|
|
|
573
800
|
[{
|
574
801
|
uri: params[:uri],
|
575
802
|
mimeType: "text/plain",
|
576
|
-
text: params[:uri]
|
803
|
+
text: "Hello from example resource! URI: #{params[:uri]}"
|
577
804
|
}]
|
578
805
|
end
|
579
|
-
|
580
806
|
```
|
581
807
|
|
582
808
|
otherwise `resources/read` requests will be a no-op.
|
583
809
|
|
810
|
+
### Resource Templates
|
811
|
+
|
812
|
+
The `MCP::ResourceTemplate` class provides a way to register resource templates with the server.
|
813
|
+
|
814
|
+
```ruby
|
815
|
+
resource_template = MCP::ResourceTemplate.new(
|
816
|
+
uri_template: "https://example.com/my_resource_template",
|
817
|
+
name: "my-resource-template",
|
818
|
+
title: "My Resource Template", # WARNING: This is a `Draft` and is not supported in the `Version 2025-06-18 (latest)` specification.
|
819
|
+
description: "Lorem ipsum dolor sit amet",
|
820
|
+
mime_type: "text/html",
|
821
|
+
)
|
822
|
+
|
823
|
+
server = MCP::Server.new(
|
824
|
+
name: "my_server",
|
825
|
+
resource_templates: [resource_template],
|
826
|
+
)
|
827
|
+
```
|
828
|
+
|
829
|
+
## Building an MCP Client
|
830
|
+
|
831
|
+
The `MCP::Client` class provides an interface for interacting with MCP servers.
|
832
|
+
|
833
|
+
This class supports:
|
834
|
+
|
835
|
+
- Tool listing via the `tools/list` method (`MCP::Client#tools`)
|
836
|
+
- Tool invocation via the `tools/call` method (`MCP::Client#call_tools`)
|
837
|
+
- Resource listing via the `resources/list` method (`MCP::Client#resources`)
|
838
|
+
- Resource reading via the `resources/read` method (`MCP::Client#read_resources`)
|
839
|
+
- Automatic JSON-RPC 2.0 message formatting
|
840
|
+
- UUID request ID generation
|
841
|
+
|
842
|
+
Clients are initialized with a transport layer instance that handles the low-level communication mechanics.
|
843
|
+
Authorization is handled by the transport layer.
|
844
|
+
|
845
|
+
## Transport Layer Interface
|
846
|
+
|
847
|
+
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:
|
848
|
+
|
849
|
+
```ruby
|
850
|
+
class CustomTransport
|
851
|
+
# Sends a JSON-RPC request to the server and returns the raw response.
|
852
|
+
#
|
853
|
+
# @param request [Hash] A complete JSON-RPC request object.
|
854
|
+
# https://www.jsonrpc.org/specification#request_object
|
855
|
+
# @return [Hash] A hash modeling a JSON-RPC response object.
|
856
|
+
# https://www.jsonrpc.org/specification#response_object
|
857
|
+
def send_request(request:)
|
858
|
+
# Your transport-specific logic here
|
859
|
+
# - HTTP: POST to endpoint with JSON body
|
860
|
+
# - WebSocket: Send message over WebSocket
|
861
|
+
# - stdio: Write to stdout, read from stdin
|
862
|
+
# - etc.
|
863
|
+
end
|
864
|
+
end
|
865
|
+
```
|
866
|
+
|
867
|
+
### HTTP Transport Layer
|
868
|
+
|
869
|
+
Use the `MCP::Client::HTTP` transport to interact with MCP servers using simple HTTP requests.
|
870
|
+
|
871
|
+
You'll need to add `faraday` as a dependency in order to use the HTTP transport layer:
|
872
|
+
|
873
|
+
```ruby
|
874
|
+
gem 'mcp'
|
875
|
+
gem 'faraday', '>= 2.0'
|
876
|
+
```
|
877
|
+
|
878
|
+
Example usage:
|
879
|
+
|
880
|
+
```ruby
|
881
|
+
http_transport = MCP::Client::HTTP.new(url: "https://api.example.com/mcp")
|
882
|
+
client = MCP::Client.new(transport: http_transport)
|
883
|
+
|
884
|
+
# List available tools
|
885
|
+
tools = client.tools
|
886
|
+
tools.each do |tool|
|
887
|
+
puts <<~TOOL_INFORMATION
|
888
|
+
Tool: #{tool.name}
|
889
|
+
Description: #{tool.description}
|
890
|
+
Input Schema: #{tool.input_schema}
|
891
|
+
TOOL_INFORMATION
|
892
|
+
end
|
893
|
+
|
894
|
+
# Call a specific tool
|
895
|
+
response = client.call_tool(
|
896
|
+
tool: tools.first,
|
897
|
+
arguments: { message: "Hello, world!" }
|
898
|
+
)
|
899
|
+
```
|
900
|
+
|
901
|
+
#### HTTP Authorization
|
902
|
+
|
903
|
+
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:
|
904
|
+
|
905
|
+
```ruby
|
906
|
+
http_transport = MCP::Client::HTTP.new(
|
907
|
+
url: "https://api.example.com/mcp",
|
908
|
+
headers: {
|
909
|
+
"Authorization" => "Bearer my_token"
|
910
|
+
}
|
911
|
+
)
|
912
|
+
|
913
|
+
client = MCP::Client.new(transport: http_transport)
|
914
|
+
client.tools # will make the call using Bearer auth
|
915
|
+
```
|
916
|
+
|
917
|
+
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.
|
918
|
+
|
919
|
+
### Tool Objects
|
920
|
+
|
921
|
+
The client provides a wrapper class for tools returned by the server:
|
922
|
+
|
923
|
+
- `MCP::Client::Tool` - Represents a single tool with its metadata
|
924
|
+
|
925
|
+
This class provides easy access to tool properties like name, description, input schema, and output schema.
|
926
|
+
|
584
927
|
## Releases
|
585
928
|
|
586
929
|
This gem is published to [RubyGems.org](https://rubygems.org/gems/mcp)
|