Skip to content

Commit 03cd319

Browse files
committed
Add resource template
1 parent 394bbb0 commit 03cd319

File tree

8 files changed

+82
-8
lines changed

8 files changed

+82
-8
lines changed

Gemfile.lock

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ PATH
22
remote: .
33
specs:
44
ruby_mcp (0.1.0)
5+
addressable (~> 2.8, >= 2.8.7)
56
zeitwerk (~> 2.7, >= 2.7.2)
67

78
GEM
@@ -20,6 +21,8 @@ GEM
2021
securerandom (>= 0.3)
2122
tzinfo (~> 2.0, >= 2.0.5)
2223
uri (>= 0.13.1)
24+
addressable (2.8.7)
25+
public_suffix (>= 2.0.2, < 7.0)
2326
ast (2.4.3)
2427
base64 (0.2.0)
2528
benchmark (0.4.0)
@@ -53,6 +56,7 @@ GEM
5356
psych (5.2.3)
5457
date
5558
stringio
59+
public_suffix (6.0.1)
5660
racc (1.8.1)
5761
rack (3.1.12)
5862
rainbow (3.1.1)

lib/ruby_mcp.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
require "securerandom"
33
require "logger"
44
require "zeitwerk"
5+
require "addressable"
56

67
loader = Zeitwerk::Loader.for_gem
78
loader.inflector.inflect("ruby_mcp" => "RubyMCP")
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module RubyMCP::Capabilities::Resources
2+
def add_resource(...)
3+
@resources.add(...)
4+
end
5+
6+
def add_resource_template(uri_template:, name:, description:, mime_type:, reader:)
7+
@resources.add_resource_template(uri_template:, name:, description:, mime_type:, reader:)
8+
end
9+
end

lib/ruby_mcp/handlers/resources_read.rb

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
class RubyMCP::Handlers::ResourcesRead
22
def handle(server, request)
3-
if resource = server.resources.find(request.uri)
3+
resources = server.resources.find(request.uri)
4+
if resources.any?
45
server.answer(request,
5-
contents: [ {
6-
uri: resource.uri,
7-
mimeType: resource.mime_type,
8-
text: resource.reader.call(resource)
9-
} ]
6+
contents: resources.map do |resource|
7+
{
8+
uri: request.uri,
9+
mimeType: resource.mime_type,
10+
text: resource.reader.call(resource)
11+
}
12+
end
1013
)
1114
else
1215
server.error(

lib/ruby_mcp/resources.rb

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
module RubyMCP
22
Resource = Data.define(:uri, :name, :description, :mime_type, :reader)
3+
ResourceTemplate = Data.define(:uri_template, :name, :description, :mime_type, :reader)
34

45
class Resources
56
def initialize
67
@resources = {}
8+
@resource_templates = {}
79
end
810

911
def add(uri:, name:, description: nil, mime_type: nil, reader: nil)
1012
@resources[uri] = Resource.new(uri, name, description, mime_type, reader)
1113
end
1214

1315
def find(uri)
14-
@resources[uri]
16+
[ @resources[uri] ].concat(find_in_templates(uri).map(&:last)).compact
17+
end
18+
19+
def add_resource_template(uri_template:, **args)
20+
@resource_templates[uri_template] = ResourceTemplate.new(
21+
uri_template: Addressable::Template.new(uri_template),
22+
**args
23+
)
1524
end
1625

1726
def as_json
@@ -24,5 +33,14 @@ def as_json
2433
}
2534
end
2635
end
36+
37+
private
38+
39+
def find_in_templates(uri)
40+
addressable_uri = Addressable::URI.parse(uri)
41+
@resource_templates.find_all do |template, resource_template|
42+
resource_template.uri_template.extract(addressable_uri)
43+
end
44+
end
2745
end
2846
end

lib/ruby_mcp/server.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module RubyMCP
22
class Server
33
include Capabilities::Logging
4+
include Capabilities::Resources
45

56
attr_reader :lifecycle, :prompts, :resources
67

ruby_mcp.gemspec

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@ Gem::Specification.new do |spec|
2828
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
2929
spec.require_paths = [ "lib" ]
3030

31-
spec.add_dependency "zeitwerk", '~> 2.7', '>= 2.7.2'
31+
spec.add_dependency "zeitwerk", "~> 2.7", ">= 2.7.2"
32+
spec.add_dependency "addressable", "~> 2.8", ">= 2.8.7"
3233
end

test/capabilities/test_resources_capability.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,41 @@ def test_read_resources_not_found
122122
}
123123
)
124124
end
125+
126+
def test_read_template
127+
@server.add_resource_template(
128+
uri_template: "https://{host}.de",
129+
name: "german_website",
130+
description: "Every german website",
131+
mime_type: "text/html",
132+
reader: ->(resource) {
133+
"The first demo content of #{resource.name}"
134+
}
135+
)
136+
137+
@transport.client_message(
138+
jsonrpc: "2.0",
139+
id: 1,
140+
method: "resources/read",
141+
params: {
142+
uri: "https://example.de"
143+
}
144+
)
145+
146+
@transport.process_message
147+
148+
assert_last_response(
149+
jsonrpc: "2.0",
150+
id: 1,
151+
result: {
152+
contents: [
153+
{
154+
uri: "https://example.de",
155+
mimeType: "text/html",
156+
text: "The first demo content of german_website"
157+
}
158+
]
159+
}
160+
)
161+
end
125162
end

0 commit comments

Comments
 (0)