Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:**

Expand Down
4 changes: 4 additions & 0 deletions lib/mcp/instrumentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ def instrument_call(method, &block)

result = yield block

if respond_to?(:client) && client
add_instrumentation_data(client: client)
end
Copy link
Member

@koic koic Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was concerned that the mix-in module Instrumentation depends on information specific to Server. One idea I had was that doing something like the following would avoid introducing a circular dependency and would allow Server to avoid exposing an unnecessary client attribute.

          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 eff1a84..5913a25 100644
--- a/lib/mcp/server.rb
+++ b/lib/mcp/server.rb
@@ -41,7 +41,7 @@ module MCP
 
     include Instrumentation
 
-    attr_accessor :description, :icons, :name, :title, :version, :website_url, :instructions, :tools, :prompts, :resources, :server_context, :configuration, :capabilities, :transport, :logging_message_notification, :client
+    attr_accessor :description, :icons, :name, :title, :version, :website_url, :instructions, :tools, :prompts, :resources, :server_context, :configuration, :capabilities, :transport, :logging_message_notification
 
     def initialize(
       description: nil,
@@ -259,7 +259,9 @@ module MCP
     def handle_request(request, method)
       handler = @handlers[method]
       unless handler
-        instrument_call("unsupported_method") {}
+        instrument_call("unsupported_method") do
+          add_instrumentation_data(client: @client) if @client
+        end
         return
       end
 
@@ -267,7 +269,7 @@ module MCP
 
       ->(params) {
         instrument_call(method) do
-          case method
+          result = case method
           when Methods::TOOLS_LIST
             { tools: @handlers[Methods::TOOLS_LIST].call(params) }
           when Methods::PROMPTS_LIST
@@ -281,6 +283,9 @@ module MCP
           else
             @handlers[method].call(params)
           end
+          add_instrumentation_data(client: @client) if @client
+
+          result
         rescue => e
           report_exception(e, { request: request })
           if e.is_a?(RequestHandlerError)


result
ensure
end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
Expand Down
4 changes: 3 additions & 1 deletion lib/mcp/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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!

Expand Down Expand Up @@ -314,6 +315,7 @@ def server_info
end

def init(request)
@client = request[:clientInfo] || request["clientInfo"] if request
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to be unresolved.

Suggested change
@client = request[:clientInfo] || request["clientInfo"] if request
@client = request[:clientInfo] if request

#221 (comment)

{
protocolVersion: configuration.protocol_version,
capabilities: capabilities,
Expand Down
47 changes: 47 additions & 0 deletions test/mcp/server_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down