Skip to content

Commit ad87c8d

Browse files
authored
Merge pull request #163 from dak2/add-client-prompts-support
Add `prompts/list` and `prompts/get` support to client
2 parents b36aea0 + e88deb6 commit ad87c8d

File tree

3 files changed

+161
-0
lines changed

3 files changed

+161
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,8 @@ This class supports:
836836
- Tool invocation via the `tools/call` method (`MCP::Client#call_tools`)
837837
- Resource listing via the `resources/list` method (`MCP::Client#resources`)
838838
- Resource reading via the `resources/read` method (`MCP::Client#read_resources`)
839+
- Prompt listing via the `prompts/list` method (`MCP::Client#prompts`)
840+
- Prompt retrieval via the `prompts/get` method (`MCP::Client#get_prompt`)
839841
- Automatic JSON-RPC 2.0 message formatting
840842
- UUID request ID generation
841843

lib/mcp/client.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,20 @@ def resources
5858
response.dig("result", "resources") || []
5959
end
6060

61+
# Returns the list of prompts available from the server.
62+
# Each call will make a new request – the result is not cached.
63+
#
64+
# @return [Array<Hash>] An array of available prompts.
65+
def prompts
66+
response = transport.send_request(request: {
67+
jsonrpc: JsonRpcHandler::Version::V2_0,
68+
id: request_id,
69+
method: "prompts/list",
70+
})
71+
72+
response.dig("result", "prompts") || []
73+
end
74+
6175
# Calls a tool via the transport layer and returns the full response from the server.
6276
#
6377
# @param tool [MCP::Client::Tool] The tool to be called.
@@ -96,6 +110,21 @@ def read_resource(uri:)
96110
response.dig("result", "contents") || []
97111
end
98112

113+
# Gets a prompt from the server by name and returns its details.
114+
#
115+
# @param name [String] The name of the prompt to get.
116+
# @return [Hash] A hash containing the prompt details.
117+
def get_prompt(name:)
118+
response = transport.send_request(request: {
119+
jsonrpc: JsonRpcHandler::Version::V2_0,
120+
id: request_id,
121+
method: "prompts/get",
122+
params: { name: name },
123+
})
124+
125+
response.fetch("result", {})
126+
end
127+
99128
private
100129

101130
def request_id

test/mcp/client_test.rb

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,5 +156,135 @@ def test_read_resource_returns_empty_array_when_no_contents
156156

157157
assert_empty(contents)
158158
end
159+
160+
def test_prompts_sends_request_to_transport_and_returns_prompts_array
161+
transport = mock
162+
mock_response = {
163+
"result" => {
164+
"prompts" => [
165+
{
166+
"name" => "prompt_1",
167+
"description" => "First prompt",
168+
"arguments" => [
169+
{
170+
"name" => "code_1",
171+
"description" => "The code_1 to review",
172+
"required" => true,
173+
},
174+
],
175+
},
176+
{
177+
"name" => "prompt_2",
178+
"description" => "Second prompt",
179+
"arguments" => [
180+
{
181+
"name" => "code_2",
182+
"description" => "The code_2 to review",
183+
"required" => true,
184+
},
185+
],
186+
},
187+
],
188+
},
189+
}
190+
191+
# Only checking for the essential parts of the request
192+
transport.expects(:send_request).with do |args|
193+
args in { request: { method: "prompts/list", jsonrpc: "2.0" } }
194+
end.returns(mock_response).once
195+
196+
client = Client.new(transport: transport)
197+
prompts = client.prompts
198+
199+
assert_equal(2, prompts.size)
200+
assert_equal("prompt_1", prompts.first["name"])
201+
assert_equal("First prompt", prompts.first["description"])
202+
assert_equal("code_1", prompts.first["arguments"].first["name"])
203+
assert_equal("The code_1 to review", prompts.first["arguments"].first["description"])
204+
assert(prompts.first["arguments"].first["required"])
205+
206+
assert_equal("prompt_2", prompts.last["name"])
207+
assert_equal("Second prompt", prompts.last["description"])
208+
assert_equal("code_2", prompts.last["arguments"].first["name"])
209+
assert_equal("The code_2 to review", prompts.last["arguments"].first["description"])
210+
assert(prompts.last["arguments"].first["required"])
211+
end
212+
213+
def test_prompts_returns_empty_array_when_no_prompts
214+
transport = mock
215+
mock_response = { "result" => { "prompts" => [] } }
216+
217+
transport.expects(:send_request).returns(mock_response).once
218+
219+
client = Client.new(transport: transport)
220+
prompts = client.prompts
221+
222+
assert_empty(prompts)
223+
end
224+
225+
def test_get_prompt_sends_request_to_transport_and_returns_contents
226+
transport = mock
227+
name = "first_prompt"
228+
mock_response = {
229+
"result" => {
230+
"description" => "First prompt",
231+
"messages" => [
232+
{
233+
"role" => "user",
234+
"content" => {
235+
"text" => "First prompt content",
236+
"type" => "text",
237+
},
238+
},
239+
],
240+
},
241+
}
242+
243+
# Only checking for the essential parts of the request
244+
transport.expects(:send_request).with do |args|
245+
args in {
246+
request: {
247+
method: "prompts/get",
248+
jsonrpc: "2.0",
249+
params: {
250+
name: name,
251+
},
252+
},
253+
}
254+
end.returns(mock_response).once
255+
256+
client = Client.new(transport: transport)
257+
contents = client.get_prompt(name: name)
258+
259+
assert_equal("First prompt", contents["description"])
260+
assert_equal("user", contents["messages"].first["role"])
261+
assert_equal("First prompt content", contents["messages"].first["content"]["text"])
262+
end
263+
264+
def test_get_prompt_returns_empty_hash_when_no_contents
265+
transport = mock
266+
name = "nonexistent_prompt"
267+
mock_response = { "result" => {} }
268+
269+
transport.expects(:send_request).returns(mock_response).once
270+
271+
client = Client.new(transport: transport)
272+
contents = client.get_prompt(name: name)
273+
274+
assert_empty(contents)
275+
end
276+
277+
def test_get_prompt_returns_empty_hash
278+
transport = mock
279+
name = "nonexistent_prompt"
280+
mock_response = {}
281+
282+
transport.expects(:send_request).returns(mock_response).once
283+
284+
client = Client.new(transport: transport)
285+
contents = client.get_prompt(name: name)
286+
287+
assert_empty(contents)
288+
end
159289
end
160290
end

0 commit comments

Comments
 (0)