Skip to content

Commit 86a173a

Browse files
committed
Merge remote-tracking branch 'upstream/main' into add-basic-http-client-support
2 parents 47d969e + 9486c63 commit 86a173a

25 files changed

+445
-194
lines changed

.rubocop.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ plugins:
77

88
Gemspec/DevelopmentDependencies:
99
Enabled: true
10+
11+
Minitest/LiteralAsActualArgument:
12+
Enabled: true

Gemfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ gem "sorbet-static-and-runtime"
2222
group :test do
2323
gem "faraday", ">= 2.0"
2424
gem "minitest", "~> 5.1", require: false
25-
gem "minitest-reporters"
2625
gem "mocha"
2726
gem "webmock"
2827
end

README.md

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,9 @@ The server supports sending notifications to clients when lists of tools, prompt
110110

111111
The server provides three notification methods:
112112

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
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
116116

117117
#### Notification Format
118118

@@ -136,7 +136,7 @@ server.transport = transport
136136

137137
# When tools change, notify clients
138138
server.define_tool(name: "new_tool") { |**args| { result: "ok" } }
139-
server.notify_tools_list_changed()
139+
server.notify_tools_list_changed
140140
```
141141

142142
### Unsupported Features ( to be implemented in future versions )
@@ -157,11 +157,11 @@ You can use the `Server#handle_json` method to handle requests.
157157

158158
```ruby
159159
class ApplicationController < ActionController::Base
160-
161160
def index
162161
server = MCP::Server.new(
163162
name: "my_server",
164163
version: "1.0.0",
164+
instructions: "Use the tools of this server as a last resort",
165165
tools: [SomeTool, AnotherTool],
166166
prompts: [MyPrompt],
167167
server_context: { user_id: current_user.id },
@@ -328,21 +328,22 @@ config.instrumentation_callback = ->(data) {
328328

329329
### Server Protocol Version
330330

331-
The server's protocol version can be overridden using the `protocol_version` class method:
331+
The server's protocol version can be overridden using the `protocol_version` keyword argument:
332332

333333
```ruby
334-
MCP::Server.protocol_version = "2024-11-05"
334+
configuration = MCP::Configuration.new(protocol_version: "2024-11-05")
335+
MCP::Server.new(name: "test_server", configuration: configuration)
335336
```
336337

337338
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`:
338339

339340
```ruby
340-
MCP::Server.protocol_version = nil
341+
MCP::Configuration.new(protocol_version: nil)
341342
```
342343

343344
If an invalid `protocol_version` value is set, an `ArgumentError` is raised.
344345

345-
Be sure to check the [MCP spec](https://modelcontextprotocol.io/specification) for the protocol version to understand the supported features for the version being set.
346+
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.
346347

347348
### Exception Reporting
348349

@@ -366,14 +367,15 @@ If no exception reporter is configured, a default no-op reporter is used that si
366367

367368
### Tools
368369

369-
MCP spec includes [Tools](https://modelcontextprotocol.io/docs/concepts/tools) which provide functionality to LLM apps.
370+
MCP spec includes [Tools](https://modelcontextprotocol.io/specification/2025-06-18/server/tools) which provide functionality to LLM apps.
370371

371372
This gem provides a `MCP::Tool` class that can be used to create tools in two ways:
372373

373374
1. As a class definition:
374375

375376
```ruby
376377
class MyTool < MCP::Tool
378+
title "My Tool" # WARNING: This is a `Draft` and is not supported in the `Version 2025-06-18 (latest)` specification.
377379
description "This tool performs specific functionality..."
378380
input_schema(
379381
properties: {
@@ -382,11 +384,11 @@ class MyTool < MCP::Tool
382384
required: ["message"]
383385
)
384386
annotations(
385-
title: "My Tool",
386387
read_only_hint: true,
387388
destructive_hint: false,
388389
idempotent_hint: true,
389-
open_world_hint: false
390+
open_world_hint: false,
391+
title: "My Tool"
390392
)
391393

392394
def self.call(message:, server_context:)
@@ -402,10 +404,11 @@ tool = MyTool
402404
```ruby
403405
tool = MCP::Tool.define(
404406
name: "my_tool",
407+
title: "My Tool", # WARNING: This is a `Draft` and is not supported in the `Version 2025-06-18 (latest)` specification.
405408
description: "This tool performs specific functionality...",
406409
annotations: {
407-
title: "My Tool",
408-
read_only_hint: true
410+
read_only_hint: true,
411+
title: "My Tool"
409412
}
410413
) do |args, server_context|
411414
MCP::Tool::Response.new([{ type: "text", text: "OK" }])
@@ -419,17 +422,17 @@ e.g. around authentication state.
419422

420423
Tools can include annotations that provide additional metadata about their behavior. The following annotations are supported:
421424

422-
- `title`: A human-readable title for the tool
423-
- `read_only_hint`: Indicates if the tool only reads data (doesn't modify state)
424425
- `destructive_hint`: Indicates if the tool performs destructive operations
425426
- `idempotent_hint`: Indicates if the tool's operations are idempotent
426427
- `open_world_hint`: Indicates if the tool operates in an open world context
428+
- `read_only_hint`: Indicates if the tool only reads data (doesn't modify state)
429+
- `title`: A human-readable title for the tool
427430

428431
Annotations can be set either through the class definition using the `annotations` class method or when defining a tool using the `define` method.
429432

430433
### Prompts
431434

432-
MCP spec includes [Prompts](https://modelcontextprotocol.io/docs/concepts/prompts), which enable servers to define reusable prompt templates and workflows that clients can easily surface to users and LLMs.
435+
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.
433436

434437
The `MCP::Prompt` class provides two ways to create prompts:
435438

@@ -438,6 +441,7 @@ The `MCP::Prompt` class provides two ways to create prompts:
438441
```ruby
439442
class MyPrompt < MCP::Prompt
440443
prompt_name "my_prompt" # Optional - defaults to underscored class name
444+
title "My Prompt" # WARNING: This is a `Draft` and is not supported in the `Version 2025-06-18 (latest)` specification.
441445
description "This prompt performs specific functionality..."
442446
arguments [
443447
MCP::Prompt::Argument.new(
@@ -474,6 +478,7 @@ prompt = MyPrompt
474478
```ruby
475479
prompt = MCP::Prompt.define(
476480
name: "my_prompt",
481+
title: "My Prompt", # WARNING: This is a `Draft` and is not supported in the `Version 2025-06-18 (latest)` specification.
477482
description: "This prompt performs specific functionality...",
478483
arguments: [
479484
MCP::Prompt::Argument.new(
@@ -540,26 +545,28 @@ end
540545
```
541546

542547
The data contains the following keys:
543-
`method`: the method called, e.g. `ping`, `tools/list`, `tools/call` etc
544-
`tool_name`: the name of the tool called
545-
`prompt_name`: the name of the prompt called
546-
`resource_uri`: the uri of the resource called
547-
`error`: if looking up tools/prompts etc failed, e.g. `tool_not_found`
548-
`duration`: the duration of the call in seconds
548+
549+
- `method`: the method called, e.g. `ping`, `tools/list`, `tools/call` etc
550+
- `tool_name`: the name of the tool called
551+
- `prompt_name`: the name of the prompt called
552+
- `resource_uri`: the uri of the resource called
553+
- `error`: if looking up tools/prompts etc failed, e.g. `tool_not_found`
554+
- `duration`: the duration of the call in seconds
549555

550556
`tool_name`, `prompt_name` and `resource_uri` are only populated if a matching handler is registered.
551557
This is to avoid potential issues with metric cardinality
552558

553559
### Resources
554560

555-
MCP spec includes [Resources](https://modelcontextprotocol.io/docs/concepts/resources)
561+
MCP spec includes [Resources](https://modelcontextprotocol.io/specification/2025-06-18/server/resources).
556562

557563
The `MCP::Resource` class provides a way to register resources with the server.
558564

559565
```ruby
560566
resource = MCP::Resource.new(
561567
uri: "https://example.com/my_resource",
562-
name: "My Resource",
568+
name: "my-resource",
569+
title: "My Resource", # WARNING: This is a `Draft` and is not supported in the `Version 2025-06-18 (latest)` specification.
563570
description: "Lorem ipsum dolor sit amet",
564571
mime_type: "text/html",
565572
)
@@ -577,7 +584,7 @@ server.resources_read_handler do |params|
577584
[{
578585
uri: params[:uri],
579586
mimeType: "text/plain",
580-
text: params[:uri],
587+
text: "Hello from example resource! URI: #{params[:uri]}"
581588
}]
582589
end
583590

examples/http_server.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ def template(args, server_context:)
6262
resources: [
6363
MCP::Resource.new(
6464
uri: "https://test_resource.invalid",
65-
name: "Test resource",
65+
name: "test-resource",
66+
title: "Test Resource",
6667
description: "Test resource that echoes back the uri as its content",
6768
mime_type: "text/plain",
6869
),

examples/stdio_server.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ def template(args, server_context:)
5959
resources: [
6060
MCP::Resource.new(
6161
uri: "https://test_resource.invalid",
62-
name: "Test resource",
62+
name: "test-resource",
63+
title: "Test Resource",
6364
description: "Test resource that echoes back the uri as its content",
6465
mime_type: "text/plain",
6566
),

lib/mcp/prompt.rb

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class Prompt
66
class << self
77
NOT_SET = Object.new
88

9+
attr_reader :title_value
910
attr_reader :description_value
1011
attr_reader :arguments_value
1112

@@ -14,12 +15,13 @@ def template(args, server_context: nil)
1415
end
1516

1617
def to_h
17-
{ name: name_value, description: description_value, arguments: arguments_value.map(&:to_h) }.compact
18+
{ name: name_value, title: title_value, description: description_value, arguments: arguments_value.map(&:to_h) }.compact
1819
end
1920

2021
def inherited(subclass)
2122
super
2223
subclass.instance_variable_set(:@name_value, nil)
24+
subclass.instance_variable_set(:@title_value, nil)
2325
subclass.instance_variable_set(:@description_value, nil)
2426
subclass.instance_variable_set(:@arguments_value, nil)
2527
end
@@ -36,6 +38,14 @@ def name_value
3638
@name_value || StringUtils.handle_from_class_name(name)
3739
end
3840

41+
def title(value = NOT_SET)
42+
if value == NOT_SET
43+
@title_value
44+
else
45+
@title_value = value
46+
end
47+
end
48+
3949
def description(value = NOT_SET)
4050
if value == NOT_SET
4151
@description_value
@@ -52,9 +62,10 @@ def arguments(value = NOT_SET)
5262
end
5363
end
5464

55-
def define(name: nil, description: nil, arguments: [], &block)
65+
def define(name: nil, title: nil, description: nil, arguments: [], &block)
5666
Class.new(self) do
5767
prompt_name name
68+
title title
5869
description description
5970
arguments arguments
6071
define_singleton_method(:template) do |args, server_context: nil|

lib/mcp/resource.rb

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,23 @@
33

44
module MCP
55
class Resource
6-
attr_reader :uri, :name, :description, :mime_type
6+
attr_reader :uri, :name, :title, :description, :mime_type
77

8-
def initialize(uri:, name:, description: nil, mime_type: nil)
8+
def initialize(uri:, name:, title: nil, description: nil, mime_type: nil)
99
@uri = uri
1010
@name = name
11+
@title = title
1112
@description = description
1213
@mime_type = mime_type
1314
end
1415

1516
def to_h
1617
{
17-
uri: @uri,
18-
name: @name,
19-
description: @description,
20-
mimeType: @mime_type,
18+
uri: uri,
19+
name: name,
20+
title: title,
21+
description: description,
22+
mimeType: mime_type,
2123
}.compact
2224
end
2325
end

lib/mcp/resource_template.rb

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,23 @@
33

44
module MCP
55
class ResourceTemplate
6-
attr_reader :uri_template, :name, :description, :mime_type
6+
attr_reader :uri_template, :name, :title, :description, :mime_type
77

8-
def initialize(uri_template:, name:, description: nil, mime_type: nil)
8+
def initialize(uri_template:, name:, title: nil, description: nil, mime_type: nil)
99
@uri_template = uri_template
1010
@name = name
11+
@title = title
1112
@description = description
1213
@mime_type = mime_type
1314
end
1415

1516
def to_h
1617
{
17-
uriTemplate: @uri_template,
18-
name: @name,
19-
description: @description,
20-
mimeType: @mime_type,
18+
uriTemplate: uri_template,
19+
name: name,
20+
title: title,
21+
description: description,
22+
mimeType: mime_type,
2123
}.compact
2224
end
2325
end

0 commit comments

Comments
 (0)