legion-mcp 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/legion-mcp.gemspec +1 -1
  4. data/lib/legion/mcp/actors/self_generate_cycle.rb +4 -10
  5. data/lib/legion/mcp/auth.rb +5 -1
  6. data/lib/legion/mcp/catalog_bridge.rb +7 -1
  7. data/lib/legion/mcp/catalog_dispatcher.rb +60 -9
  8. data/lib/legion/mcp/client/connection.rb +62 -3
  9. data/lib/legion/mcp/client/pool.rb +9 -2
  10. data/lib/legion/mcp/client.rb +10 -2
  11. data/lib/legion/mcp/cold_start.rb +7 -2
  12. data/lib/legion/mcp/context_guard.rb +4 -1
  13. data/lib/legion/mcp/dynamic_injector.rb +4 -1
  14. data/lib/legion/mcp/embedding_index.rb +6 -2
  15. data/lib/legion/mcp/function_discovery.rb +5 -1
  16. data/lib/legion/mcp/logging_support.rb +159 -0
  17. data/lib/legion/mcp/observer.rb +10 -4
  18. data/lib/legion/mcp/override_broadcast.rb +14 -4
  19. data/lib/legion/mcp/pattern_compiler.rb +4 -1
  20. data/lib/legion/mcp/pattern_exchange.rb +5 -1
  21. data/lib/legion/mcp/pattern_gossip.rb +8 -2
  22. data/lib/legion/mcp/pattern_store.rb +125 -18
  23. data/lib/legion/mcp/resources/extension_info.rb +7 -2
  24. data/lib/legion/mcp/resources/runner_catalog.rb +7 -2
  25. data/lib/legion/mcp/self_generate.rb +7 -1
  26. data/lib/legion/mcp/server.rb +53 -5
  27. data/lib/legion/mcp/state_tracker.rb +12 -4
  28. data/lib/legion/mcp/structural_index.rb +6 -2
  29. data/lib/legion/mcp/tier_router.rb +135 -23
  30. data/lib/legion/mcp/tools/absorb.rb +5 -1
  31. data/lib/legion/mcp/tools/ask_peer.rb +5 -1
  32. data/lib/legion/mcp/tools/broadcast_peers.rb +5 -1
  33. data/lib/legion/mcp/tools/create_chain.rb +7 -2
  34. data/lib/legion/mcp/tools/create_relationship.rb +7 -2
  35. data/lib/legion/mcp/tools/create_schedule.rb +7 -2
  36. data/lib/legion/mcp/tools/dataset_list.rb +7 -2
  37. data/lib/legion/mcp/tools/dataset_show.rb +7 -2
  38. data/lib/legion/mcp/tools/delete_chain.rb +7 -2
  39. data/lib/legion/mcp/tools/delete_relationship.rb +7 -2
  40. data/lib/legion/mcp/tools/delete_schedule.rb +7 -2
  41. data/lib/legion/mcp/tools/delete_task.rb +7 -2
  42. data/lib/legion/mcp/tools/describe_runner.rb +7 -2
  43. data/lib/legion/mcp/tools/disable_extension.rb +7 -2
  44. data/lib/legion/mcp/tools/discover_tools.rb +5 -1
  45. data/lib/legion/mcp/tools/do_action.rb +141 -25
  46. data/lib/legion/mcp/tools/enable_extension.rb +7 -2
  47. data/lib/legion/mcp/tools/eval_list.rb +7 -2
  48. data/lib/legion/mcp/tools/eval_results.rb +9 -3
  49. data/lib/legion/mcp/tools/eval_run.rb +7 -2
  50. data/lib/legion/mcp/tools/experiment_results.rb +9 -3
  51. data/lib/legion/mcp/tools/get_config.rb +5 -1
  52. data/lib/legion/mcp/tools/get_extension.rb +7 -2
  53. data/lib/legion/mcp/tools/get_status.rb +11 -4
  54. data/lib/legion/mcp/tools/get_task.rb +7 -2
  55. data/lib/legion/mcp/tools/get_task_logs.rb +7 -2
  56. data/lib/legion/mcp/tools/knowledge_context.rb +6 -2
  57. data/lib/legion/mcp/tools/knowledge_health.rb +7 -2
  58. data/lib/legion/mcp/tools/list_chains.rb +7 -2
  59. data/lib/legion/mcp/tools/list_extensions.rb +7 -2
  60. data/lib/legion/mcp/tools/list_peers.rb +5 -1
  61. data/lib/legion/mcp/tools/list_relationships.rb +7 -2
  62. data/lib/legion/mcp/tools/list_schedules.rb +7 -2
  63. data/lib/legion/mcp/tools/list_tasks.rb +7 -2
  64. data/lib/legion/mcp/tools/list_workers.rb +7 -2
  65. data/lib/legion/mcp/tools/mesh_status.rb +5 -1
  66. data/lib/legion/mcp/tools/mind_growth_approve.rb +5 -1
  67. data/lib/legion/mcp/tools/mind_growth_build_queue.rb +5 -1
  68. data/lib/legion/mcp/tools/mind_growth_cognitive_profile.rb +5 -1
  69. data/lib/legion/mcp/tools/mind_growth_health.rb +5 -1
  70. data/lib/legion/mcp/tools/mind_growth_propose.rb +5 -1
  71. data/lib/legion/mcp/tools/mind_growth_status.rb +5 -1
  72. data/lib/legion/mcp/tools/notify_peer.rb +5 -1
  73. data/lib/legion/mcp/tools/plan_action.rb +7 -2
  74. data/lib/legion/mcp/tools/prompt_list.rb +7 -2
  75. data/lib/legion/mcp/tools/prompt_run.rb +7 -2
  76. data/lib/legion/mcp/tools/prompt_show.rb +7 -2
  77. data/lib/legion/mcp/tools/query_knowledge.rb +5 -1
  78. data/lib/legion/mcp/tools/rbac_assignments.rb +5 -1
  79. data/lib/legion/mcp/tools/rbac_check.rb +5 -1
  80. data/lib/legion/mcp/tools/rbac_grants.rb +5 -1
  81. data/lib/legion/mcp/tools/routing_stats.rb +7 -2
  82. data/lib/legion/mcp/tools/run_task.rb +7 -2
  83. data/lib/legion/mcp/tools/search_sessions.rb +7 -2
  84. data/lib/legion/mcp/tools/show_worker.rb +7 -2
  85. data/lib/legion/mcp/tools/state_diff.rb +5 -1
  86. data/lib/legion/mcp/tools/structural_index.rb +5 -1
  87. data/lib/legion/mcp/tools/team_summary.rb +7 -2
  88. data/lib/legion/mcp/tools/tool_audit.rb +5 -1
  89. data/lib/legion/mcp/tools/update_chain.rb +7 -2
  90. data/lib/legion/mcp/tools/update_relationship.rb +7 -2
  91. data/lib/legion/mcp/tools/update_schedule.rb +7 -2
  92. data/lib/legion/mcp/tools/worker_costs.rb +7 -2
  93. data/lib/legion/mcp/tools/worker_lifecycle.rb +9 -3
  94. data/lib/legion/mcp/version.rb +1 -1
  95. data/lib/legion/mcp.rb +13 -0
  96. metadata +4 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ee9fe829857bd0a2546ea95daf827d33fe60084094e665a78f7fe78742064978
4
- data.tar.gz: cc57fd55347d9a541b382d7ca8a92d4a2d9094e674142e4336cfaadf46f4071c
3
+ metadata.gz: 30a63f2232747773e34419533d0b0ef00b6c10c396885a976b322702281f5062
4
+ data.tar.gz: f7191a38ce628ffc0c61a46010ce8c7f6ac4188c213c5462de449c9a118fe0c1
5
5
  SHA512:
6
- metadata.gz: 15839e1d0bda42420e4734eef4a550a8b8b163453ec68a5eaf75aeb33b1d931625a5da8b30449db7627d2965ad7306d2a2c00633f559895ee442da35e5d5cee4
7
- data.tar.gz: 77b1a31f1c5a24bc4c36b537500dfe849f9ff413a14de994032551d3161e52dee33729bd292ebb1a69fbe2625eeda682c985aa0c3937ed9d97ea30da7d0049d9
6
+ metadata.gz: 1bf54172fb099fb382a8d4921990b47ad005d545163656de251c2e025bc28dbcebc789752009298dc8cab87a539651b0ec56a440e1209d27cb619c66ea95e8dd
7
+ data.tar.gz: 246f2c244d3e4429017e5ccdf339d2b0a00033a8d49df8f652ab8ca52c7ed9b0e1f1d6eb1cf765971d7ab3cebd7f410ebb8733e42f840bad931b2e4397c55933
data/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.7.1] - 2026-04-02
6
+
7
+ ### Added
8
+ - `LoggingSupport` helper for structured MCP event logging with request, parameter, and result summarization
9
+
10
+ ### Changed
11
+ - Uplifted non-Sinatra `lib/**/*.rb` logging paths to use `Legion::Logging::Helper` instead of direct `Legion::Logging.*` calls
12
+ - Added `handle_exception` coverage across MCP tool, resource, router, client, observer, and pattern-store rescue paths
13
+ - Added `info`-level tracing for MCP tool/resource entrypoints and key client, routing, and pattern lifecycle actions
14
+ - Removed the custom fallback logger from `Actor::SelfGenerateCycle` in favor of the shared logging helper
15
+ - Added explicit `require 'legion/logging'` at the main MCP entrypoint
16
+ - Raised the minimum `legion-logging` dependency to `>= 1.4.3` for helper support
17
+
5
18
  ## [0.7.0] - 2026-03-31
6
19
 
7
20
  ### Added
data/legion-mcp.gemspec CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.add_dependency 'concurrent-ruby', '>= 1.2'
30
30
  spec.add_dependency 'legion-data', '>= 1.4.19'
31
31
  spec.add_dependency 'legion-json', '>= 1.2.0'
32
- spec.add_dependency 'legion-logging', '>= 1.2.8'
32
+ spec.add_dependency 'legion-logging', '>= 1.4.3'
33
33
  spec.add_dependency 'legion-settings', '>= 1.3.12'
34
34
  spec.add_dependency 'mcp', '~> 0.8'
35
35
  end
@@ -6,6 +6,8 @@ module Legion
6
6
  module MCP
7
7
  module Actor
8
8
  class SelfGenerateCycle < Legion::Extensions::Actors::Every
9
+ include Legion::Logging::Helper
10
+
9
11
  def runner_class = self.class
10
12
  def runner_function = 'action'
11
13
  def check_subtask? = false
@@ -18,6 +20,7 @@ module Legion
18
20
  300
19
21
  end
20
22
  rescue StandardError => e
23
+ handle_exception(e, level: :warn, operation: 'legion.mcp.actors.self_generate_cycle.time')
21
24
  log.warn(e.message)
22
25
  300
23
26
  end
@@ -25,6 +28,7 @@ module Legion
25
28
  def enabled?
26
29
  SelfGenerate.enabled?
27
30
  rescue StandardError => e
31
+ handle_exception(e, level: :warn, operation: 'legion.mcp.actors.self_generate_cycle.enabled?')
28
32
  log.warn(e.message)
29
33
  false
30
34
  end
@@ -32,16 +36,6 @@ module Legion
32
36
  def action(_payload = nil)
33
37
  SelfGenerate.run_cycle
34
38
  end
35
-
36
- private
37
-
38
- def log
39
- return Legion::Logging if defined?(Legion::Logging)
40
-
41
- @log ||= Object.new.tap do |nl|
42
- %i[debug info warn error fatal].each { |m| nl.define_singleton_method(m) { |*| nil } }
43
- end
44
- end
45
39
  end
46
40
  end
47
41
  end
@@ -3,9 +3,12 @@
3
3
  module Legion
4
4
  module MCP
5
5
  module Auth
6
+ extend Legion::Logging::Helper
7
+
6
8
  module_function
7
9
 
8
10
  def authenticate(token)
11
+ log.info('Starting legion.mcp.auth.authenticate')
9
12
  return { authenticated: false, error: 'missing_token' } unless token
10
13
 
11
14
  if jwt_token?(token)
@@ -34,7 +37,8 @@ module Legion
34
37
  { authenticated: true, identity: { user_id: claims[:sub], risk_tier: claims[:risk_tier]&.to_sym,
35
38
  tenant_id: claims[:tenant_id], worker_id: claims[:worker_id] } }
36
39
  rescue StandardError => e
37
- Legion::Logging.warn("Auth#verify_jwt failed: #{e.message}") if defined?(Legion::Logging)
40
+ handle_exception(e, level: :warn, operation: 'legion.mcp.auth.verify_jwt')
41
+ log.warn("Auth#verify_jwt failed: #{e.message}")
38
42
  { authenticated: false, error: e.message }
39
43
  end
40
44
 
@@ -3,6 +3,8 @@
3
3
  module Legion
4
4
  module MCP
5
5
  module CatalogBridge
6
+ include Legion::Logging::Helper
7
+
6
8
  def hydrate_override_confidence
7
9
  return unless defined?(Legion::LLM::OverrideConfidence)
8
10
  return unless Legion::LLM::OverrideConfidence.respond_to?(:hydrate_from_l2)
@@ -19,6 +21,7 @@ module Legion
19
21
  end
20
22
 
21
23
  def dispatch_catalog_tool(tool_name, arguments)
24
+ log.info('Starting legion.mcp.catalog_bridge.dispatch_catalog_tool')
22
25
  return nil unless defined?(Legion::Extensions::Catalog::Registry)
23
26
 
24
27
  cap = Legion::Extensions::Catalog::Registry.find_by_mcp_name(tool_name)
@@ -31,13 +34,16 @@ module Legion
31
34
  result = runner.send(fn, **(arguments || {}).transform_keys(&:to_sym))
32
35
  { status: :success, result: result, source: :catalog }
33
36
  rescue NameError => e
34
- Legion::Logging.warn("Catalog dispatch failed: #{e.message}") if defined?(Legion::Logging)
37
+ handle_exception(e, level: :warn, operation: 'legion.mcp.catalog_bridge.dispatch_catalog_tool')
38
+ log.warn("Catalog dispatch failed: #{e.message}")
35
39
  nil
36
40
  rescue StandardError => e
41
+ handle_exception(e, level: :error, operation: 'legion.mcp.catalog_bridge.dispatch_catalog_tool')
37
42
  { status: :error, error: e.message, source: :catalog }
38
43
  end
39
44
 
40
45
  def register_catalog_tools
46
+ log.info('Starting legion.mcp.catalog_bridge.register_catalog_tools')
41
47
  CatalogDispatcher.generate_tools_from_catalog.each { |tc| Server.register_tool(tc) }
42
48
  end
43
49
 
@@ -1,14 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'logging_support'
4
+
3
5
  module Legion
4
6
  module MCP
5
7
  module CatalogDispatcher
8
+ extend Legion::Logging::Helper
9
+
6
10
  module_function
7
11
 
8
- def dispatch(runner_class:, function:, params:, source: :mcp)
9
- return nil unless defined?(Legion::Ingress)
12
+ def dispatch(runner_class:, function:, params:, source: :mcp) # rubocop:disable Metrics/MethodLength
13
+ LoggingSupport.info(
14
+ 'catalog.dispatch.start',
15
+ runner_class: runner_class,
16
+ function: function,
17
+ source: source,
18
+ params: LoggingSupport.summarize_params(params)
19
+ )
20
+ unless defined?(Legion::Ingress)
21
+ LoggingSupport.warn(
22
+ 'catalog.dispatch.skipped',
23
+ runner_class: runner_class,
24
+ function: function,
25
+ reason: 'ingress unavailable'
26
+ )
27
+ return nil
28
+ end
10
29
 
11
- Legion::Ingress.run(
30
+ result = Legion::Ingress.run(
12
31
  payload: params,
13
32
  runner_class: runner_class,
14
33
  function: function.to_sym,
@@ -16,6 +35,14 @@ module Legion
16
35
  check_subtask: true,
17
36
  generate_task: true
18
37
  )
38
+ LoggingSupport.info(
39
+ 'catalog.dispatch.complete',
40
+ runner_class: runner_class,
41
+ function: function,
42
+ source: source,
43
+ result: LoggingSupport.summarize_result(result)
44
+ )
45
+ result
19
46
  end
20
47
 
21
48
  def build_tool_class(entry)
@@ -35,13 +62,21 @@ module Legion
35
62
  define_singleton_method(:mcp_tier) { tier }
36
63
  define_singleton_method(:catalog_entry) { true }
37
64
  end
65
+ klass.extend(Legion::Logging::Helper)
38
66
 
39
- wire_dispatch(klass, runner_class_str, function_name)
67
+ wire_dispatch(klass, runner_class_str, function_name, tool_name_val)
40
68
  klass
41
69
  end
42
70
 
43
- def wire_dispatch(klass, runner_class_str, function_name)
44
- klass.define_singleton_method(:call) do |**params|
71
+ def wire_dispatch(klass, runner_class_str, function_name, tool_name_val) # rubocop:disable Metrics/MethodLength
72
+ klass.define_singleton_method(:call) do |**params| # rubocop:disable Metrics/BlockLength
73
+ LoggingSupport.info(
74
+ 'catalog.tool_call.start',
75
+ tool_name: tool_name_val,
76
+ runner_class: runner_class_str,
77
+ function: function_name,
78
+ params: LoggingSupport.summarize_params(params)
79
+ )
45
80
  result = CatalogDispatcher.dispatch(
46
81
  runner_class: runner_class_str,
47
82
  function: function_name,
@@ -53,10 +88,25 @@ module Legion
53
88
  ::MCP::Tool::Response.new([{ type: 'text', text: text }], error: true)
54
89
  else
55
90
  text = defined?(Legion::JSON) ? Legion::JSON.dump(result) : result.to_s
56
- ::MCP::Tool::Response.new([{ type: 'text', text: text }])
91
+ response = ::MCP::Tool::Response.new([{ type: 'text', text: text }])
92
+ LoggingSupport.info(
93
+ 'catalog.tool_call.complete',
94
+ tool_name: tool_name_val,
95
+ runner_class: runner_class_str,
96
+ function: function_name,
97
+ result: LoggingSupport.summarize_result(response)
98
+ )
99
+ response
57
100
  end
58
101
  rescue StandardError => e
59
- Legion::Logging.warn("CatalogDispatcher: #{function_name} failed: #{e.message}") if defined?(Legion::Logging)
102
+ handle_exception(e, level: :warn, operation: 'legion.mcp.catalog_dispatcher.call')
103
+ LoggingSupport.warn(
104
+ 'catalog.tool_call.failed',
105
+ tool_name: tool_name_val,
106
+ runner_class: runner_class_str,
107
+ function: function_name,
108
+ error: e.message
109
+ )
60
110
  text = Legion::JSON.dump({ error: e.message })
61
111
  ::MCP::Tool::Response.new([{ type: 'text', text: text }], error: true)
62
112
  end
@@ -77,7 +127,8 @@ module Legion
77
127
  tier: cap.respond_to?(:tier) ? cap.tier : nil
78
128
  )
79
129
  rescue StandardError => e
80
- Legion::Logging.debug("CatalogDispatcher: skipping #{cap}: #{e.message}") if defined?(Legion::Logging)
130
+ handle_exception(e, level: :debug, operation: 'legion.mcp.catalog_dispatcher.generate_tools_from_catalog')
131
+ log.debug("CatalogDispatcher: skipping #{cap}: #{e.message}")
81
132
  nil
82
133
  end
83
134
  end
@@ -1,9 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../logging_support'
4
+
3
5
  module Legion
4
6
  module MCP
5
7
  module Client
6
- class Connection
8
+ class Connection # rubocop:disable Metrics/ClassLength
9
+ include Legion::Logging::Helper
10
+
7
11
  attr_reader :name, :transport_type, :config
8
12
 
9
13
  TOOL_CACHE_TTL = 300 # seconds
@@ -26,6 +30,12 @@ module Legion
26
30
  @mutex.synchronize do
27
31
  return if @connected
28
32
 
33
+ LoggingSupport.info(
34
+ 'client.connect.start',
35
+ connection: @name,
36
+ transport: @transport_type,
37
+ config: @config.slice(:url, :command)
38
+ )
29
39
  case @transport_type
30
40
  when :stdio
31
41
  connect_stdio
@@ -35,9 +45,15 @@ module Legion
35
45
  raise ArgumentError, "Unknown transport: #{@transport_type}"
36
46
  end
37
47
  @connected = true
48
+ LoggingSupport.info(
49
+ 'client.connect.complete',
50
+ connection: @name,
51
+ transport: @transport_type
52
+ )
38
53
  end
39
- rescue StandardError
54
+ rescue StandardError => e
40
55
  @connected = false
56
+ handle_exception(e, level: :error, operation: 'legion.mcp.client.connection.connect')
41
57
  raise
42
58
  end
43
59
 
@@ -53,18 +69,61 @@ module Legion
53
69
  @mutex.synchronize do
54
70
  if !force_refresh && @tools_cache && @tools_cached_at &&
55
71
  (Time.now - @tools_cached_at) < TOOL_CACHE_TTL
72
+ LoggingSupport.info(
73
+ 'client.tools.cache_hit',
74
+ connection: @name,
75
+ transport: @transport_type,
76
+ count: @tools_cache.size
77
+ )
56
78
  return @tools_cache
57
79
  end
58
80
 
81
+ LoggingSupport.info(
82
+ 'client.tools.fetch.start',
83
+ connection: @name,
84
+ transport: @transport_type,
85
+ force_refresh: force_refresh
86
+ )
59
87
  @tools_cache = fetch_tools
60
88
  @tools_cached_at = Time.now
89
+ LoggingSupport.info(
90
+ 'client.tools.fetch.complete',
91
+ connection: @name,
92
+ transport: @transport_type,
93
+ count: @tools_cache.size
94
+ )
61
95
  @tools_cache
62
96
  end
63
97
  end
64
98
 
65
99
  def call_tool(name:, arguments: {})
66
100
  connect unless connected?
67
- execute_tool_call(name: name, arguments: arguments)
101
+ LoggingSupport.info(
102
+ 'client.tool_call.start',
103
+ connection: @name,
104
+ transport: @transport_type,
105
+ tool_name: name,
106
+ arguments: LoggingSupport.summarize_params(arguments)
107
+ )
108
+ result = execute_tool_call(name: name, arguments: arguments)
109
+ LoggingSupport.info(
110
+ 'client.tool_call.complete',
111
+ connection: @name,
112
+ transport: @transport_type,
113
+ tool_name: name,
114
+ result: LoggingSupport.summarize_result(result)
115
+ )
116
+ result
117
+ rescue StandardError => e
118
+ handle_exception(e, level: :warn, operation: 'legion.mcp.client.connection.call_tool')
119
+ LoggingSupport.warn(
120
+ 'client.tool_call.failed',
121
+ connection: @name,
122
+ transport: @transport_type,
123
+ tool_name: name,
124
+ error: e.message
125
+ )
126
+ raise
68
127
  end
69
128
 
70
129
  private
@@ -7,6 +7,8 @@ module Legion
7
7
  @connections = {}
8
8
  @mutex = Mutex.new
9
9
 
10
+ extend Legion::Logging::Helper
11
+
10
12
  module_function
11
13
 
12
14
  def connection_for(server_name)
@@ -30,7 +32,8 @@ module Legion
30
32
  tool.merge(source: { type: :mcp, server: name })
31
33
  end
32
34
  rescue StandardError => e
33
- Legion::Logging.warn("MCP tool discovery failed for #{name}: #{e.message}") if defined?(Legion::Logging)
35
+ handle_exception(e, level: :warn, operation: 'legion.mcp.client.pool.all_tools')
36
+ log.warn("MCP tool discovery failed for #{name}: #{e.message}")
34
37
  ServerRegistry.mark_unhealthy(name)
35
38
  []
36
39
  end
@@ -38,7 +41,11 @@ module Legion
38
41
 
39
42
  def reset!
40
43
  @mutex.synchronize do
41
- @connections.each_value { |c| c.disconnect rescue nil } # rubocop:disable Style/RescueModifier
44
+ @connections.each_value do |connection|
45
+ connection.disconnect
46
+ rescue StandardError => e
47
+ handle_exception(e, level: :debug, operation: 'legion.mcp.client.pool.reset!')
48
+ end
42
49
  @connections.clear
43
50
  end
44
51
  end
@@ -3,14 +3,22 @@
3
3
  module Legion
4
4
  module MCP
5
5
  module Client
6
+ extend Legion::Logging::Helper
7
+
6
8
  module_function
7
9
 
8
10
  def boot
9
- servers = Legion::Settings.dig(:mcp, :servers) rescue nil # rubocop:disable Style/RescueModifier
11
+ log.info('Starting legion.mcp.client.boot')
12
+ servers = begin
13
+ Legion::Settings.dig(:mcp, :servers)
14
+ rescue StandardError => e
15
+ handle_exception(e, level: :debug, operation: 'legion.mcp.client.boot')
16
+ nil
17
+ end
10
18
  return unless servers.is_a?(Hash) && servers.any?
11
19
 
12
20
  ServerRegistry.load_from_settings(servers)
13
- Legion::Logging.info("MCP Client: #{servers.length} servers registered") if defined?(Legion::Logging)
21
+ log.info("MCP Client: #{servers.length} servers registered")
14
22
  end
15
23
 
16
24
  def shutdown
@@ -6,9 +6,12 @@ require_relative 'pattern_exchange'
6
6
  module Legion
7
7
  module MCP
8
8
  module ColdStart
9
+ extend Legion::Logging::Helper
10
+
9
11
  module_function
10
12
 
11
13
  def load_community_patterns(path: nil)
14
+ log.info('Starting legion.mcp.cold_start.load_community_patterns')
12
15
  return { skipped: true, reason: 'store not empty' } unless PatternStore.empty?
13
16
 
14
17
  path ||= configured_path
@@ -16,7 +19,8 @@ module Legion
16
19
 
17
20
  PatternExchange.import_from_file(path, trust_level: :community)
18
21
  rescue StandardError => e
19
- Legion::Logging.error("ColdStart#load_community_patterns failed: #{e.message}") if defined?(Legion::Logging)
22
+ handle_exception(e, level: :error, operation: 'legion.mcp.cold_start.load_community_patterns')
23
+ log.error("ColdStart#load_community_patterns failed: #{e.message}")
20
24
  { error: e.message, imported: 0 }
21
25
  end
22
26
 
@@ -25,7 +29,8 @@ module Legion
25
29
 
26
30
  Legion::Settings.dig(:mcp, :cold_start, :patterns_path)
27
31
  rescue StandardError => e
28
- Legion::Logging.warn("ColdStart#configured_path failed: #{e.message}") if defined?(Legion::Logging)
32
+ handle_exception(e, level: :warn, operation: 'legion.mcp.cold_start.configured_path')
33
+ log.warn("ColdStart#configured_path failed: #{e.message}")
29
34
  nil
30
35
  end
31
36
  end
@@ -8,6 +8,8 @@ module Legion
8
8
  DEFAULT_RAPID_FIRE_WINDOW_SECS = 600
9
9
  DEFAULT_ANOMALY_MISS_THRESHOLD = 2
10
10
 
11
+ extend Legion::Logging::Helper
12
+
11
13
  module_function
12
14
 
13
15
  def check(pattern, _params, _context)
@@ -76,7 +78,8 @@ module Legion
76
78
 
77
79
  Legion::Settings.dig(:mcp, :tier0, :guards, key)
78
80
  rescue StandardError => e
79
- Legion::Logging.warn("ContextGuard#setting failed for key #{key}: #{e.message}") if defined?(Legion::Logging)
81
+ handle_exception(e, level: :warn, operation: 'legion.mcp.context_guard.setting')
82
+ log.warn("ContextGuard#setting failed for key #{key}: #{e.message}")
80
83
  nil
81
84
  end
82
85
 
@@ -5,6 +5,8 @@ module Legion
5
5
  module DynamicInjector
6
6
  MAX_INJECTED = 10
7
7
 
8
+ extend Legion::Logging::Helper
9
+
8
10
  module_function
9
11
 
10
12
  def enabled?
@@ -52,7 +54,8 @@ module Legion
52
54
 
53
55
  server.notify_tools_list_changed
54
56
  rescue StandardError => e
55
- Legion::Logging.debug("DynamicInjector: notify failed: #{e.message}") if defined?(Legion::Logging)
57
+ handle_exception(e, level: :debug, operation: 'legion.mcp.dynamic_injector.notify_if_changed')
58
+ log.debug("DynamicInjector: notify failed: #{e.message}")
56
59
  end
57
60
 
58
61
  def inject_for_context(server, intent_string, previous_names: [])
@@ -3,6 +3,8 @@
3
3
  module Legion
4
4
  module MCP
5
5
  module EmbeddingIndex
6
+ extend Legion::Logging::Helper
7
+
6
8
  module_function
7
9
 
8
10
  def build_from_tool_data(tool_data, embedder: default_embedder)
@@ -98,7 +100,8 @@ module Legion
98
100
 
99
101
  result
100
102
  rescue StandardError => e
101
- Legion::Logging.debug("EmbeddingIndex#safe_embed failed: #{e.message}") if defined?(Legion::Logging)
103
+ handle_exception(e, level: :debug, operation: 'legion.mcp.embedding_index.safe_embed')
104
+ log.debug("EmbeddingIndex#safe_embed failed: #{e.message}")
102
105
  nil
103
106
  end
104
107
 
@@ -107,7 +110,8 @@ module Legion
107
110
 
108
111
  ->(text) { Legion::LLM.embed(text)[:vector] }
109
112
  rescue StandardError => e
110
- Legion::Logging.debug("EmbeddingIndex#default_embedder failed: #{e.message}") if defined?(Legion::Logging)
113
+ handle_exception(e, level: :debug, operation: 'legion.mcp.embedding_index.default_embedder')
114
+ log.debug("EmbeddingIndex#default_embedder failed: #{e.message}")
111
115
  nil
112
116
  end
113
117
  end
@@ -3,6 +3,8 @@
3
3
  module Legion
4
4
  module MCP
5
5
  module FunctionDiscovery
6
+ extend Legion::Logging::Helper
7
+
6
8
  module_function
7
9
 
8
10
  def discover_and_register
@@ -19,7 +21,8 @@ module Legion
19
21
 
20
22
  ext.runner_modules.each { |runner_mod| build_tools_from_runner(runner_mod) }
21
23
  rescue StandardError => e
22
- Legion::Logging.debug("FunctionDiscovery: skipping #{ext}: #{e.message}") if defined?(Legion::Logging)
24
+ handle_exception(e, level: :debug, operation: 'legion.mcp.function_discovery.discover_and_register')
25
+ log.debug("FunctionDiscovery: skipping #{ext}: #{e.message}")
23
26
  end
24
27
  end
25
28
 
@@ -141,6 +144,7 @@ module Legion
141
144
  begin
142
145
  runner_ref.public_send(func_ref, **params)
143
146
  rescue StandardError => e
147
+ handle_exception(e, level: :warn, operation: 'legion.mcp.function_discovery.call')
144
148
  error = true
145
149
  { error: e.message }
146
150
  end