Class: Legion::MCP::Client::Connection

Inherits:
Object
  • Object
show all
Includes:
Logging::Helper
Defined in:
lib/legion/mcp/client/connection.rb

Overview

rubocop:disable Metrics/ClassLength

Constant Summary collapse

TOOL_CACHE_TTL =

seconds

300

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name:, transport:, **config) ⇒ Connection

Returns a new instance of Connection.



16
17
18
19
20
21
22
23
24
25
26
# File 'lib/legion/mcp/client/connection.rb', line 16

def initialize(name:, transport:, **config)
  @name = name
  @transport_type = transport.to_sym
  @config = config
  @tools_cache = nil
  @tools_cached_at = nil
  @connected = false
  @mcp_client = nil
  @mcp_transport = nil
  @mutex = Mutex.new
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



12
13
14
# File 'lib/legion/mcp/client/connection.rb', line 12

def config
  @config
end

#nameObject (readonly)

Returns the value of attribute name.



12
13
14
# File 'lib/legion/mcp/client/connection.rb', line 12

def name
  @name
end

#transport_typeObject (readonly)

Returns the value of attribute transport_type.



12
13
14
# File 'lib/legion/mcp/client/connection.rb', line 12

def transport_type
  @transport_type
end

Instance Method Details

#call_tool(name:, arguments: {}, context: {}) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/legion/mcp/client/connection.rb', line 88

def call_tool(name:, arguments: {}, context: {})
  connect unless connected?
  exchange_id = TracingContext.generate_exchange_id

  log.info("[mcp] client.tool_call.start #{Utils.format_fields(
    connection: @name, transport: @transport_type, tool_name: name,
    exchange_id: exchange_id, trace_id: context[:trace_id],
    arguments: Utils.summarize_params(arguments)
  )}")

  start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
  result = execute_tool_call(name: name, arguments: arguments, context: context)
  elapsed = ((::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start_time) * 1000).round(1)

  log.info("[mcp] client.tool_call.complete #{Utils.format_fields(
    connection: @name, transport: @transport_type, tool_name: name,
    exchange_id: exchange_id, duration_ms: elapsed,
    result: Utils.summarize_result(result)
  )}")

  emit_client_audit(tool_name: name, arguments: arguments, result: result,
                    context: context, exchange_id: exchange_id, duration_ms: elapsed)

  result
rescue StandardError => e
  handle_exception(e, level: :warn, operation: 'legion.mcp.client.connection.call_tool')
  raise
end

#connectObject



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/legion/mcp/client/connection.rb', line 32

def connect
  @mutex.synchronize do
    return if @connected

    log.info("[mcp] client.connect.start #{Utils.format_fields(connection: @name, transport: @transport_type,
                                                               config: @config.slice(:url, :command))}")
    case @transport_type
    when :stdio
      connect_stdio
    when :http, :streamable_http
      connect_http
    else
      raise ArgumentError, "Unknown transport: #{@transport_type}"
    end

    verify_connection!

    @connected = true
    log.info("[mcp] client.connect.complete #{Utils.format_fields(connection: @name, transport: @transport_type)}")
  end
rescue StandardError => e
  @connected = false
  handle_exception(e, level: :error, operation: 'legion.mcp.client.connection.connect')
  raise
end

#connected?Boolean

Returns:

  • (Boolean)


28
29
30
# File 'lib/legion/mcp/client/connection.rb', line 28

def connected?
  @connected
end

#disconnectObject



58
59
60
61
62
63
64
65
66
67
# File 'lib/legion/mcp/client/connection.rb', line 58

def disconnect
  log.debug("[mcp][client] action=disconnect connection=#{@name}")
  @mutex.synchronize do
    @mcp_transport&.close if @mcp_transport.respond_to?(:close)
    @connected = false
    @mcp_client = nil
    @mcp_transport = nil
    @tools_cache = nil
  end
end

#tools(force_refresh: false) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/legion/mcp/client/connection.rb', line 69

def tools(force_refresh: false)
  @mutex.synchronize do
    if !force_refresh && @tools_cache && @tools_cached_at &&
       (Time.now - @tools_cached_at) < TOOL_CACHE_TTL
      log.info("[mcp] client.tools.cache_hit #{Utils.format_fields(connection: @name, transport: @transport_type,
                                                                   count: @tools_cache.size)}")
      return @tools_cache
    end

    log.info("[mcp] client.tools.fetch.start #{Utils.format_fields(connection: @name, transport: @transport_type,
                                                                   force_refresh: force_refresh)}")
    @tools_cache = fetch_tools
    @tools_cached_at = Time.now
    log.info("[mcp] client.tools.fetch.complete #{Utils.format_fields(connection: @name, transport: @transport_type,
                                                                      count: @tools_cache.size)}")
    @tools_cache
  end
end