Class: UpstreamProxy

Inherits:
Object
  • Object
show all
Defined in:
app/mcp/upstream_proxy.rb

Overview

Connects to an upstream MCP server and provides methods to list tools and forward calls.
Uses the official MCP Ruby SDK's client with HTTP transport.

Examples:

upstream = UpstreamRegistry::Upstream.new(
  service_key: 'example',
  url: 'https://api.example.com/mcp',
  headers: { 'Authorization' => 'Bearer ...' }
)
proxy = UpstreamProxy.new(upstream)
tools = proxy.list_tools       # => Array<MCP::Client::Tool>
result = proxy.call_tool('some_tool', { key: 'value' })

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(upstream) ⇒ UpstreamProxy

Returns a new instance of UpstreamProxy.



21
22
23
24
# File 'app/mcp/upstream_proxy.rb', line 21

def initialize(upstream)
  @upstream = upstream
  @service_key = upstream.service_key
end

Instance Attribute Details

#service_keyObject (readonly)

Returns the value of attribute service_key.



19
20
21
# File 'app/mcp/upstream_proxy.rb', line 19

def service_key
  @service_key
end

#upstreamObject (readonly)

Returns the value of attribute upstream.



19
20
21
# File 'app/mcp/upstream_proxy.rb', line 19

def upstream
  @upstream
end

Instance Method Details

#call_tool(tool_name, arguments = {}) ⇒ Hash

Call a tool on the upstream server by its original (non-namespaced) name.

Parameters:

  • tool_name (String)

    The tool name as known by the upstream

  • arguments (Hash) (defaults to: {})

    The arguments to pass

Returns:

  • (Hash)

    The tool result with 'content' array (or 'isError' flag)



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'app/mcp/upstream_proxy.rb', line 40

def call_tool(tool_name, arguments = {})
  tool = list_tools.find { |t| t.name == tool_name }
  raise "Unknown tool '#{tool_name}' on upstream '#{service_key}'" unless tool

  response = client.call_tool(tool: tool, arguments: arguments)

  # The MCP client returns the full JSON-RPC response.
  # Success: { "result" => { "content" => [...] } }
  # Error:   { "error" => { "code" => ..., "message" => "..." } }
  if response['error']
    error_msg = response.dig('error', 'message') || 'Unknown upstream error'
    { 'isError' => true, 'content' => [{ 'type' => 'text', 'text' => "Upstream error (#{service_key}): #{error_msg}" }] }
  else
    response['result'] || { 'content' => [{ 'type' => 'text', 'text' => '{}' }] }
  end
rescue MCP::Client::RequestHandlerError => e
  { 'isError' => true, 'content' => [{ 'type' => 'text', 'text' => "Upstream error (#{service_key}): #{e.message}" }] }
rescue => e
  { 'isError' => true, 'content' => [{ 'type' => 'text', 'text' => "Proxy error (#{service_key}): #{e.message}" }] }
end

#list_toolsArray<MCP::Client::Tool>

List all tools from the upstream server.
Results are cached for the lifetime of this proxy instance.

Returns:

  • (Array<MCP::Client::Tool>)


29
30
31
32
33
34
# File 'app/mcp/upstream_proxy.rb', line 29

def list_tools
  @tools ||= client.tools
rescue => e
  Rails.logger.error "[MCP Proxy] Failed to list tools from #{service_key}: #{e.message}"
  []
end