Skip to content

Commit efdde36

Browse files
authored
Merge pull request #32 from henriquenfaria/hexdigest-unique-key
Use SHA256 hexdigest to uniquely identify MCP tools
2 parents 3d457fe + e80c22a commit efdde36

File tree

5 files changed

+83
-9
lines changed

5 files changed

+83
-9
lines changed

lib/mcp/sse_client.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require "securerandom"
44
require "faraday"
55
require "uri"
6+
require "digest"
67

78
module Raix
89
module MCP
@@ -84,7 +85,8 @@ def close
8485
end
8586

8687
def unique_key
87-
@url.parameterize.underscore.gsub("https_", "")
88+
parametrized_url = @url.parameterize.underscore.gsub("https_", "")
89+
Digest::SHA256.hexdigest(parametrized_url)[0..2]
8890
end
8991

9092
private

lib/mcp/stdio_client.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require_relative "tool"
22
require "json"
33
require "securerandom"
4+
require "digest"
45

56
module Raix
67
module MCP
@@ -57,7 +58,8 @@ def close
5758
end
5859

5960
def unique_key
60-
@args.join(" ").parameterize.underscore
61+
parametrized_args = @args.join(" ").parameterize.underscore
62+
Digest::SHA256.hexdigest(parametrized_args)[0..2]
6163
end
6264

6365
private

lib/raix/mcp.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def mcp(client:, only: nil, except: nil)
102102
filtered_tools.each do |tool|
103103
remote_name = tool.name
104104
# TODO: Revisit later whether this much context is needed in the function name
105-
local_name = "#{client.unique_key}_#{remote_name}".to_sym
105+
local_name = "#{remote_name}_#{client.unique_key}".to_sym
106106

107107
description = tool.description
108108
input_schema = tool.input_schema || {}
@@ -140,7 +140,7 @@ def mcp(client:, only: nil, except: nil)
140140
id: call_id,
141141
type: "function",
142142
function: {
143-
name: remote_name,
143+
name: local_name.to_s,
144144
arguments: arguments.to_json
145145
}
146146
}
@@ -149,7 +149,7 @@ def mcp(client:, only: nil, except: nil)
149149
{
150150
role: "tool",
151151
tool_call_id: call_id,
152-
name: remote_name,
152+
name: local_name.to_s,
153153
content: content_text
154154
}
155155
]

spec/raix/mcp/sse_spec.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,12 @@ def self.functions
5555

5656
# Print available tools for debugging
5757
tools = LiveMcpConsumer.functions.map { |f| f[:name] }
58-
expect(tools).to include(:gitmcp_io_olympiaai_raix_docs_fetch_raix_documentation)
59-
expect(tools).to include(:gitmcp_io_olympiaai_raix_docs_search_raix_documentation)
60-
expect(tools).to include(:gitmcp_io_olympiaai_raix_docs_search_raix_code)
61-
expect(tools).to include(:gitmcp_io_olympiaai_raix_docs_fetch_generic_url_content)
58+
59+
unique_key_hash = "715"
60+
expect(tools).to include(:"fetch_raix_documentation_#{unique_key_hash}")
61+
expect(tools).to include(:"search_raix_documentation_#{unique_key_hash}")
62+
expect(tools).to include(:"search_raix_code_#{unique_key_hash}")
63+
expect(tools).to include(:"fetch_generic_url_content_#{unique_key_hash}")
6264
end
6365

6466
it "successfully calls a function on the GitMCP server", :novcr do

spec/raix/mcp_spec.rb

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,71 @@ def self.name
326326
expect(result["bool4"]).to eq(false)
327327
end
328328
end
329+
330+
RSpec.describe "MCP function name mapping" do
331+
let(:test_class) do
332+
Class.new do
333+
include Raix::ChatCompletion
334+
include Raix::MCP
335+
336+
attr_accessor :transcript
337+
338+
def initialize
339+
@transcript = []
340+
end
341+
342+
def self.name
343+
"TestMcpFunctionNames"
344+
end
345+
346+
def chat_completion_args
347+
{}
348+
end
349+
350+
def loop
351+
false
352+
end
353+
end
354+
end
355+
356+
it "uses local_name with prefix in transcript instead of remote_name" do
357+
client_key = "client_key"
358+
mock_tool = OpenStruct.new(
359+
name: "get_data",
360+
description: "Gets some data",
361+
input_schema: {
362+
"properties" => {
363+
"id" => { "type" => "integer" }
364+
}
365+
}
366+
)
367+
mock_client = double("MCP::StdioClient",
368+
unique_key: client_key,
369+
close: nil,
370+
tools: [mock_tool])
371+
372+
data_result = "Data for ID 123"
373+
allow(mock_client).to receive(:call_tool).with("get_data", id: 123).and_return(data_result)
374+
test_class.mcp(client: mock_client)
375+
instance = test_class.new
376+
377+
local_method_name = :get_data_client_key
378+
expect(instance).to respond_to(local_method_name)
379+
380+
result = instance.send(local_method_name, { id: "123" }, nil)
381+
expect(result).to eq(data_result)
382+
383+
expect(instance.transcript.size).to eq(1)
384+
messages = instance.transcript[0]
385+
expect(messages).to be_an(Array)
386+
expect(messages.size).to eq(2)
387+
388+
assistant_msg = messages[0]
389+
expect(assistant_msg[:role]).to eq("assistant")
390+
expect(assistant_msg[:tool_calls][0][:function][:name]).to eq("get_data_#{client_key}")
391+
392+
tool_msg = messages[1]
393+
expect(tool_msg[:role]).to eq("tool")
394+
expect(tool_msg[:name]).to eq("get_data_#{client_key}")
395+
end
396+
end

0 commit comments

Comments
 (0)