Skip to content

Commit cb5d028

Browse files
committed
Phase 4: Implement Complete Primitives - Resources, Prompts, Roots, and Sampling
1 parent 515fc90 commit cb5d028

File tree

11 files changed

+1269
-1
lines changed

11 files changed

+1269
-1
lines changed

lib/ruby_mcp/client.rb

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
require_relative 'client/client'
44
require_relative 'client/retry'
55
require_relative 'client/streaming'
6+
require_relative 'client/sampling'
7+
require_relative 'client/roots'
68

79
module MCP
810
# Creates a new client instance with optional block configuration
@@ -15,6 +17,8 @@ def self.Client(options = {})
1517
# Add additional capabilities
1618
client.extend(Client::Retry)
1719
client.extend(Client::Streaming)
20+
client.extend(Client::Sampling)
21+
client.extend(Client::Roots)
1822

1923
yield client if block_given?
2024
client
@@ -29,6 +33,10 @@ class Client
2933
def initialize(options = {})
3034
@client = MCP.Client(options)
3135

36+
# Set up roots and sampling handlers if provided
37+
@client.set_roots(options[:roots]) if options[:roots]
38+
@client.set_sampling_handler(options[:sampling_handler]) if options[:sampling_handler]
39+
3240
yield self if block_given?
3341

3442
self
@@ -54,7 +62,7 @@ def connected?
5462
@client.connected?
5563
end
5664

57-
# Get the list of available tools
65+
# List available tools on the server
5866
# @return [Array<Hash>] The list of available tools
5967
def list_tools
6068
@client.list_tools
@@ -87,6 +95,74 @@ def with_retry(options = {}, retriable_errors = [StandardError], retry_condition
8795
@client.with_retry(options, retriable_errors, retry_condition, &block)
8896
end
8997

98+
# List available resources on the server
99+
# @return [Array<Hash>] The list of available resources
100+
def list_resources
101+
@client.list_resources
102+
end
103+
104+
# List available resource templates on the server
105+
# @return [Array<Hash>] The list of available resource templates
106+
def list_resource_templates
107+
@client.list_resource_templates
108+
end
109+
110+
# Read a resource from the server
111+
# @param uri [String] The URI of the resource to read
112+
# @param params [Hash] Optional parameters for template resources
113+
# @return [Array<Hash>] The resource content
114+
def read_resource(uri, params = nil)
115+
@client.read_resource(uri, params)
116+
end
117+
118+
# List available prompts on the server
119+
# @return [Array<Hash>] The list of available prompts
120+
def list_prompts
121+
@client.list_prompts
122+
end
123+
124+
# Get a prompt from the server
125+
# @param name [String] The name of the prompt to get
126+
# @param arguments [Hash] Optional arguments for the prompt
127+
# @return [Array<Hash>] The prompt messages
128+
def get_prompt(name, arguments = {})
129+
@client.get_prompt(name, arguments)
130+
end
131+
132+
# List available roots on the server
133+
# @return [Array<Hash>] The list of available roots
134+
def list_roots
135+
@client.list_roots
136+
end
137+
138+
# Read a file from a root on the server
139+
# @param root [String] The name of the root to read from
140+
# @param path [String] The path to read
141+
# @return [String] The file content
142+
def read_root_file(root, path)
143+
@client.read_root_file(root, path)
144+
end
145+
146+
# Allow models to call their host model for text generation
147+
# @param prompt [String, Array<Hash>] The prompt to generate from
148+
# @param options [Hash] Generation options
149+
# @return [String] The generated text
150+
def sample(prompt, options = {})
151+
@client.sample(prompt, options)
152+
end
153+
154+
# Set the roots list handler
155+
# @param roots [Array<Hash>] The roots to register
156+
def set_roots(roots)
157+
@client.set_roots(roots)
158+
end
159+
160+
# Set the sampling handler
161+
# @param handler [Proc] The handler function
162+
def set_sampling_handler(handler)
163+
@client.set_sampling_handler(handler)
164+
end
165+
90166
# Forward other methods to the client instance
91167
def method_missing(method, *args, &block)
92168
if @client.respond_to?(method)

lib/ruby_mcp/client/roots.rb

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# frozen_string_literal: true
2+
3+
module MCP
4+
module Client
5+
# Client-side roots functionality
6+
module Roots
7+
# Register the roots list handler with the server
8+
# @param roots [Array<Hash>] The roots to register
9+
def set_roots(roots)
10+
@roots = roots
11+
12+
# Register event handlers
13+
on_event(:roots_list_changed) do
14+
handle_roots_list_changed
15+
end
16+
17+
# Send the roots list to the server if already connected
18+
send_roots_list if connected?
19+
end
20+
21+
# Send the roots list to the server
22+
def send_roots_list
23+
ensure_connected
24+
25+
# Convert to MCP format
26+
roots_list = @roots.map do |root|
27+
{
28+
name: root[:name],
29+
description: root[:description] || "",
30+
allowWrites: root[:allow_writes] || false
31+
}
32+
end
33+
34+
# Send the roots list notification
35+
request = MCP::Protocol::JsonRPC.notification('roots/list', {
36+
roots: roots_list
37+
})
38+
39+
@connection.send_notification(request)
40+
end
41+
42+
private
43+
44+
# Handle roots/list_changed notification
45+
def handle_roots_list_changed
46+
# Send the updated roots list
47+
send_roots_list
48+
end
49+
end
50+
end
51+
end

lib/ruby_mcp/client/sampling.rb

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# frozen_string_literal: true
2+
3+
module MCP
4+
module Client
5+
# Sampling functionality for client-side LLM generation
6+
module Sampling
7+
# Allows models to call their host model for text generation
8+
# @param prompt [String, Array<Hash>] The prompt to generate from
9+
# @param options [Hash] Generation options
10+
# @return [String] The generated text
11+
def sample(prompt, options = {})
12+
if @sampling_handler
13+
# Call the registered sampling handler
14+
@sampling_handler.call(prompt, options)
15+
else
16+
raise MCP::Errors::ClientError, "No sampling handler registered"
17+
end
18+
end
19+
20+
# Register a sampling handler
21+
# @param handler [Proc] The handler function
22+
def set_sampling_handler(handler)
23+
@sampling_handler = handler
24+
end
25+
26+
# Add sampling capabilities to a client
27+
# @param client [MCP::Client::Client] The client to add sampling to
28+
def self.included(client)
29+
unless client.included_modules.include?(self)
30+
client.include(self)
31+
end
32+
end
33+
end
34+
35+
# Default sampling handler implementation
36+
class SamplingHandler
37+
attr_reader :model
38+
39+
# Initialize a sampling handler
40+
# @param model [String] The model to use for sampling
41+
# @param options [Hash] Default options for the model
42+
def initialize(model, options = {})
43+
@model = model
44+
@default_options = options
45+
end
46+
47+
# Generate text from a prompt
48+
# @param prompt [String, Array<Hash>] The prompt to generate from
49+
# @param options [Hash] Generation options
50+
# @return [String] The generated text
51+
def call(prompt, options = {})
52+
# This is a placeholder for actual model integration
53+
# In a real implementation, you'd call your model API here
54+
raise NotImplementedError, "SamplingHandler#call must be implemented"
55+
end
56+
end
57+
end
58+
end

lib/ruby_mcp/server.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
require_relative 'server/tools/tool'
44
require_relative 'server/tools/manager'
5+
require_relative 'server/resources/resource'
6+
require_relative 'server/resources/manager'
7+
require_relative 'server/prompts/prompt'
8+
require_relative 'server/prompts/manager'
9+
require_relative 'server/roots/root'
10+
require_relative 'server/roots/manager'
511
require_relative 'server/dsl'
612
require_relative 'server/server'
713

0 commit comments

Comments
 (0)