diff --git a/Gemfile.lock b/Gemfile.lock index 3a99330..6391669 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - model_context_protocol (0.5.2) + model_context_protocol (0.6.0) json_rpc_handler (~> 0.1) GEM diff --git a/README.md b/README.md index a452fde..285b60b 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ module ModelContextProtocol name: "my_server", tools: [SomeTool, AnotherTool], prompts: [MyPrompt], - context: { user_id: current_user.id }, + server_context: { user_id: current_user.id }, ) render(json: server.handle_json(request.body.read).to_h) end @@ -69,7 +69,7 @@ module ModelContextProtocol name: "my_server", tools: [SomeTool, AnotherTool], prompts: [MyPrompt], - context: { user_id: current_user.id }, + server_context: { user_id: current_user.id }, ) render(json: server.handle(JSON.parse(request.body.read)).to_h) end @@ -96,7 +96,7 @@ class ExampleTool < ModelContextProtocol::Tool required: ["message"] class << self - def call(message:, context:) + def call(message:, server_context:) ModelContextProtocol::Tool::Response.new([{ type: "text", text: "Hello from example tool! Message: #{message}", @@ -137,7 +137,7 @@ server = ModelContextProtocol::Server.new( name: "my_server", tools: [SomeTool, AnotherTool], prompts: [MyPrompt], - context: { user_id: current_user.id }, + server_context: { user_id: current_user.id }, ) request = { jsonrpc: "2.0", @@ -154,11 +154,11 @@ The gem can be configured using the `ModelContextProtocol.configure` block: ```ruby ModelContextProtocol.configure do |config| - config.exception_reporter = ->(exception, context) do + config.exception_reporter = ->(exception, server_context) do # Your exception reporting logic here # For example with Bugsnag: Bugsnag.notify(exception) do |report| - report.add_metadata(:model_context_protocol, context) + report.add_metadata(:model_context_protocol, server_context) end end @@ -170,11 +170,11 @@ This is useful for systems where an application hosts more than one MCP server b they might require different instrumentation callbacks. configuration = ModelContextProtocol::Configuration.new -configuration.exception_reporter = ->(exception, context) do +configuration.exception_reporter = ->(exception, server_context) do # Your exception reporting logic here # For example with Bugsnag: Bugsnag.notify(exception) do |report| - report.add_metadata(:model_context_protocol, context) + report.add_metadata(:model_context_protocol, server_context) end end @@ -207,9 +207,9 @@ Be sure to check the [MCP spec](https://spec.modelcontextprotocol.io/specificati The exception reporter receives two arguments: - `exception`: The Ruby exception object that was raised -- `context`: A hash containing contextual information about where the error occurred +- `server_context`: A hash containing contextual information about where the error occurred -The context hash includes: +The server_context hash includes: - For tool calls: `{ tool_name: "name", arguments: { ... } }` - For general request handling: `{ request: { ... } }` @@ -248,7 +248,7 @@ class MyTool < ModelContextProtocol::Tool }, required: ['message'] - def self.call(message:, context:) + def self.call(message:, server_context:) Tool::Response.new([{ type: "text", text: "OK" }]) end end @@ -266,12 +266,12 @@ tool = ModelContextProtocol::Tool.define( title: "My Tool", read_only_hint: true } -) do |args, context| +) do |args, server_context| Tool::Response.new([{ type: "text", text: "OK" }]) end ``` -The context parameter is the context passed into the server and can be used to pass per request information, +The server_context parameter is the server_context passed into the server and can be used to pass per request information, e.g. around authentication state. ### Tool Annotations @@ -307,7 +307,7 @@ class MyPrompt < ModelContextProtocol::Prompt ] class << self - def template(args, context:) + def template(args, server_context:) Prompt::Result.new( description: "Response description", messages: [ @@ -341,7 +341,7 @@ prompt = ModelContextProtocol::Prompt.define( required: true ) ] -) do |args, context:| +) do |args, server_context:| Prompt::Result.new( description: "Response description", messages: [ @@ -358,7 +358,7 @@ prompt = ModelContextProtocol::Prompt.define( end ``` -The context parameter is the context passed into the server and can be used to pass per request information, +The server_context parameter is the server_context passed into the server and can be used to pass per request information, e.g. around authentication state or user preferences. ### Key Components @@ -376,7 +376,7 @@ Register prompts with the MCP server: server = ModelContextProtocol::Server.new( name: "my_server", prompts: [MyPrompt], - context: { user_id: current_user.id }, + server_context: { user_id: current_user.id }, ) ``` diff --git a/examples/stdio_server.rb b/examples/stdio_server.rb index 5e2934b..e71b1a7 100755 --- a/examples/stdio_server.rb +++ b/examples/stdio_server.rb @@ -15,7 +15,7 @@ class ExampleTool < ModelContextProtocol::Tool required: ["message"] class << self - def call(message:, context:) + def call(message:, server_context:) ModelContextProtocol::Tool::Response.new([{ type: "text", text: "Hello from example tool! Message: #{message}", diff --git a/lib/model_context_protocol/configuration.rb b/lib/model_context_protocol/configuration.rb index 36222d1..97fe532 100644 --- a/lib/model_context_protocol/configuration.rb +++ b/lib/model_context_protocol/configuration.rb @@ -65,7 +65,7 @@ module ModelContextProtocol private def default_exception_reporter - @default_exception_reporter ||= ->(exception, context) {} + @default_exception_reporter ||= ->(exception, server_context) {} end def default_instrumentation_callback diff --git a/lib/model_context_protocol/prompt.rb b/lib/model_context_protocol/prompt.rb index 8e40280..6aa923f 100644 --- a/lib/model_context_protocol/prompt.rb +++ b/lib/model_context_protocol/prompt.rb @@ -50,7 +50,7 @@ module ModelContextProtocol attr_reader :description_value attr_reader :arguments_value - def template(args, context:) + def template(args, server_context:) raise NotImplementedError, "Subclasses must implement template" end @@ -98,8 +98,8 @@ module ModelContextProtocol prompt_name name description description arguments arguments - define_singleton_method(:template) do |args, context:| - instance_exec(args, context:, &block) + define_singleton_method(:template) do |args, server_context:| + instance_exec(args, server_context:, &block) end end end diff --git a/lib/model_context_protocol/server.rb b/lib/model_context_protocol/server.rb index 4df7372..bd47b3c 100644 --- a/lib/model_context_protocol/server.rb +++ b/lib/model_context_protocol/server.rb @@ -20,16 +20,16 @@ module ModelContextProtocol include Instrumentation - attr_accessor :name, :tools, :prompts, :resources, :context, :configuration + attr_accessor :name, :tools, :prompts, :resources, :server_context, :configuration - def initialize(name: "model_context_protocol", tools: [], prompts: [], resources: [], context: nil, + def initialize(name: "model_context_protocol", tools: [], prompts: [], resources: [], server_context: nil, configuration: nil) @name = name @tools = tools.to_h { |t| [t.name_value, t] } @prompts = prompts.to_h { |p| [p.name_value, p] } @resources = resources @resource_index = index_resources_by_uri(resources) - @context = context + @server_context = server_context @configuration = ModelContextProtocol.configuration.merge(configuration) @handlers = { Methods::RESOURCES_LIST => method(:list_resources), @@ -154,7 +154,7 @@ module ModelContextProtocol add_instrumentation_data(tool_name:) begin - tool.call(**request[:arguments], context:).to_h + tool.call(**request[:arguments], server_context:).to_h rescue => e raise RequestHandlerError.new("Internal error calling tool #{tool_name}", request, original_error: e) end @@ -179,7 +179,7 @@ module ModelContextProtocol prompt_args = request[:arguments] prompt.validate_arguments!(prompt_args) - prompt.template(prompt_args, context:).to_h + prompt.template(prompt_args, server_context:).to_h end def list_resources(request) @@ -202,8 +202,8 @@ module ModelContextProtocol resource.to_h end - def report_exception(exception, context = {}) - configuration.exception_reporter.call(exception, context) + def report_exception(exception, server_context = {}) + configuration.exception_reporter.call(exception, server_context) end def index_resources_by_uri(resources) diff --git a/lib/model_context_protocol/tool.rb b/lib/model_context_protocol/tool.rb index 6415b77..fa5324d 100644 --- a/lib/model_context_protocol/tool.rb +++ b/lib/model_context_protocol/tool.rb @@ -44,7 +44,7 @@ module ModelContextProtocol attr_reader :input_schema_value attr_reader :annotations_value - def call(*args, context:) + def call(*args, server_context:) raise NotImplementedError, "Subclasses must implement call" end @@ -108,8 +108,8 @@ module ModelContextProtocol description description input_schema input_schema self.annotations(annotations) if annotations - define_singleton_method(:call) do |*args, context:| - instance_exec(*args, context:, &block) + define_singleton_method(:call) do |*args, server_context:| + instance_exec(*args, server_context:, &block) end end end diff --git a/lib/model_context_protocol/version.rb b/lib/model_context_protocol/version.rb index 14744f8..16d6321 100644 --- a/lib/model_context_protocol/version.rb +++ b/lib/model_context_protocol/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module ModelContextProtocol - VERSION = "0.5.2" + VERSION = "0.6.0" end diff --git a/test/model_context_protocol/configuration_test.rb b/test/model_context_protocol/configuration_test.rb index b021ecb..e71efd4 100644 --- a/test/model_context_protocol/configuration_test.rb +++ b/test/model_context_protocol/configuration_test.rb @@ -10,10 +10,10 @@ module ModelContextProtocol # The default reporter should be callable but do nothing exception = StandardError.new("test error") - context = { test: "context" } + server_context = { test: "context" } # Should not raise any errors - config.exception_reporter.call(exception, context) + config.exception_reporter.call(exception, server_context) end test "allows setting a custom exception reporter" do @@ -21,9 +21,9 @@ module ModelContextProtocol reported_exception = nil reported_context = nil - config.exception_reporter = ->(exception, context) do + config.exception_reporter = ->(exception, server_context) do reported_exception = exception - reported_context = context + reported_context = server_context end test_exception = StandardError.new("test error") diff --git a/test/model_context_protocol/prompt_test.rb b/test/model_context_protocol/prompt_test.rb index 9d979d0..151875c 100644 --- a/test/model_context_protocol/prompt_test.rb +++ b/test/model_context_protocol/prompt_test.rb @@ -12,7 +12,7 @@ module ModelContextProtocol ] class << self - def template(args, context:) + def template(args, server_context:) Prompt::Result.new( description: "Hello, world!", messages: [ @@ -35,7 +35,7 @@ module ModelContextProtocol ], } - result = prompt.template({ "test_argument" => "Hello, friend!" }, context: { user_id: 123 }) + result = prompt.template({ "test_argument" => "Hello, friend!" }, server_context: { user_id: 123 }) assert_equal expected_template_result, result.to_h end @@ -49,7 +49,7 @@ module ModelContextProtocol ] class << self - def template(args, context:) + def template(args, server_context:) Prompt::Result.new( description: "Hello, world!", messages: [ @@ -77,7 +77,7 @@ module ModelContextProtocol ], } - result = prompt.template({ "test_argument" => "Hello, friend!" }, context: { user_id: 123 }) + result = prompt.template({ "test_argument" => "Hello, friend!" }, server_context: { user_id: 123 }) assert_equal expected_template_result, result.to_h end @@ -89,7 +89,7 @@ module ModelContextProtocol ] class << self - def template(args, context:) + def template(args, server_context:) Prompt::Result.new( description: "Hello, world!", messages: [ @@ -115,8 +115,8 @@ module ModelContextProtocol arguments: [ Prompt::Argument.new(name: "test_argument", description: "Test argument", required: true), ], - ) do |args, context:| - content = Content::Text.new(args["test_argument"] + " user: #{context[:user_id]}") + ) do |args, server_context:| + content = Content::Text.new(args["test_argument"] + " user: #{server_context[:user_id]}") Prompt::Result.new( description: "Hello, world!", @@ -139,7 +139,7 @@ module ModelContextProtocol ], } - result = prompt.template({ "test_argument" => "Hello, friend!" }, context: { user_id: 123 }) + result = prompt.template({ "test_argument" => "Hello, friend!" }, server_context: { user_id: 123 }) assert_equal expected, result.to_h end end diff --git a/test/model_context_protocol/server_test.rb b/test/model_context_protocol/server_test.rb index 92fb9e9..dd6caa1 100644 --- a/test/model_context_protocol/server_test.rb +++ b/test/model_context_protocol/server_test.rb @@ -182,7 +182,7 @@ module ModelContextProtocol tool_args = { "arg" => "value" } tool_response = Tool::Response.new([{ "result" => "success" }]) - @tool.expects(:call).with(**tool_args, context: @server.context).returns(tool_response) + @tool.expects(:call).with(**tool_args, server_context: @server.server_context).returns(tool_response) request = { jsonrpc: "2.0", @@ -204,7 +204,7 @@ module ModelContextProtocol tool_args = { arg: "value" } tool_response = Tool::Response.new([{ result: "success" }]) - @tool.expects(:call).with(**tool_args, context: @server.context).returns(tool_response) + @tool.expects(:call).with(**tool_args, server_context: @server.server_context).returns(tool_response) request = JSON.generate({ jsonrpc: "2.0", @@ -219,7 +219,7 @@ module ModelContextProtocol end test "#handle tools/call returns internal error and reports exception if the tool raises an error" do - @server.configuration.exception_reporter.expects(:call).with do |exception, context| + @server.configuration.exception_reporter.expects(:call).with do |exception, server_context| assert_not_nil exception assert_equal( { @@ -230,7 +230,7 @@ module ModelContextProtocol id: 1, }, }, - context, + server_context, ) end @@ -553,8 +553,8 @@ module ModelContextProtocol test "the server configuration takes precedence over the global configuration" do configuration = ModelContextProtocol::Configuration.new local_callback = ->(data) { puts "Local callback #{data.inspect}" } - local_exception_reporter = ->(exception, context) { - puts "Local exception reporter #{exception.inspect} #{context.inspect}" + local_exception_reporter = ->(exception, server_context) { + puts "Local exception reporter #{exception.inspect} #{server_context.inspect}" } configuration.instrumentation_callback = local_callback configuration.exception_reporter = local_exception_reporter