Internal Release 0.6.0
Rename Context Arg
This commit is contained in:
parent
6da8bfab1a
commit
62184957e0
11 changed files with 52 additions and 52 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
34
README.md
34
README.md
|
|
@ -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 },
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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}",
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue