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
This commit is contained in:
parent
92a08bafc7
commit
e88deb6b65
3 changed files with 161 additions and 0 deletions
|
|
@ -830,6 +830,8 @@ This class supports:
|
||||||
- Tool invocation via the `tools/call` method (`MCP::Client#call_tools`)
|
- Tool invocation via the `tools/call` method (`MCP::Client#call_tools`)
|
||||||
- Resource listing via the `resources/list` method (`MCP::Client#resources`)
|
- Resource listing via the `resources/list` method (`MCP::Client#resources`)
|
||||||
- Resource reading via the `resources/read` method (`MCP::Client#read_resources`)
|
- Resource reading via the `resources/read` method (`MCP::Client#read_resources`)
|
||||||
|
- Prompt listing via the `prompts/list` method (`MCP::Client#prompts`)
|
||||||
|
- Prompt retrieval via the `prompts/get` method (`MCP::Client#get_prompt`)
|
||||||
- Automatic JSON-RPC 2.0 message formatting
|
- Automatic JSON-RPC 2.0 message formatting
|
||||||
- UUID request ID generation
|
- UUID request ID generation
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,20 @@ module MCP
|
||||||
response.dig("result", "resources") || []
|
response.dig("result", "resources") || []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns the list of prompts available from the server.
|
||||||
|
# Each call will make a new request – the result is not cached.
|
||||||
|
#
|
||||||
|
# @return [Array<Hash>] An array of available prompts.
|
||||||
|
def prompts
|
||||||
|
response = transport.send_request(request: {
|
||||||
|
jsonrpc: JsonRpcHandler::Version::V2_0,
|
||||||
|
id: request_id,
|
||||||
|
method: "prompts/list",
|
||||||
|
})
|
||||||
|
|
||||||
|
response.dig("result", "prompts") || []
|
||||||
|
end
|
||||||
|
|
||||||
# Calls a tool via the transport layer and returns the full response from the server.
|
# Calls a tool via the transport layer and returns the full response from the server.
|
||||||
#
|
#
|
||||||
# @param tool [MCP::Client::Tool] The tool to be called.
|
# @param tool [MCP::Client::Tool] The tool to be called.
|
||||||
|
|
@ -96,6 +110,21 @@ module MCP
|
||||||
response.dig("result", "contents") || []
|
response.dig("result", "contents") || []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Gets a prompt from the server by name and returns its details.
|
||||||
|
#
|
||||||
|
# @param name [String] The name of the prompt to get.
|
||||||
|
# @return [Hash] A hash containing the prompt details.
|
||||||
|
def get_prompt(name:)
|
||||||
|
response = transport.send_request(request: {
|
||||||
|
jsonrpc: JsonRpcHandler::Version::V2_0,
|
||||||
|
id: request_id,
|
||||||
|
method: "prompts/get",
|
||||||
|
params: { name: name },
|
||||||
|
})
|
||||||
|
|
||||||
|
response.fetch("result", {})
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def request_id
|
def request_id
|
||||||
|
|
|
||||||
|
|
@ -141,5 +141,135 @@ module MCP
|
||||||
|
|
||||||
assert_empty(contents)
|
assert_empty(contents)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_prompts_sends_request_to_transport_and_returns_prompts_array
|
||||||
|
transport = mock
|
||||||
|
mock_response = {
|
||||||
|
"result" => {
|
||||||
|
"prompts" => [
|
||||||
|
{
|
||||||
|
"name" => "prompt_1",
|
||||||
|
"description" => "First prompt",
|
||||||
|
"arguments" => [
|
||||||
|
{
|
||||||
|
"name" => "code_1",
|
||||||
|
"description" => "The code_1 to review",
|
||||||
|
"required" => true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name" => "prompt_2",
|
||||||
|
"description" => "Second prompt",
|
||||||
|
"arguments" => [
|
||||||
|
{
|
||||||
|
"name" => "code_2",
|
||||||
|
"description" => "The code_2 to review",
|
||||||
|
"required" => true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Only checking for the essential parts of the request
|
||||||
|
transport.expects(:send_request).with do |args|
|
||||||
|
args in { request: { method: "prompts/list", jsonrpc: "2.0" } }
|
||||||
|
end.returns(mock_response).once
|
||||||
|
|
||||||
|
client = Client.new(transport: transport)
|
||||||
|
prompts = client.prompts
|
||||||
|
|
||||||
|
assert_equal(2, prompts.size)
|
||||||
|
assert_equal("prompt_1", prompts.first["name"])
|
||||||
|
assert_equal("First prompt", prompts.first["description"])
|
||||||
|
assert_equal("code_1", prompts.first["arguments"].first["name"])
|
||||||
|
assert_equal("The code_1 to review", prompts.first["arguments"].first["description"])
|
||||||
|
assert(prompts.first["arguments"].first["required"])
|
||||||
|
|
||||||
|
assert_equal("prompt_2", prompts.last["name"])
|
||||||
|
assert_equal("Second prompt", prompts.last["description"])
|
||||||
|
assert_equal("code_2", prompts.last["arguments"].first["name"])
|
||||||
|
assert_equal("The code_2 to review", prompts.last["arguments"].first["description"])
|
||||||
|
assert(prompts.last["arguments"].first["required"])
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_prompts_returns_empty_array_when_no_prompts
|
||||||
|
transport = mock
|
||||||
|
mock_response = { "result" => { "prompts" => [] } }
|
||||||
|
|
||||||
|
transport.expects(:send_request).returns(mock_response).once
|
||||||
|
|
||||||
|
client = Client.new(transport: transport)
|
||||||
|
prompts = client.prompts
|
||||||
|
|
||||||
|
assert_empty(prompts)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_get_prompt_sends_request_to_transport_and_returns_contents
|
||||||
|
transport = mock
|
||||||
|
name = "first_prompt"
|
||||||
|
mock_response = {
|
||||||
|
"result" => {
|
||||||
|
"description" => "First prompt",
|
||||||
|
"messages" => [
|
||||||
|
{
|
||||||
|
"role" => "user",
|
||||||
|
"content" => {
|
||||||
|
"text" => "First prompt content",
|
||||||
|
"type" => "text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Only checking for the essential parts of the request
|
||||||
|
transport.expects(:send_request).with do |args|
|
||||||
|
args in {
|
||||||
|
request: {
|
||||||
|
method: "prompts/get",
|
||||||
|
jsonrpc: "2.0",
|
||||||
|
params: {
|
||||||
|
name: name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
end.returns(mock_response).once
|
||||||
|
|
||||||
|
client = Client.new(transport: transport)
|
||||||
|
contents = client.get_prompt(name: name)
|
||||||
|
|
||||||
|
assert_equal("First prompt", contents["description"])
|
||||||
|
assert_equal("user", contents["messages"].first["role"])
|
||||||
|
assert_equal("First prompt content", contents["messages"].first["content"]["text"])
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_get_prompt_returns_empty_hash_when_no_contents
|
||||||
|
transport = mock
|
||||||
|
name = "nonexistent_prompt"
|
||||||
|
mock_response = { "result" => {} }
|
||||||
|
|
||||||
|
transport.expects(:send_request).returns(mock_response).once
|
||||||
|
|
||||||
|
client = Client.new(transport: transport)
|
||||||
|
contents = client.get_prompt(name: name)
|
||||||
|
|
||||||
|
assert_empty(contents)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_get_prompt_returns_empty_hash
|
||||||
|
transport = mock
|
||||||
|
name = "nonexistent_prompt"
|
||||||
|
mock_response = {}
|
||||||
|
|
||||||
|
transport.expects(:send_request).returns(mock_response).once
|
||||||
|
|
||||||
|
client = Client.new(transport: transport)
|
||||||
|
contents = client.get_prompt(name: name)
|
||||||
|
|
||||||
|
assert_empty(contents)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue