Skip to content

Commit 6eede35

Browse files
committed
Add prompts/list and prompts/get support to client
This commit adds two new methods to `MCP::Client`: - prompts: Lists available prompts from the server via `prompts/list` - get_prompt: Retrieves a specific prompt by name via `prompts/get` Both methods follow the same pattern as the existing resources support, including comprehensive test coverage and README documentation updates. ref. https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/server/prompts.mdx#protocol-messages
1 parent 92a08ba commit 6eede35

File tree

3 files changed

+160
-0
lines changed

3 files changed

+160
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,8 @@ This class supports:
830830
- Tool invocation via the `tools/call` method (`MCP::Client#call_tools`)
831831
- Resource listing via the `resources/list` method (`MCP::Client#resources`)
832832
- Resource reading via the `resources/read` method (`MCP::Client#read_resources`)
833+
- Prompt listing via the `prompts/list` method (`MCP::Client#prompts`)
834+
- Prompt retrieval via the `prompts/get` method (`MCP::Client#get_prompt`)
833835
- Automatic JSON-RPC 2.0 message formatting
834836
- UUID request ID generation
835837

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.dig("result") || {}
126+
end
127+
99128
private
100129

101130
def request_id

test/mcp/client_test.rb

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,5 +141,134 @@ def test_read_resource_returns_empty_array_when_no_contents
141141

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

0 commit comments

Comments
 (0)