Add automatic _meta parameter extraction support#172
Add automatic _meta parameter extraction support#172koic merged 1 commit intomodelcontextprotocol:mainfrom
Conversation
fdb400a to
ad9677a
Compare
0a22bf1 to
d20733b
Compare
|
@scutuatua-crypto lgtm? |
lib/mcp/server.rb
Outdated
| context | ||
| end | ||
|
|
||
| call_prompt_template_with_args(prompt, prompt_args, context_with_meta) |
There was a problem hiding this comment.
The code duplication caught my attention. Could it be updated as a change like the following overall?
index e9a4e50..862c3cd 100644
--- a/lib/mcp/server.rb
+++ b/lib/mcp/server.rb
@@ -419,7 +419,7 @@ module MCP
end
end
- call_tool_with_args(tool, arguments)
+ call_tool_with_args(tool, arguments, server_context_with_meta(request))
rescue RequestHandlerError
raise
rescue => e
@@ -445,7 +445,7 @@ module MCP
prompt_args = request[:arguments]
prompt.validate_arguments!(prompt_args)
- call_prompt_template_with_args(prompt, prompt_args)
+ call_prompt_template_with_args(prompt, prompt_args, server_context_with_meta(request))
end
def list_resources(request)
@@ -488,7 +488,7 @@ module MCP
parameters.any? { |type, name| type == :keyrest || name == :server_context }
end
- def call_tool_with_args(tool, arguments)
+ def call_tool_with_args(tool, arguments, server_context)
args = arguments&.transform_keys(&:to_sym) || {}
if accepts_server_context?(tool.method(:call))
@@ -498,12 +498,25 @@ module MCP
end
end
- def call_prompt_template_with_args(prompt, args)
+ def call_prompt_template_with_args(prompt, args, server_context)
if accepts_server_context?(prompt.method(:template))
prompt.template(args, server_context: server_context).to_h
else
prompt.template(args).to_h
end
end
+
+ def server_context_with_meta(request)
+ meta = request[:_meta]
+ if @server_context && meta
+ context = @server_context.dup
+ context[:_meta] = meta
+ context
+ elsif meta
+ { _meta: meta }
+ elsif @server_context
+ @server_context
+ end
+ end
end
end
test/mcp/server_context_test.rb
Outdated
| response[:result][:messages][0][:content][:text] | ||
| end | ||
|
|
||
| # _meta extraction tests |
There was a problem hiding this comment.
This comment appears unnecessary since it is already explained in the test code.
test/mcp/server_context_test.rb
Outdated
|
|
||
| class << self | ||
| def call(message:, server_context: nil) | ||
| meta_info = server_context&.dig(:_meta, :provider, :metadata) || "no metadata" |
There was a problem hiding this comment.
In this test, it appears that a server_context with _meta set is always passed.
| meta_info = server_context&.dig(:_meta, :provider, :metadata) || "no metadata" | |
| def call(message:, server_context:) | |
| meta_info = server_context.dig(:_meta, :provider, :metadata) |
test/mcp/server_context_test.rb
Outdated
| class << self | ||
| def call(message:, server_context: nil) | ||
| user = server_context&.dig(:user) || "unknown" | ||
| session_id = server_context&.dig(:_meta, :session_id) || "unknown" |
There was a problem hiding this comment.
| session_id = server_context&.dig(:_meta, :session_id) || "unknown" | |
| def call(message:, server_context:) | |
| user = server_context[:user] | |
| session_id = server_context.dig(:_meta, :session_id) |
test/mcp/server_context_test.rb
Outdated
| class << self | ||
| def call(server_context: nil) | ||
| priority = server_context&.dig(:priority) || "none" | ||
| meta_priority = server_context&.dig(:_meta, :priority) || "none" |
There was a problem hiding this comment.
| meta_priority = server_context&.dig(:_meta, :priority) || "none" | |
| def call(server_context:) | |
| priority = server_context[:priority] | |
| meta_priority = server_context.dig(:_meta, :priority) |
test/mcp/server_context_test.rb
Outdated
|
|
||
| class << self | ||
| def template(args, server_context: nil) | ||
| meta_info = server_context&.dig(:_meta, :request_id) || "no request id" |
There was a problem hiding this comment.
| meta_info = server_context&.dig(:_meta, :request_id) || "no request id" | |
| def template(args, server_context:) | |
| meta_info = server_context.dig(:_meta, :request_id) |
test/mcp/server_context_test.rb
Outdated
| def template(args, server_context: nil) | ||
| user = server_context&.dig(:user) || "unknown" | ||
| trace_id = server_context&.dig(:_meta, :trace_id) || "unknown" |
There was a problem hiding this comment.
| def template(args, server_context: nil) | |
| user = server_context&.dig(:user) || "unknown" | |
| trace_id = server_context&.dig(:_meta, :trace_id) || "unknown" | |
| def template(args, server_context:) | |
| user = server_context[:user] | |
| trace_id = server_context.dig(:_meta, :trace_id) |
README.md
Outdated
| def self.call(message:, server_context: nil) | ||
| # Access provider-specific metadata | ||
| session_id = server_context&.dig(:_meta, :session_id) | ||
| request_id = server_context&.dig(:_meta, :request_id) | ||
|
|
||
| # Access server's original context | ||
| user_id = server_context&.dig(:user_id) |
There was a problem hiding this comment.
It seems that the example would be simpler if written as follows.
| def self.call(message:, server_context: nil) | |
| # Access provider-specific metadata | |
| session_id = server_context&.dig(:_meta, :session_id) | |
| request_id = server_context&.dig(:_meta, :request_id) | |
| # Access server's original context | |
| user_id = server_context&.dig(:user_id) | |
| def self.call(message:, server_context:) | |
| # Access provider-specific metadata | |
| session_id = server_context.dig(:_meta, :session_id) | |
| request_id = server_context.dig(:_meta, :request_id) | |
| # Access server's original context | |
| user_id = server_context.dig(:user_id) |
879856e to
4ca32c5
Compare
|
@koic feedback addressed |
|
@erickreutz This looks good to me. Can you squash your commits into one? |
0b8f6f4 to
50cd86c
Compare
|
@koic done |
|
Thanks! |
Summary
This PR adds native support for the MCP protocol's
_metaparameter, eliminating the need for manual extraction in controllers.Background
The MCP specification defines a
_metafield that allows clients to pass request-specific metadata. Previously, Ruby SDK users had to manually extract this field from request parameters.Changes
_metafrom request parameters incall_toolandget_promptmethods_metais passed as a nested field withinserver_context(accessible viaserver_context[:_meta])_metawithin the contextserver_contextor_metapresentTesting
_metaextraction and nesting behaviorDocumentation
_metausage examplesUsage Example
Breaking Changes
None - this is backwards compatible. Tools that don't use
server_contextor don't access_metawill continue to work unchanged.Notes
While implementing this feature, we discovered that the README incorrectly states that server_context is passed to exception and instrumentation callbacks, though the actual implementation only passes contextual error information to these callbacks.