Merge pull request #153 from modelcontextprotocol/prompt-spec-compliance
fix `Prompt` type gaps with 2025-06-18 spec
This commit is contained in:
commit
f312695798
4 changed files with 86 additions and 10 deletions
12
README.md
12
README.md
|
|
@ -623,10 +623,12 @@ class MyPrompt < MCP::Prompt
|
||||||
arguments [
|
arguments [
|
||||||
MCP::Prompt::Argument.new(
|
MCP::Prompt::Argument.new(
|
||||||
name: "message",
|
name: "message",
|
||||||
|
title: "Message Title",
|
||||||
description: "Input message",
|
description: "Input message",
|
||||||
required: true
|
required: true
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
meta({ version: "1.0", category: "example" })
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def template(args, server_context:)
|
def template(args, server_context:)
|
||||||
|
|
@ -660,10 +662,12 @@ prompt = MCP::Prompt.define(
|
||||||
arguments: [
|
arguments: [
|
||||||
MCP::Prompt::Argument.new(
|
MCP::Prompt::Argument.new(
|
||||||
name: "message",
|
name: "message",
|
||||||
|
title: "Message Title",
|
||||||
description: "Input message",
|
description: "Input message",
|
||||||
required: true
|
required: true
|
||||||
)
|
)
|
||||||
]
|
],
|
||||||
|
meta: { version: "1.0", category: "example" }
|
||||||
) do |args, server_context:|
|
) do |args, server_context:|
|
||||||
MCP::Prompt::Result.new(
|
MCP::Prompt::Result.new(
|
||||||
description: "Response description",
|
description: "Response description",
|
||||||
|
|
@ -691,10 +695,12 @@ server.define_prompt(
|
||||||
arguments: [
|
arguments: [
|
||||||
Prompt::Argument.new(
|
Prompt::Argument.new(
|
||||||
name: "message",
|
name: "message",
|
||||||
|
title: "Message Title",
|
||||||
description: "Input message",
|
description: "Input message",
|
||||||
required: true
|
required: true
|
||||||
)
|
)
|
||||||
]
|
],
|
||||||
|
meta: { version: "1.0", category: "example" }
|
||||||
) do |args, server_context:|
|
) do |args, server_context:|
|
||||||
Prompt::Result.new(
|
Prompt::Result.new(
|
||||||
description: "Response description",
|
description: "Response description",
|
||||||
|
|
@ -717,7 +723,7 @@ e.g. around authentication state or user preferences.
|
||||||
|
|
||||||
### Key Components
|
### Key Components
|
||||||
|
|
||||||
- `MCP::Prompt::Argument` - Defines input parameters for the prompt template
|
- `MCP::Prompt::Argument` - Defines input parameters for the prompt template with name, title, description, and required flag
|
||||||
- `MCP::Prompt::Message` - Represents a message in the conversation with a role and content
|
- `MCP::Prompt::Message` - Represents a message in the conversation with a role and content
|
||||||
- `MCP::Prompt::Result` - The output of a prompt template containing description and messages
|
- `MCP::Prompt::Result` - The output of a prompt template containing description and messages
|
||||||
- `MCP::Content::Text` - Text content for messages
|
- `MCP::Content::Text` - Text content for messages
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,20 @@ module MCP
|
||||||
attr_reader :title_value
|
attr_reader :title_value
|
||||||
attr_reader :description_value
|
attr_reader :description_value
|
||||||
attr_reader :arguments_value
|
attr_reader :arguments_value
|
||||||
|
attr_reader :meta_value
|
||||||
|
|
||||||
def template(args, server_context: nil)
|
def template(args, server_context: nil)
|
||||||
raise NotImplementedError, "Subclasses must implement template"
|
raise NotImplementedError, "Subclasses must implement template"
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_h
|
def to_h
|
||||||
{ name: name_value, title: title_value, description: description_value, arguments: arguments_value.map(&:to_h) }.compact
|
{
|
||||||
|
name: name_value,
|
||||||
|
title: title_value,
|
||||||
|
description: description_value,
|
||||||
|
arguments: arguments_value&.map(&:to_h),
|
||||||
|
_meta: meta_value,
|
||||||
|
}.compact
|
||||||
end
|
end
|
||||||
|
|
||||||
def inherited(subclass)
|
def inherited(subclass)
|
||||||
|
|
@ -23,6 +30,7 @@ module MCP
|
||||||
subclass.instance_variable_set(:@title_value, nil)
|
subclass.instance_variable_set(:@title_value, nil)
|
||||||
subclass.instance_variable_set(:@description_value, nil)
|
subclass.instance_variable_set(:@description_value, nil)
|
||||||
subclass.instance_variable_set(:@arguments_value, nil)
|
subclass.instance_variable_set(:@arguments_value, nil)
|
||||||
|
subclass.instance_variable_set(:@meta_value, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
def prompt_name(value = NOT_SET)
|
def prompt_name(value = NOT_SET)
|
||||||
|
|
@ -61,7 +69,15 @@ module MCP
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def define(name: nil, title: nil, description: nil, arguments: [], &block)
|
def meta(value = NOT_SET)
|
||||||
|
if value == NOT_SET
|
||||||
|
@meta_value
|
||||||
|
else
|
||||||
|
@meta_value = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def define(name: nil, title: nil, description: nil, arguments: [], meta: nil, &block)
|
||||||
Class.new(self) do
|
Class.new(self) do
|
||||||
prompt_name name
|
prompt_name name
|
||||||
title title
|
title title
|
||||||
|
|
@ -70,6 +86,7 @@ module MCP
|
||||||
define_singleton_method(:template) do |args, server_context: nil|
|
define_singleton_method(:template) do |args, server_context: nil|
|
||||||
instance_exec(args, server_context:, &block)
|
instance_exec(args, server_context:, &block)
|
||||||
end
|
end
|
||||||
|
meta meta
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,22 @@
|
||||||
module MCP
|
module MCP
|
||||||
class Prompt
|
class Prompt
|
||||||
class Argument
|
class Argument
|
||||||
attr_reader :name, :description, :required, :arguments
|
attr_reader :name, :title, :description, :required
|
||||||
|
|
||||||
def initialize(name:, description: nil, required: false)
|
def initialize(name:, title: nil, description: nil, required: false)
|
||||||
@name = name
|
@name = name
|
||||||
|
@title = title
|
||||||
@description = description
|
@description = description
|
||||||
@required = required
|
@required = required
|
||||||
@arguments = arguments
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_h
|
def to_h
|
||||||
{ name:, description:, required: }.compact
|
{
|
||||||
|
name: name,
|
||||||
|
title: title,
|
||||||
|
description: description,
|
||||||
|
required: required,
|
||||||
|
}.compact
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -113,8 +113,14 @@ module MCP
|
||||||
title: "Mock Prompt",
|
title: "Mock Prompt",
|
||||||
description: "a mock prompt for testing",
|
description: "a mock prompt for testing",
|
||||||
arguments: [
|
arguments: [
|
||||||
Prompt::Argument.new(name: "test_argument", description: "Test argument", required: true),
|
Prompt::Argument.new(
|
||||||
|
name: "test_argument",
|
||||||
|
title: "Test argument title",
|
||||||
|
description: "This is a test argument description",
|
||||||
|
required: true,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
|
meta: { foo: "bar" },
|
||||||
) do |args, server_context:|
|
) do |args, server_context:|
|
||||||
content = Content::Text.new(args["test_argument"] + " user: #{server_context[:user_id]}")
|
content = Content::Text.new(args["test_argument"] + " user: #{server_context[:user_id]}")
|
||||||
|
|
||||||
|
|
@ -130,6 +136,10 @@ module MCP
|
||||||
assert_equal "mock_prompt", prompt.name_value
|
assert_equal "mock_prompt", prompt.name_value
|
||||||
assert_equal "a mock prompt for testing", prompt.description
|
assert_equal "a mock prompt for testing", prompt.description
|
||||||
assert_equal "test_argument", prompt.arguments.first.name
|
assert_equal "test_argument", prompt.arguments.first.name
|
||||||
|
assert_equal "Test argument title", prompt.arguments.first.title
|
||||||
|
assert_equal "This is a test argument description", prompt.arguments.first.description
|
||||||
|
assert prompt.arguments.first.required
|
||||||
|
assert_equal({ foo: "bar" }, prompt.meta_value)
|
||||||
|
|
||||||
expected = {
|
expected = {
|
||||||
description: "Hello, world!",
|
description: "Hello, world!",
|
||||||
|
|
@ -142,5 +152,43 @@ module MCP
|
||||||
result = prompt.template({ "test_argument" => "Hello, friend!" }, server_context: { user_id: 123 })
|
result = prompt.template({ "test_argument" => "Hello, friend!" }, server_context: { user_id: 123 })
|
||||||
assert_equal expected, result.to_h
|
assert_equal expected, result.to_h
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "#to_h returns a hash with name, title, description, arguments, and meta" do
|
||||||
|
class FullPrompt < Prompt
|
||||||
|
prompt_name "test_prompt"
|
||||||
|
description "Test prompt description"
|
||||||
|
title "Test Prompt title"
|
||||||
|
arguments [
|
||||||
|
Prompt::Argument.new(name: "test_argument", description: "Test argument", required: true),
|
||||||
|
]
|
||||||
|
meta({ test: "meta" })
|
||||||
|
end
|
||||||
|
|
||||||
|
expected = {
|
||||||
|
name: "test_prompt",
|
||||||
|
title: "Test Prompt title",
|
||||||
|
description: "Test prompt description",
|
||||||
|
arguments: [
|
||||||
|
{ name: "test_argument", description: "Test argument", required: true },
|
||||||
|
],
|
||||||
|
_meta: { test: "meta" },
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_equal expected, FullPrompt.to_h
|
||||||
|
end
|
||||||
|
|
||||||
|
test "#to_h handles nil arguments value" do
|
||||||
|
class NoArgumentsPrompt < Prompt
|
||||||
|
description "No arguments prompt"
|
||||||
|
end
|
||||||
|
prompt = NoArgumentsPrompt
|
||||||
|
|
||||||
|
expected = {
|
||||||
|
name: "no_arguments_prompt",
|
||||||
|
description: "No arguments prompt",
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_equal expected, prompt.to_h
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue