From 0593818cd45b95d7f1de5b2a314557e464c421a0 Mon Sep 17 00:00:00 2001 From: Mikhail Dubov Date: Tue, 3 Feb 2026 19:51:48 +0000 Subject: [PATCH] Add client info to instrumentation callback --- README.md | 1 + lib/mcp/instrumentation.rb | 4 ++++ lib/mcp/server.rb | 4 +++- test/mcp/server_test.rb | 47 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6440384c..c1f2a3db 100644 --- a/README.md +++ b/README.md @@ -400,6 +400,7 @@ The instrumentation callback receives a hash with the following possible keys: - `resource_uri`: (String, optional) The URI of the resource called - `error`: (String, optional) Error code if a lookup failed - `duration`: (Float) Duration of the call in seconds +- `client`: (Hash, optional) Client information with `name` and `version` keys, from the initialize request **Type:** diff --git a/lib/mcp/instrumentation.rb b/lib/mcp/instrumentation.rb index 40975a91..6c6d91d6 100644 --- a/lib/mcp/instrumentation.rb +++ b/lib/mcp/instrumentation.rb @@ -10,6 +10,10 @@ def instrument_call(method, &block) result = yield block + if respond_to?(:client) && client + add_instrumentation_data(client: client) + end + result ensure end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) diff --git a/lib/mcp/server.rb b/lib/mcp/server.rb index e1005166..eff1a84b 100644 --- a/lib/mcp/server.rb +++ b/lib/mcp/server.rb @@ -41,7 +41,7 @@ def initialize(method_name) include Instrumentation - attr_accessor :description, :icons, :name, :title, :version, :website_url, :instructions, :tools, :prompts, :resources, :server_context, :configuration, :capabilities, :transport, :logging_message_notification + attr_accessor :description, :icons, :name, :title, :version, :website_url, :instructions, :tools, :prompts, :resources, :server_context, :configuration, :capabilities, :transport, :logging_message_notification, :client def initialize( description: nil, @@ -75,6 +75,7 @@ def initialize( @resource_index = index_resources_by_uri(resources) @server_context = server_context @configuration = MCP.configuration.merge(configuration) + @client = nil validate! @@ -314,6 +315,7 @@ def server_info end def init(request) + @client = request[:clientInfo] || request["clientInfo"] if request { protocolVersion: configuration.protocol_version, capabilities: capabilities, diff --git a/test/mcp/server_test.rb b/test/mcp/server_test.rb index 061987c1..9c81401d 100644 --- a/test/mcp/server_test.rb +++ b/test/mcp/server_test.rb @@ -159,6 +159,53 @@ class ServerTest < ActiveSupport::TestCase assert_instrumentation_data({ method: "initialize" }) end + test "#handle initialize request with clientInfo includes client in instrumentation data" do + client_info = { name: "test_client", version: "1.0.0" } + request = { + jsonrpc: "2.0", + method: "initialize", + id: 1, + params: { + clientInfo: client_info, + }, + } + + @server.handle(request) + assert_instrumentation_data({ method: "initialize", client: client_info }) + end + + test "instrumentation data includes client info for subsequent requests after initialize" do + client_info = { name: "test_client", version: "1.0.0" } + initialize_request = { + jsonrpc: "2.0", + method: "initialize", + id: 1, + params: { + clientInfo: client_info, + }, + } + @server.handle(initialize_request) + + ping_request = { + jsonrpc: "2.0", + method: "ping", + id: 2, + } + @server.handle(ping_request) + assert_instrumentation_data({ method: "ping", client: client_info }) + end + + test "instrumentation data does not include client key when no clientInfo provided" do + request = { + jsonrpc: "2.0", + method: "ping", + id: 1, + } + + @server.handle(request) + assert_instrumentation_data({ method: "ping" }) + end + test "#handle returns nil for notification requests" do request = { jsonrpc: "2.0",