Internal Release 0.6.0

Rename Context Arg
This commit is contained in:
Tyler Rowsell 2025-04-23 16:30:01 -04:00 • committed by Topher Bullock
commit 62184957e0
11 changed files with 52 additions and 52 deletions

View file

@ -1,7 +1,7 @@
PATH PATH
remote: . remote: .
specs: specs:
model_context_protocol (0.5.2) model_context_protocol (0.6.0)
json_rpc_handler (~> 0.1) json_rpc_handler (~> 0.1)
GEM GEM

View file

@ -49,7 +49,7 @@ module ModelContextProtocol
name: "my_server", name: "my_server",
tools: [SomeTool, AnotherTool], tools: [SomeTool, AnotherTool],
prompts: [MyPrompt], 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) render(json: server.handle_json(request.body.read).to_h)
end end
@ -69,7 +69,7 @@ module ModelContextProtocol
name: "my_server", name: "my_server",
tools: [SomeTool, AnotherTool], tools: [SomeTool, AnotherTool],
prompts: [MyPrompt], 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) render(json: server.handle(JSON.parse(request.body.read)).to_h)
end end
@ -96,7 +96,7 @@ class ExampleTool < ModelContextProtocol::Tool
required: ["message"] required: ["message"]
class << self class << self
def call(message:, context:) def call(message:, server_context:)
ModelContextProtocol::Tool::Response.new([{ ModelContextProtocol::Tool::Response.new([{
type: "text", type: "text",
text: "Hello from example tool! Message: #{message}", text: "Hello from example tool! Message: #{message}",
@ -137,7 +137,7 @@ server = ModelContextProtocol::Server.new(
name: "my_server", name: "my_server",
tools: [SomeTool, AnotherTool], tools: [SomeTool, AnotherTool],
prompts: [MyPrompt], prompts: [MyPrompt],
context: { user_id: current_user.id }, server_context: { user_id: current_user.id },
) )
request = { request = {
jsonrpc: "2.0", jsonrpc: "2.0",
@ -154,11 +154,11 @@ The gem can be configured using the `ModelContextProtocol.configure` block:
```ruby ```ruby
ModelContextProtocol.configure do |config| ModelContextProtocol.configure do |config|
config.exception_reporter = ->(exception, context) do config.exception_reporter = ->(exception, server_context) do
# Your exception reporting logic here # Your exception reporting logic here
# For example with Bugsnag: # For example with Bugsnag:
Bugsnag.notify(exception) do |report| Bugsnag.notify(exception) do |report|
report.add_metadata(:model_context_protocol, context) report.add_metadata(:model_context_protocol, server_context)
end end
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. they might require different instrumentation callbacks.
configuration = ModelContextProtocol::Configuration.new configuration = ModelContextProtocol::Configuration.new
configuration.exception_reporter = ->(exception, context) do configuration.exception_reporter = ->(exception, server_context) do
# Your exception reporting logic here # Your exception reporting logic here
# For example with Bugsnag: # For example with Bugsnag:
Bugsnag.notify(exception) do |report| Bugsnag.notify(exception) do |report|
report.add_metadata(:model_context_protocol, context) report.add_metadata(:model_context_protocol, server_context)
end end
end end
@ -207,9 +207,9 @@ Be sure to check the [MCP spec](https://spec.modelcontextprotocol.io/specificati
The exception reporter receives two arguments: The exception reporter receives two arguments:
- `exception`: The Ruby exception object that was raised - `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 tool calls: `{ tool_name: "name", arguments: { ... } }`
- For general request handling: `{ request: { ... } }` - For general request handling: `{ request: { ... } }`
@ -248,7 +248,7 @@ class MyTool < ModelContextProtocol::Tool
}, },
required: ['message'] required: ['message']
def self.call(message:, context:) def self.call(message:, server_context:)
Tool::Response.new([{ type: "text", text: "OK" }]) Tool::Response.new([{ type: "text", text: "OK" }])
end end
end end
@ -266,12 +266,12 @@ tool = ModelContextProtocol::Tool.define(
title: "My Tool", title: "My Tool",
read_only_hint: true read_only_hint: true
} }
) do |args, context| ) do |args, server_context|
Tool::Response.new([{ type: "text", text: "OK" }]) Tool::Response.new([{ type: "text", text: "OK" }])
end 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. e.g. around authentication state.
### Tool Annotations ### Tool Annotations
@ -307,7 +307,7 @@ class MyPrompt < ModelContextProtocol::Prompt
] ]
class << self class << self
def template(args, context:) def template(args, server_context:)
Prompt::Result.new( Prompt::Result.new(
description: "Response description", description: "Response description",
messages: [ messages: [
@ -341,7 +341,7 @@ prompt = ModelContextProtocol::Prompt.define(
required: true required: true
) )
] ]
) do |args, context:| ) do |args, server_context:|
Prompt::Result.new( Prompt::Result.new(
description: "Response description", description: "Response description",
messages: [ messages: [
@ -358,7 +358,7 @@ prompt = ModelContextProtocol::Prompt.define(
end 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. e.g. around authentication state or user preferences.
### Key Components ### Key Components
@ -376,7 +376,7 @@ Register prompts with the MCP server:
server = ModelContextProtocol::Server.new( server = ModelContextProtocol::Server.new(
name: "my_server", name: "my_server",
prompts: [MyPrompt], prompts: [MyPrompt],
context: { user_id: current_user.id }, server_context: { user_id: current_user.id },
) )
``` ```

View file

@ -15,7 +15,7 @@ class ExampleTool < ModelContextProtocol::Tool
required: ["message"] required: ["message"]
class << self class << self
def call(message:, context:) def call(message:, server_context:)
ModelContextProtocol::Tool::Response.new([{ ModelContextProtocol::Tool::Response.new([{
type: "text", type: "text",
text: "Hello from example tool! Message: #{message}", text: "Hello from example tool! Message: #{message}",

View file

@ -65,7 +65,7 @@ module ModelContextProtocol
private private
def default_exception_reporter def default_exception_reporter
@default_exception_reporter ||= ->(exception, context) {} @default_exception_reporter ||= ->(exception, server_context) {}
end end
def default_instrumentation_callback def default_instrumentation_callback

View file

@ -50,7 +50,7 @@ module ModelContextProtocol
attr_reader :description_value attr_reader :description_value
attr_reader :arguments_value attr_reader :arguments_value
def template(args, context:) def template(args, server_context:)
raise NotImplementedError, "Subclasses must implement template" raise NotImplementedError, "Subclasses must implement template"
end end
@ -98,8 +98,8 @@ module ModelContextProtocol
prompt_name name prompt_name name
description description description description
arguments arguments arguments arguments
define_singleton_method(:template) do |args, context:| define_singleton_method(:template) do |args, server_context:|
instance_exec(args, context:, &block) instance_exec(args, server_context:, &block)
end end
end end
end end

View file

@ -20,16 +20,16 @@ module ModelContextProtocol
include Instrumentation 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) configuration: nil)
@name = name @name = name
@tools = tools.to_h { |t| [t.name_value, t] } @tools = tools.to_h { |t| [t.name_value, t] }
@prompts = prompts.to_h { |p| [p.name_value, p] } @prompts = prompts.to_h { |p| [p.name_value, p] }
@resources = resources @resources = resources
@resource_index = index_resources_by_uri(resources) @resource_index = index_resources_by_uri(resources)
@context = context @server_context = server_context
@configuration = ModelContextProtocol.configuration.merge(configuration) @configuration = ModelContextProtocol.configuration.merge(configuration)
@handlers = { @handlers = {
Methods::RESOURCES_LIST => method(:list_resources), Methods::RESOURCES_LIST => method(:list_resources),
@ -154,7 +154,7 @@ module ModelContextProtocol
add_instrumentation_data(tool_name:) add_instrumentation_data(tool_name:)
begin begin
tool.call(**request[:arguments], context:).to_h tool.call(**request[:arguments], server_context:).to_h
rescue => e rescue => e
raise RequestHandlerError.new("Internal error calling tool #{tool_name}", request, original_error: e) raise RequestHandlerError.new("Internal error calling tool #{tool_name}", request, original_error: e)
end end
@ -179,7 +179,7 @@ module ModelContextProtocol
prompt_args = request[:arguments] prompt_args = request[:arguments]
prompt.validate_arguments!(prompt_args) prompt.validate_arguments!(prompt_args)
prompt.template(prompt_args, context:).to_h prompt.template(prompt_args, server_context:).to_h
end end
def list_resources(request) def list_resources(request)
@ -202,8 +202,8 @@ module ModelContextProtocol
resource.to_h resource.to_h
end end
def report_exception(exception, context = {}) def report_exception(exception, server_context = {})
configuration.exception_reporter.call(exception, context) configuration.exception_reporter.call(exception, server_context)
end end
def index_resources_by_uri(resources) def index_resources_by_uri(resources)

View file

@ -44,7 +44,7 @@ module ModelContextProtocol
attr_reader :input_schema_value attr_reader :input_schema_value
attr_reader :annotations_value attr_reader :annotations_value
def call(*args, context:) def call(*args, server_context:)
raise NotImplementedError, "Subclasses must implement call" raise NotImplementedError, "Subclasses must implement call"
end end
@ -108,8 +108,8 @@ module ModelContextProtocol
description description description description
input_schema input_schema input_schema input_schema
self.annotations(annotations) if annotations self.annotations(annotations) if annotations
define_singleton_method(:call) do |*args, context:| define_singleton_method(:call) do |*args, server_context:|
instance_exec(*args, context:, &block) instance_exec(*args, server_context:, &block)
end end
end end
end end

View file

@ -1,5 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
module ModelContextProtocol module ModelContextProtocol
VERSION = "0.5.2" VERSION = "0.6.0"
end end

View file

@ -10,10 +10,10 @@ module ModelContextProtocol
# The default reporter should be callable but do nothing # The default reporter should be callable but do nothing
exception = StandardError.new("test error") exception = StandardError.new("test error")
context = { test: "context" } server_context = { test: "context" }
# Should not raise any errors # Should not raise any errors
config.exception_reporter.call(exception, context) config.exception_reporter.call(exception, server_context)
end end
test "allows setting a custom exception reporter" do test "allows setting a custom exception reporter" do
@ -21,9 +21,9 @@ module ModelContextProtocol
reported_exception = nil reported_exception = nil
reported_context = nil reported_context = nil
config.exception_reporter = ->(exception, context) do config.exception_reporter = ->(exception, server_context) do
reported_exception = exception reported_exception = exception
reported_context = context reported_context = server_context
end end
test_exception = StandardError.new("test error") test_exception = StandardError.new("test error")

View file

@ -12,7 +12,7 @@ module ModelContextProtocol
] ]
class << self class << self
def template(args, context:) def template(args, server_context:)
Prompt::Result.new( Prompt::Result.new(
description: "Hello, world!", description: "Hello, world!",
messages: [ 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 assert_equal expected_template_result, result.to_h
end end
@ -49,7 +49,7 @@ module ModelContextProtocol
] ]
class << self class << self
def template(args, context:) def template(args, server_context:)
Prompt::Result.new( Prompt::Result.new(
description: "Hello, world!", description: "Hello, world!",
messages: [ 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 assert_equal expected_template_result, result.to_h
end end
@ -89,7 +89,7 @@ module ModelContextProtocol
] ]
class << self class << self
def template(args, context:) def template(args, server_context:)
Prompt::Result.new( Prompt::Result.new(
description: "Hello, world!", description: "Hello, world!",
messages: [ messages: [
@ -115,8 +115,8 @@ module ModelContextProtocol
arguments: [ arguments: [
Prompt::Argument.new(name: "test_argument", description: "Test argument", required: true), Prompt::Argument.new(name: "test_argument", description: "Test argument", required: true),
], ],
) do |args, context:| ) do |args, server_context:|
content = Content::Text.new(args["test_argument"] + " user: #{context[:user_id]}") content = Content::Text.new(args["test_argument"] + " user: #{server_context[:user_id]}")
Prompt::Result.new( Prompt::Result.new(
description: "Hello, world!", 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 assert_equal expected, result.to_h
end end
end end

View file

@ -182,7 +182,7 @@ module ModelContextProtocol
tool_args = { "arg" => "value" } tool_args = { "arg" => "value" }
tool_response = Tool::Response.new([{ "result" => "success" }]) 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 = { request = {
jsonrpc: "2.0", jsonrpc: "2.0",
@ -204,7 +204,7 @@ module ModelContextProtocol
tool_args = { arg: "value" } tool_args = { arg: "value" }
tool_response = Tool::Response.new([{ result: "success" }]) 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({ request = JSON.generate({
jsonrpc: "2.0", jsonrpc: "2.0",
@ -219,7 +219,7 @@ module ModelContextProtocol
end end
test "#handle tools/call returns internal error and reports exception if the tool raises an error" do 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_not_nil exception
assert_equal( assert_equal(
{ {
@ -230,7 +230,7 @@ module ModelContextProtocol
id: 1, id: 1,
}, },
}, },
context, server_context,
) )
end end
@ -553,8 +553,8 @@ module ModelContextProtocol
test "the server configuration takes precedence over the global configuration" do test "the server configuration takes precedence over the global configuration" do
configuration = ModelContextProtocol::Configuration.new configuration = ModelContextProtocol::Configuration.new
local_callback = ->(data) { puts "Local callback #{data.inspect}" } local_callback = ->(data) { puts "Local callback #{data.inspect}" }
local_exception_reporter = ->(exception, context) { local_exception_reporter = ->(exception, server_context) {
puts "Local exception reporter #{exception.inspect} #{context.inspect}" puts "Local exception reporter #{exception.inspect} #{server_context.inspect}"
} }
configuration.instrumentation_callback = local_callback configuration.instrumentation_callback = local_callback
configuration.exception_reporter = local_exception_reporter configuration.exception_reporter = local_exception_reporter