diff --git a/README.md b/README.md index ba66f1c..ce50c9d 100644 --- a/README.md +++ b/README.md @@ -624,10 +624,12 @@ class MyPrompt < MCP::Prompt arguments [ MCP::Prompt::Argument.new( name: "message", + title: "Message Title", description: "Input message", required: true ) ] + meta({ version: "1.0", category: "example" }) class << self def template(args, server_context:) @@ -661,10 +663,12 @@ prompt = MCP::Prompt.define( arguments: [ MCP::Prompt::Argument.new( name: "message", + title: "Message Title", description: "Input message", required: true ) - ] + ], + meta: { version: "1.0", category: "example" } ) do |args, server_context:| MCP::Prompt::Result.new( description: "Response description", @@ -692,10 +696,12 @@ server.define_prompt( arguments: [ Prompt::Argument.new( name: "message", + title: "Message Title", description: "Input message", required: true ) - ] + ], + meta: { version: "1.0", category: "example" } ) do |args, server_context:| Prompt::Result.new( description: "Response description", @@ -718,7 +724,7 @@ e.g. around authentication state or user preferences. ### 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::Result` - The output of a prompt template containing description and messages - `MCP::Content::Text` - Text content for messages diff --git a/lib/mcp/prompt.rb b/lib/mcp/prompt.rb index 8f69925..a414bdc 100644 --- a/lib/mcp/prompt.rb +++ b/lib/mcp/prompt.rb @@ -8,13 +8,20 @@ class << self attr_reader :title_value attr_reader :description_value attr_reader :arguments_value + attr_reader :meta_value def template(args, server_context: nil) raise NotImplementedError, "Subclasses must implement template" end 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 def inherited(subclass) @@ -23,6 +30,7 @@ def inherited(subclass) subclass.instance_variable_set(:@title_value, nil) subclass.instance_variable_set(:@description_value, nil) subclass.instance_variable_set(:@arguments_value, nil) + subclass.instance_variable_set(:@meta_value, nil) end def prompt_name(value = NOT_SET) @@ -61,7 +69,15 @@ def arguments(value = NOT_SET) 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 prompt_name name title title @@ -70,6 +86,7 @@ def define(name: nil, title: nil, description: nil, arguments: [], &block) define_singleton_method(:template) do |args, server_context: nil| instance_exec(args, server_context:, &block) end + meta meta end end diff --git a/lib/mcp/prompt/argument.rb b/lib/mcp/prompt/argument.rb index 8ec0cb0..336e2b1 100644 --- a/lib/mcp/prompt/argument.rb +++ b/lib/mcp/prompt/argument.rb @@ -3,17 +3,22 @@ module MCP class Prompt 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 + @title = title @description = description @required = required - @arguments = arguments end def to_h - { name:, description:, required: }.compact + { + name: name, + title: title, + description: description, + required: required, + }.compact end end end diff --git a/test/mcp/prompt_test.rb b/test/mcp/prompt_test.rb index 878fd06..41f3857 100644 --- a/test/mcp/prompt_test.rb +++ b/test/mcp/prompt_test.rb @@ -113,8 +113,14 @@ def template(args, server_context:) title: "Mock Prompt", description: "a mock prompt for testing", 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:| content = Content::Text.new(args["test_argument"] + " user: #{server_context[:user_id]}") @@ -130,6 +136,10 @@ def template(args, server_context:) assert_equal "mock_prompt", prompt.name_value assert_equal "a mock prompt for testing", prompt.description 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 = { description: "Hello, world!", @@ -142,5 +152,43 @@ def template(args, server_context:) result = prompt.template({ "test_argument" => "Hello, friend!" }, server_context: { user_id: 123 }) assert_equal expected, result.to_h 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