legion-mcp 0.8.1 → 0.9.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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +43 -0
  4. data/CLAUDE.md +34 -42
  5. data/README.md +169 -45
  6. data/legion-mcp.gemspec +1 -1
  7. data/lib/legion/mcp/actors/self_generate_cycle.rb +3 -5
  8. data/lib/legion/mcp/audit.rb +50 -0
  9. data/lib/legion/mcp/auth.rb +70 -11
  10. data/lib/legion/mcp/catalog_dispatcher.rb +29 -50
  11. data/lib/legion/mcp/client/connection.rb +163 -59
  12. data/lib/legion/mcp/client/pool.rb +40 -5
  13. data/lib/legion/mcp/client/server_registry.rb +7 -0
  14. data/lib/legion/mcp/client.rb +4 -1
  15. data/lib/legion/mcp/cold_start.rb +7 -9
  16. data/lib/legion/mcp/context_compiler.rb +6 -1
  17. data/lib/legion/mcp/context_guard.rb +1 -3
  18. data/lib/legion/mcp/deferred_registry.rb +15 -5
  19. data/lib/legion/mcp/discovery.rb +18 -0
  20. data/lib/legion/mcp/dynamic_injector.rb +4 -1
  21. data/lib/legion/mcp/embedding_index.rb +5 -3
  22. data/lib/legion/mcp/function_discovery.rb +46 -6
  23. data/lib/legion/mcp/gap_detector.rb +8 -3
  24. data/lib/legion/mcp/observer.rb +11 -9
  25. data/lib/legion/mcp/override_broadcast.rb +4 -3
  26. data/lib/legion/mcp/patterns/compiler.rb +59 -0
  27. data/lib/legion/mcp/patterns/exchange.rb +63 -0
  28. data/lib/legion/mcp/patterns/gossip.rb +68 -0
  29. data/lib/legion/mcp/patterns/schema.rb +92 -0
  30. data/lib/legion/mcp/patterns/store.rb +469 -0
  31. data/lib/legion/mcp/patterns.rb +25 -0
  32. data/lib/legion/mcp/resources/extension_info.rb +1 -3
  33. data/lib/legion/mcp/resources/runner_catalog.rb +24 -3
  34. data/lib/legion/mcp/self_generate.rb +6 -9
  35. data/lib/legion/mcp/server.rb +185 -84
  36. data/lib/legion/mcp/state_tracker.rb +4 -4
  37. data/lib/legion/mcp/structural_index.rb +6 -4
  38. data/lib/legion/mcp/tier_router.rb +55 -95
  39. data/lib/legion/mcp/tool_adapter.rb +101 -1
  40. data/lib/legion/mcp/tool_governance.rb +71 -5
  41. data/lib/legion/mcp/tool_quality.rb +3 -0
  42. data/lib/legion/mcp/tools/create_chain.rb +1 -3
  43. data/lib/legion/mcp/tools/create_relationship.rb +1 -3
  44. data/lib/legion/mcp/tools/create_schedule.rb +0 -2
  45. data/lib/legion/mcp/tools/delete_chain.rb +1 -3
  46. data/lib/legion/mcp/tools/delete_relationship.rb +1 -3
  47. data/lib/legion/mcp/tools/delete_schedule.rb +0 -2
  48. data/lib/legion/mcp/tools/delete_task.rb +0 -2
  49. data/lib/legion/mcp/tools/describe_runner.rb +0 -2
  50. data/lib/legion/mcp/tools/discover_tools.rb +2 -2
  51. data/lib/legion/mcp/tools/do_action.rb +102 -116
  52. data/lib/legion/mcp/tools/get_task.rb +0 -2
  53. data/lib/legion/mcp/tools/get_task_logs.rb +0 -2
  54. data/lib/legion/mcp/tools/list_chains.rb +1 -3
  55. data/lib/legion/mcp/tools/list_relationships.rb +0 -2
  56. data/lib/legion/mcp/tools/list_schedules.rb +0 -2
  57. data/lib/legion/mcp/tools/list_tasks.rb +0 -2
  58. data/lib/legion/mcp/tools/plan_action.rb +0 -2
  59. data/lib/legion/mcp/tools/run_task.rb +0 -2
  60. data/lib/legion/mcp/tools/search_sessions.rb +1 -2
  61. data/lib/legion/mcp/tools/skills.rb +2 -4
  62. data/lib/legion/mcp/tools/state_diff.rb +0 -1
  63. data/lib/legion/mcp/tools/structural_index.rb +0 -1
  64. data/lib/legion/mcp/tools/tool_audit.rb +0 -1
  65. data/lib/legion/mcp/tools/update_chain.rb +1 -3
  66. data/lib/legion/mcp/tools/update_relationship.rb +1 -3
  67. data/lib/legion/mcp/tools/update_schedule.rb +0 -2
  68. data/lib/legion/mcp/tools_loader.rb +13 -50
  69. data/lib/legion/mcp/tracing_context.rb +58 -0
  70. data/lib/legion/mcp/transport/exchanges/audit.rb +15 -0
  71. data/lib/legion/mcp/transport/messages/client_call_event.rb +23 -0
  72. data/lib/legion/mcp/transport/messages/governance_event.rb +23 -0
  73. data/lib/legion/mcp/transport/messages/tool_call_event.rb +23 -0
  74. data/lib/legion/mcp/usage_filter.rb +6 -1
  75. data/lib/legion/mcp/{logging_support.rb → utils.rb} +3 -29
  76. data/lib/legion/mcp/version.rb +1 -1
  77. data/lib/legion/mcp.rb +27 -5
  78. metadata +17 -48
  79. data/lib/legion/mcp/pattern_compiler.rb +0 -55
  80. data/lib/legion/mcp/pattern_exchange.rb +0 -61
  81. data/lib/legion/mcp/pattern_gossip.rb +0 -68
  82. data/lib/legion/mcp/pattern_schema.rb +0 -80
  83. data/lib/legion/mcp/pattern_store.rb +0 -492
  84. data/lib/legion/mcp/tools/absorb.rb +0 -64
  85. data/lib/legion/mcp/tools/ask_peer.rb +0 -61
  86. data/lib/legion/mcp/tools/broadcast_peers.rb +0 -60
  87. data/lib/legion/mcp/tools/dataset_list.rb +0 -55
  88. data/lib/legion/mcp/tools/dataset_show.rb +0 -61
  89. data/lib/legion/mcp/tools/disable_extension.rb +0 -57
  90. data/lib/legion/mcp/tools/enable_extension.rb +0 -57
  91. data/lib/legion/mcp/tools/eval_list.rb +0 -55
  92. data/lib/legion/mcp/tools/eval_results.rb +0 -86
  93. data/lib/legion/mcp/tools/eval_run.rb +0 -64
  94. data/lib/legion/mcp/tools/experiment_results.rb +0 -86
  95. data/lib/legion/mcp/tools/get_config.rb +0 -68
  96. data/lib/legion/mcp/tools/get_extension.rb +0 -63
  97. data/lib/legion/mcp/tools/get_status.rb +0 -61
  98. data/lib/legion/mcp/tools/knowledge_context.rb +0 -108
  99. data/lib/legion/mcp/tools/knowledge_health.rb +0 -62
  100. data/lib/legion/mcp/tools/list_extensions.rb +0 -53
  101. data/lib/legion/mcp/tools/list_peers.rb +0 -52
  102. data/lib/legion/mcp/tools/list_workers.rb +0 -61
  103. data/lib/legion/mcp/tools/mesh_status.rb +0 -48
  104. data/lib/legion/mcp/tools/mind_growth_approve.rb +0 -53
  105. data/lib/legion/mcp/tools/mind_growth_build_queue.rb +0 -48
  106. data/lib/legion/mcp/tools/mind_growth_cognitive_profile.rb +0 -48
  107. data/lib/legion/mcp/tools/mind_growth_health.rb +0 -48
  108. data/lib/legion/mcp/tools/mind_growth_propose.rb +0 -60
  109. data/lib/legion/mcp/tools/mind_growth_status.rb +0 -48
  110. data/lib/legion/mcp/tools/notify_peer.rb +0 -59
  111. data/lib/legion/mcp/tools/prompt_list.rb +0 -55
  112. data/lib/legion/mcp/tools/prompt_run.rb +0 -66
  113. data/lib/legion/mcp/tools/prompt_show.rb +0 -62
  114. data/lib/legion/mcp/tools/query_knowledge.rb +0 -56
  115. data/lib/legion/mcp/tools/rbac_assignments.rb +0 -50
  116. data/lib/legion/mcp/tools/rbac_check.rb +0 -51
  117. data/lib/legion/mcp/tools/rbac_grants.rb +0 -46
  118. data/lib/legion/mcp/tools/routing_stats.rb +0 -58
  119. data/lib/legion/mcp/tools/show_worker.rb +0 -55
  120. data/lib/legion/mcp/tools/team_summary.rb +0 -62
  121. data/lib/legion/mcp/tools/worker_costs.rb +0 -62
  122. data/lib/legion/mcp/tools/worker_lifecycle.rb +0 -63
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dbc548bee22e23b6829490dcb7df334ae19c470f4f0d5d9148831b125083e323
4
- data.tar.gz: 56bc136cb3b5801dce2d7fed3ba947ff9de1abd64c3dd996caa67cdffca49cd1
3
+ metadata.gz: 79ba0ac270a1e93bfb9558d37194307398336a43d759c7a9457daa28ca2e01a6
4
+ data.tar.gz: d97d87371fe9b4127d07a907abbd3b623015ce58be95425229eb17f9ad1885d6
5
5
  SHA512:
6
- metadata.gz: 62db181395833185ab2e9b11b7ef3281a2e49761aab8d61feabce4fd19389fe768af86b26d0e24501ebb0ca2237ec87bd820ca18417b4eda3e77921fade69508
7
- data.tar.gz: f5f1429b9cd74bcfaaf61083a38c5c5245644beee9e6ae3910ccb947f0f6809b8e9ef664bdce3d6a4ad3b9ec493788d92bb587c0d53bde7b75d7bed3b5a0fef0
6
+ metadata.gz: 633cef5680447266ec2276df835d4f571f3d3d7ece699c857e26cf2f2208d1823d655d2c9d8ecdf3a0431beaea0761d6c0fa0752fe92e134c7d82bede4f62c79
7
+ data.tar.gz: 66ccd7b86d37ecb681c1fe83dd719c2fff4daa45a6b67bf4c9cd8eef5d51aa444068d8cb6f4edf24bf7271cdadb6eced92cc91eccaabe4326c6bc92b8cef1b54
data/.gitignore CHANGED
@@ -5,3 +5,4 @@ pkg/
5
5
  .bundle/
6
6
  vendor/
7
7
  .worktrees
8
+ tmp/
data/CHANGELOG.md CHANGED
@@ -1,5 +1,48 @@
1
1
  # legion-mcp Changelog
2
2
 
3
+ ## [0.9.1] - 2026-04-30
4
+
5
+ ### Fixed
6
+ - `Audit.transport_connected?` rescue block now calls `handle_exception` at `:debug` level instead of silently swallowing the error
7
+ - `ToolAdapter.dispatch_tool_instance` `rescue ArgumentError` (keyword-to-positional fallback) now calls `handle_exception` at `:debug` level
8
+ - `SkillInvoke.invoke_skill` cleanup rescue now calls `handle_exception` at `:warn` level before re-raising
9
+ - `Client::Connection.verify_connection!` rescue now calls `handle_exception` at `:error` level before re-raising as `ConnectionError`
10
+ - `Client::Connection.fetch_tools` rescue now calls `handle_exception` at `:warn` level before re-raising as `ConnectionError`
11
+ - `Client::Connection.execute_tool_call` `RequestHandlerError` rescue now calls `handle_exception` at `:warn` level before re-raising as `ConnectionError`
12
+
13
+ ### Changed
14
+ - Added debug-level logging to `Client.register`, `Client.deregister`, `ServerRegistry.deregister`, and `ServerRegistry.mark_healthy` for full action traceability
15
+ - Updated README.md to reflect 0.9.x architecture: DeferredRegistry, Client pool, Settings::Extensions integration, correct version, dependency floors, and file map
16
+
17
+ ## [0.9.0] - 2026-04-29
18
+
19
+ ### Removed
20
+ - 38 hardcoded tool files that duplicated dynamic extension discovery (extensions, workers, RBAC, status, config, prompts, datasets, evals, mind-growth, knowledge, mesh, absorb) — these are now auto-discovered via `Settings::Extensions`
21
+ - `LoggingSupport` module — replaced by direct `Legion::Logging::Helper` + `Utils` module
22
+
23
+ ### Changed
24
+ - `FunctionDiscovery.discover_and_register` now prefers reading tools from `Legion::Settings::Extensions` (the centralized registry in `legion-settings`) when available and populated, falling back to existing `Legion::Tools::Discovery` and runner-module discovery paths for backward compatibility
25
+ - `ToolAdapter.from_registry_entry` builds MCP tool classes from registry entry hashes; delegates to `from_legion_tool` when the entry contains a loaded tool class, otherwise builds a thin metadata-driven adapter
26
+ - `ToolAdapter.build_from_metadata` handles `:mcp_remote` dispatch type by proxying calls through `MCP::Client::Pool` instead of dispatching to a local tool class
27
+ - `Resources::RunnerCatalog#catalog_json` reads from `Settings::Extensions.runners` when available, falling back to the existing `legion-data` database query path
28
+ - `tools_loader.rb` now requires only 8 MCP-specific and 18 legion-data CRUD tool files (down from 65)
29
+ - All logging in server, catalog_dispatcher, observer, pattern_store, tier_router, do_action, and client/connection migrated from `LoggingSupport` to direct `log.*` calls with `Utils.format_fields`
30
+ - Bumped `legion-settings` dependency floor to `>= 1.4.0` (requires `Settings::Extensions` module)
31
+
32
+ ### Added
33
+ - `Utils` module (`lib/legion/mcp/utils.rb`) — pure-function summarization and formatting helpers extracted from `LoggingSupport`
34
+ - `Client::Pool.refresh_tools!` — re-fetches and re-registers all remote server tools
35
+ - `Client::Pool.all_tools` now registers each remote tool into `Settings::Extensions` with `dispatch_type: :mcp_remote`
36
+ - `Server.register_mcp_tools_in_settings_extensions` — registers `MCP_SPECIFIC_TOOLS` into `Settings::Extensions` with `dispatch_type: :class_call` after build
37
+ - `patterns.rb` barrel file — requires all 13 Tier 0 pattern routing modules
38
+ - `discovery.rb` barrel file — requires all 12 tool discovery and adaptation modules
39
+ - `FunctionDiscovery.settings_extensions_available?` — guard method checking if `Settings::Extensions` is defined and populated
40
+ - `FunctionDiscovery.register_from_settings_extensions` — registers tools from the centralized registry into the MCP server
41
+ - `ToolAdapter.from_registry_entry` — factory method to build MCP tools from registry entry hashes
42
+ - `ToolAdapter.build_from_metadata` — builds thin MCP tool adapters from metadata when no tool class is loaded
43
+ - `RunnerCatalog#settings_extensions_runners_available?` — guard for registry-based runner catalog
44
+ - `RunnerCatalog#catalog_from_settings_extensions` — builds runner catalog JSON from the centralized registry
45
+
3
46
  ## [0.8.1] - 2026-04-14
4
47
 
5
48
  ### Fixed
data/CLAUDE.md CHANGED
@@ -7,7 +7,7 @@
7
7
  Standalone gem providing the Model Context Protocol (MCP) server for LegionIO. Extracted from LegionIO to enable independent versioning and reuse. Includes semantic tool matching, observation pipeline, context compilation, tiered inference (Tier 0/1/2), and tool governance.
8
8
 
9
9
  **GitHub**: https://github.com/LegionIO/legion-mcp
10
- **Version**: 0.7.4
10
+ **Version**: 0.9.0
11
11
  **License**: Apache-2.0
12
12
  **Ruby**: >= 3.4
13
13
 
@@ -15,29 +15,27 @@ Standalone gem providing the Model Context Protocol (MCP) server for LegionIO. E
15
15
 
16
16
  ```
17
17
  Legion::MCP
18
- ├── Server # MCP::Server builder, governance-aware build; tool list sourced from Legion::Tools::Registry via DeferredRegistry
18
+ ├── Server # MCP::Server builder, governance-aware build; tool list sourced from Legion::Settings::Extensions via DeferredRegistry
19
19
  ├── Auth # JWT + API key authentication
20
+ ├── Utils # Pure-function summarization and formatting helpers (extracted from LoggingSupport)
20
21
  ├── ToolGovernance # Risk-tier tool filtering + invocation audit
21
- ├── ContextCompiler # Keyword + semantic tool matching, blended scoring (60% semantic + 40% keyword)
22
- ├── EmbeddingIndex # Semantic tool matching; delegates embedding persistence to Tools::EmbeddingCache (L0-L4)
23
- ├── Observer # Instrumentation pipeline: counters, ring buffer, pattern promotion
24
- ├── UsageFilter # Frequency/recency/keyword scoring for dynamic tool filtering
25
- ├── PatternStore # 4-layer degrading storage (L0 memory, L1 cache, L2 local SQLite)
26
- ├── TierRouter # Confidence-gated tier selection (Tier 0/1/2)
27
- ├── ContextGuard # Staleness, rapid-fire, anomaly detection guards
28
- ├── ToolAdapter # Adapts Legion::Tools::Base subclasses to MCP SDK format (McpToolAdapter kept as alias)
29
- ├── DeferredRegistry # Reads deferred tools from Legion::Tools::Registry at request time
30
- ├── Tools/ # MCP_SPECIFIC_TOOLS only (6 registered); remaining tool files exist but are not registered in Server.tool_registry — extension tools discovered via Legion::Tools::Discovery
22
+ ├── ToolAdapter # Adapts Legion::Tools::Base to MCP SDK format; handles :mcp_remote dispatch
23
+ ├── DeferredRegistry # Reads deferred/always-loaded tools from Legion::Settings::Extensions at request time
24
+ ├── Client::Pool # Remote MCP server connections; registers tools into Settings::Extensions
25
+ ├── Client::Connection # stdio and HTTP transport connections with TTL-cached tool lists
26
+ ├── Client::ServerRegistry # Static and dynamic server registration with health tracking
27
+ ├── patterns.rb # Barrel: PatternStore, TierRouter, ContextGuard, Observer, etc. (13 modules)
28
+ ├── discovery.rb # Barrel: FunctionDiscovery, ToolAdapter, ToolGovernance, etc. (12 modules)
29
+ ├── Tools/ # MCP_SPECIFIC_TOOLS (10) + legion-data CRUD tools (18); extension tools via Settings::Extensions
31
30
  └── Resources/ # RunnerCatalog, ExtensionInfo
32
31
  ```
33
32
 
34
- ### Tool Registry Migration Notes
33
+ ### Tool Registry
35
34
 
36
- - **Before**: legion-mcp owned 57+ individual `Tools/*.rb` files registered in `TOOL_CLASSES`.
37
- - **After**: Tools discovered dynamically via `Legion::Tools::Discovery` from extension `runner_modules` at boot. `Legion::Tools::Registry` classifies each as `:always` or `:deferred`. `DeferredRegistry` resolves the deferred set at request time.
38
- - `MCP_SPECIFIC_TOOLS` (6 tools) covers MCP-only concerns not owned by any extension.
39
- - `CatalogBridge` removed bridged old `Extensions::Capability` / `Catalog::Registry` which no longer exist.
40
- - `EmbeddingIndex` uses `Legion::Tools::EmbeddingCache` (5-tier L0–L4) instead of its own in-memory store.
35
+ - **MCP_SPECIFIC_TOOLS** (10 tools): do_action, discover_tools, plan_action, structural_index, tool_audit, state_diff, search_sessions, skill_list, skill_describe, skill_invoke, skill_cancel
36
+ - **legion-data CRUD** (18 tools): run_task, describe_runner, list/get/delete tasks, get_task_logs, CRUD chains/relationships/schedules
37
+ - **Extension tools**: Auto-discovered via `Legion::Settings::Extensions` at runtime; not shipped as static files
38
+ - **Remote MCP tools**: Fetched from remote servers via `Client::Pool.all_tools`, registered into `Settings::Extensions` with `dispatch_type: :mcp_remote`
41
39
 
42
40
  ## Dependencies
43
41
 
@@ -46,8 +44,8 @@ Legion::MCP
46
44
  | `mcp` (~> 0.8) | Yes | MCP server SDK |
47
45
  | `legion-data` (>= 1.4) | Yes | Sequel models, migrations |
48
46
  | `legion-json` (>= 1.2) | Yes | JSON serialization |
49
- | `legion-logging` (>= 0.3) | Yes | Logging |
50
- | `legion-settings` (>= 0.3) | Yes | Configuration |
47
+ | `legion-logging` (>= 1.4.3) | Yes | Logging via Helper |
48
+ | `legion-settings` (>= 1.4.0) | Yes | Configuration + Settings::Extensions |
51
49
  | `legion-cache` | Optional | L1 pattern cache (memcached/redis) |
52
50
  | `legion-llm` | Optional | Embeddings for semantic matching |
53
51
  | `legionio` | Dev only | Full framework for integration testing |
@@ -58,6 +56,7 @@ All optional dependencies use `defined?()` guards:
58
56
  - `defined?(Legion::Cache)` for L1 cache operations
59
57
  - `defined?(Legion::Data::Local)` for L2 SQLite persistence
60
58
  - `defined?(Legion::LLM)` for embedding generation
59
+ - `defined?(Legion::Settings::Extensions)` for central tool registry
61
60
  - `defined?(Legion::MCP::EmbeddingIndex)` for semantic matching
62
61
  - `defined?(Legion::MCP::TierRouter)` for Tier 0 routing
63
62
  - Every storage write wraps in `begin/rescue => nil` -- failed persistence never blocks Tier 0
@@ -68,6 +67,7 @@ All optional dependencies use `defined?()` guards:
68
67
  - **Tier routing**: Tier 0 (>= 0.8 confidence, cached), Tier 1 (0.6-0.8, local/fleet), Tier 2 (< 0.6, cloud)
69
68
  - **Pattern promotion**: Observer records intent+tool pairs; after 3 successful observations, promotes to PatternStore with seeded confidence 0.5
70
69
  - **Context guards**: Staleness (1hr), rapid-fire (5 in 10min), anomaly (2 consecutive misses) prevent stale Tier 0
70
+ - **Settings::Extensions integration**: MCP-specific tools register with `dispatch_type: :class_call`; remote MCP tools register with `dispatch_type: :mcp_remote`
71
71
 
72
72
  ## File Map
73
73
 
@@ -75,33 +75,25 @@ All optional dependencies use `defined?()` guards:
75
75
  |------|---------|
76
76
  | `lib/legion/mcp.rb` | Entry point: `Legion::MCP.server` singleton factory |
77
77
  | `lib/legion/mcp/version.rb` | `Legion::MCP::VERSION` constant |
78
- | `lib/legion/mcp/server.rb` | MCP::Server builder, governance-aware build; reads tools from Tools::Registry |
78
+ | `lib/legion/mcp/utils.rb` | Pure-function summarization and formatting helpers |
79
+ | `lib/legion/mcp/server.rb` | MCP::Server builder, governance-aware build; reads tools from Settings::Extensions |
79
80
  | `lib/legion/mcp/auth.rb` | JWT + API key authentication |
80
- | `lib/legion/mcp/tool_governance.rb` | Risk-tier tool filtering + invocation audit |
81
- | `lib/legion/mcp/context_compiler.rb` | Keyword + semantic tool matching (60/40 blend) |
82
- | `lib/legion/mcp/embedding_index.rb` | Semantic tool matching; delegates persistence to Legion::Tools::EmbeddingCache |
83
- | `lib/legion/mcp/observer.rb` | Instrumentation: counters, ring buffer, pattern promotion |
84
- | `lib/legion/mcp/usage_filter.rb` | Frequency/recency/keyword scoring for dynamic tool filtering |
85
- | `lib/legion/mcp/pattern_store.rb` | 4-layer degrading storage (L0/L1/L2) with thread-safe access |
86
- | `lib/legion/mcp/tier_router.rb` | Confidence-gated tier selection, tool chain execution |
87
- | `lib/legion/mcp/context_guard.rb` | Staleness, rapid-fire, anomaly detection |
88
- | `lib/legion/mcp/tool_adapter.rb` | MCP::ToolAdapter — wraps Legion::Tools::Base for MCP SDK (McpToolAdapter kept as alias) |
89
- | `lib/legion/mcp/deferred_registry.rb` | DeferredRegistry — reads deferred tools from Legion::Tools::Registry at request time |
90
- | `lib/legion/mcp/tools/` | All tool implementations; only MCP_SPECIFIC_TOOLS (6 tools) registered in Server.tool_registry — extension tools sourced via Legion::Tools::Discovery |
81
+ | `lib/legion/mcp/patterns.rb` | Barrel file for 13 Tier 0 pattern routing modules |
82
+ | `lib/legion/mcp/discovery.rb` | Barrel file for 12 tool discovery and adaptation modules |
83
+ | `lib/legion/mcp/tool_adapter.rb` | MCP::ToolAdapter wraps tools for MCP SDK; handles :mcp_remote dispatch |
84
+ | `lib/legion/mcp/client.rb` | Client boot/shutdown, server registration |
85
+ | `lib/legion/mcp/client/pool.rb` | Connection pool; all_tools registers into Settings::Extensions |
86
+ | `lib/legion/mcp/client/connection.rb` | stdio/HTTP transport connections |
87
+ | `lib/legion/mcp/client/server_registry.rb` | Server health tracking and registration |
91
88
  | `lib/legion/mcp/tools/do_action.rb` | Natural language intent routing with Tier 0 fast path |
92
89
  | `lib/legion/mcp/tools/discover_tools.rb` | Dynamic tool discovery with context |
93
90
  | `lib/legion/mcp/tools/run_task.rb` | Execute runner function via dot notation |
94
- | `lib/legion/mcp/tools/query_knowledge.rb` | Query Apollo knowledge store |
95
- | `lib/legion/mcp/tools/knowledge_health.rb` | Knowledge store health and quality report |
96
- | `lib/legion/mcp/tools/knowledge_context.rb` | Scoped RAG query (local/global/all) for current-task context |
97
- | `lib/legion/mcp/tools/eval_*.rb` | Evaluation management (list/run/results) |
98
- | `lib/legion/mcp/tools/experiment_results.rb` | A/B experiment result comparison |
99
- | `lib/legion/mcp/tools/dataset_*.rb` | Dataset browsing (list/show) |
100
- | `lib/legion/mcp/tools/prompt_*.rb` | Prompt template management (list/show/run) |
101
91
  | `lib/legion/mcp/tools/plan_action.rb` | Agentic planning with action decomposition |
102
- | `lib/legion/mcp/tools/ask_peer.rb` / `notify_peer.rb` / `broadcast_peers.rb` / `list_peers.rb` | Agent mesh communication |
103
- | `lib/legion/mcp/tools/mesh_status.rb` | Mesh topology status |
104
- | `lib/legion/mcp/tools/mind_growth_*.rb` | Mind growth tools (status/propose/approve/build_queue/cognitive_profile/health) |
92
+ | `lib/legion/mcp/tools/skills.rb` | Skill list/describe/invoke/cancel tools |
93
+ | `lib/legion/mcp/tools/structural_index.rb` | Query precomputed structural index |
94
+ | `lib/legion/mcp/tools/tool_audit.rb` | Audit registered tools quality |
95
+ | `lib/legion/mcp/tools/state_diff.rb` | Changed system state since timestamp |
96
+ | `lib/legion/mcp/tools/search_sessions.rb` | Search past conversation sessions |
105
97
  | `lib/legion/mcp/resources/runner_catalog.rb` | `legion://runners` resource |
106
98
  | `lib/legion/mcp/resources/extension_info.rb` | `legion://extensions/{name}` resource template |
107
99
 
data/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # legion-mcp
2
2
 
3
- MCP (Model Context Protocol) server for the LegionIO framework. Provides semantic tool matching, observation pipeline, context compilation, and tiered behavioral intelligence (Tier 0/1/2 routing).
3
+ Model Context Protocol (MCP) server for the LegionIO framework. Provides semantic tool matching, observation pipeline, context compilation, tiered behavioral intelligence (Tier 0/1/2 routing), tool governance, deferred tool loading, and MCP client pooling for remote server federation.
4
4
 
5
- **Version**: 0.6.1
5
+ **Version**: 0.9.1
6
+ **License**: Apache-2.0
7
+ **Ruby**: >= 3.4
6
8
 
7
9
  Extracted from [LegionIO](https://github.com/LegionIO/LegionIO) for independent versioning and reuse.
8
10
 
@@ -15,27 +17,53 @@ gem 'legion-mcp'
15
17
  Or in a Gemfile:
16
18
 
17
19
  ```ruby
18
- gem 'legion-mcp', '~> 0.5'
20
+ gem 'legion-mcp', '~> 0.9'
19
21
  ```
20
22
 
21
23
  ## Architecture
22
24
 
23
25
  ```
24
26
  Legion::MCP
25
- ├── Server # MCP::Server builder, TOOL_CLASSES registration
27
+ ├── Server # MCP::Server builder, governance-aware build; tool list sourced via DeferredRegistry
26
28
  ├── Auth # JWT + API key authentication
29
+ ├── Utils # Pure-function summarization and formatting helpers
27
30
  ├── ToolGovernance # Risk-tier tool filtering + invocation audit
28
- ├── ContextCompiler # Keyword + semantic tool matching (60/40 blend)
31
+ ├── ToolAdapter # Adapts Legion::Tools::Base to MCP SDK format; handles :mcp_remote dispatch
32
+ ├── DeferredRegistry # Reads deferred/always-loaded tools from Settings::Extensions at request time
33
+ ├── ContextCompiler # Keyword + semantic tool matching (60/40 blend), 17 categories
29
34
  ├── EmbeddingIndex # In-memory vector cache for semantic matching
30
- ├── Observer # Instrumentation: counters, ring buffer, pattern promotion
35
+ ├── Observer # Instrumentation: counters, ring buffer, intent tracking, pattern promotion
31
36
  ├── UsageFilter # Frequency/recency/keyword scoring for dynamic tool filtering
32
- ├── PatternStore # 4-layer degrading storage (L0 memory → L1 cache → L2 SQLite)
37
+ ├── DynamicInjector # Context-aware tool injection/removal with tools/list_changed notification
38
+ ├── CatalogDispatcher # Dispatch layer routing MCP calls through Legion::Ingress
39
+ ├── Patterns::Store # 3-layer degrading storage (L0 memory -> L1 cache -> L2 SQLite)
40
+ ├── Patterns::Schema # Portable pattern format with trust-level confidence capping
41
+ ├── Patterns::Exchange # Bulk import/export of patterns via JSON files
42
+ ├── Patterns::Gossip # AMQP-based pattern sharing between instances
43
+ ├── Patterns::Compiler # Compressed tool definitions and compiled workflows
33
44
  ├── TierRouter # Confidence-gated tier selection (0/1/2)
34
- ├── ContextGuard # Staleness, rapid-fire, anomaly detection
35
- ├── Tools/ # 59 MCP::Tool subclasses (legion.* namespace)
45
+ ├── ContextGuard # Staleness, rapid-fire, anomaly detection guards
46
+ ├── StateTracker # In-memory state snapshots with delta diff computation
47
+ ├── StructuralIndex # Precomputed index of extensions, runners, actors, and tools
48
+ ├── ToolQuality # Docstring quality audit, category resolution, capability matrix
49
+ ├── GapDetector # Unmatched intents, high-failure tools, stale candidate detection
50
+ ├── SelfGenerate # Gap detection + publication cycles with cooldown
51
+ ├── OverrideBroadcast # Mesh-wide override confirmation via RabbitMQ
52
+ ├── TracingContext # Thread-local conversation/request/exchange/trace ID propagation
53
+ ├── Client::Pool # Remote MCP server connections; registers tools into Settings::Extensions
54
+ ├── Client::Connection # stdio and HTTP transport connections with TTL-cached tool lists
55
+ ├── Client::ServerRegistry # Static and dynamic server registration with health tracking
56
+ ├── Tools/ # 10 MCP-specific + 18 legion-data CRUD tools; extension tools via Settings::Extensions
36
57
  └── Resources/ # RunnerCatalog, ExtensionInfo
37
58
  ```
38
59
 
60
+ ### Tool Registry
61
+
62
+ - **MCP_SPECIFIC_TOOLS** (10 tools): `do_action`, `discover_tools`, `plan_action`, `structural_index`, `tool_audit`, `state_diff`, `search_sessions`, `skill_list`, `skill_describe`, `skill_invoke`, `skill_cancel`
63
+ - **legion-data CRUD** (18 tools): `run_task`, `describe_runner`, list/get/delete tasks, `get_task_logs`, CRUD chains/relationships/schedules
64
+ - **Extension tools**: Auto-discovered via `Legion::Settings::Extensions` at runtime; not shipped as static files
65
+ - **Remote MCP tools**: Fetched from remote servers via `Client::Pool.all_tools`, registered into `Settings::Extensions` with `dispatch_type: :mcp_remote`
66
+
39
67
  ## Tiered Behavioral Intelligence
40
68
 
41
69
  Requests flow through three tiers, each with increasing latency and capability:
@@ -65,7 +93,7 @@ If any guard triggers, the request escalates to Tier 1.
65
93
 
66
94
  ### Storage Layers
67
95
 
68
- PatternStore degrades gracefully across 4 layers:
96
+ PatternStore degrades gracefully across 3 layers:
69
97
 
70
98
  | Layer | Backend | Requirement |
71
99
  |-------|---------|-------------|
@@ -73,40 +101,31 @@ PatternStore degrades gracefully across 4 layers:
73
101
  | L1 | Legion::Cache (memcached/redis) | `defined?(Legion::Cache)` |
74
102
  | L2 | Legion::Data::Local (SQLite) | `defined?(Legion::Data::Local)` |
75
103
 
76
- All persistence wraps in `begin/rescue => nil` failed writes never block Tier 0.
104
+ All persistence wraps in `begin/rescue => nil` -- failed writes never block Tier 0.
77
105
 
78
106
  ## Tools
79
107
 
80
- 59 MCP tools in the `legion.*` namespace:
108
+ 28 built-in MCP tools in the `legion.*` namespace (10 MCP-specific + 18 legion-data CRUD). Extension-owned tools are auto-discovered at runtime via `Legion::Settings::Extensions`.
81
109
 
82
110
  | Tool | Purpose |
83
111
  |------|---------|
84
112
  | `legion.do` | Natural language intent routing (Tier 0 fast path) |
85
- | `legion.discover_tools` | Dynamic tool discovery with context |
113
+ | `legion.tools` | Dynamic tool discovery by category, intent, or schema resolution |
114
+ | `legion.plan` | Multi-step workflow planning with LLM narrative |
115
+ | `legion.structural_index` | Precomputed structural index of extensions/runners/actors/tools |
116
+ | `legion.tool_audit` | Quality audit of registered tools (summary/matrix/issues) |
117
+ | `legion.state_diff` | Delta state polling since a given timestamp |
118
+ | `legion.search_sessions` | Search past conversation sessions by keyword |
119
+ | `legion.skill.list` | List all registered LLM skills |
120
+ | `legion.skill.describe` | Describe a specific skill |
121
+ | `legion.skill.invoke` | Invoke a skill for a conversation |
122
+ | `legion.skill.cancel` | Cancel an active skill run |
86
123
  | `legion.run_task` | Execute a runner function via dot notation |
87
124
  | `legion.describe_runner` | Runner/function discovery |
88
- | `legion.list_tasks` / `get_task` / `delete_task` | Task CRUD |
89
- | `legion.get_task_logs` | Task execution logs |
125
+ | `legion.list_tasks` / `get_task` / `delete_task` / `get_task_logs` | Task CRUD + logs |
90
126
  | `legion.list_chains` / `create_chain` / `update_chain` / `delete_chain` | Chain management |
91
- | `legion.list_relationships` / `create_relationship` / `update_relationship` / `delete_relationship` | Task relationships |
92
- | `legion.list_extensions` / `get_extension` / `enable_extension` / `disable_extension` | Extension management |
93
- | `legion.list_schedules` / `create_schedule` / `update_schedule` / `delete_schedule` | Schedule CRUD |
94
- | `legion.get_status` / `get_config` | System introspection |
95
- | `legion.list_workers` / `show_worker` / `worker_lifecycle` / `worker_costs` | Worker management |
96
- | `legion.team_summary` / `routing_stats` | Team and routing metrics |
97
- | `legion.rbac_assignments` / `rbac_check` / `rbac_grants` | Access control |
98
- | `legion.mind_growth_status` / `mind_growth_propose` / `mind_growth_approve` | Cognitive architecture growth |
99
- | `legion.mind_growth_build_queue` / `mind_growth_cognitive_profile` / `mind_growth_health` | Growth analysis and health |
100
- | `legion.query_knowledge` | Query Apollo knowledge store |
101
- | `legion.knowledge_health` | Knowledge store health and quality report |
102
- | `legion.knowledge_context` | Scoped RAG knowledge retrieval (local/global/all) |
103
- | `legion.eval_list` / `eval_run` / `eval_results` | Evaluation management |
104
- | `legion.experiment_results` | A/B experiment result comparison |
105
- | `legion.dataset_list` / `dataset_show` | Dataset browsing |
106
- | `legion.prompt_list` / `prompt_show` / `prompt_run` | Prompt template management |
107
- | `legion.plan_action` | Agentic planning with action decomposition |
108
- | `legion.ask_peer` / `notify_peer` / `broadcast_peers` / `list_peers` | Agent mesh communication |
109
- | `legion.mesh_status` | Mesh topology status |
127
+ | `legion.list_relationships` / `create_*` / `update_*` / `delete_*` | Relationship CRUD |
128
+ | `legion.list_schedules` / `create_*` / `update_*` / `delete_*` | Schedule CRUD |
110
129
 
111
130
  ## Resources
112
131
 
@@ -115,6 +134,24 @@ All persistence wraps in `begin/rescue => nil` — failed writes never block Tie
115
134
  | `legion://runners` | All registered extension.runner.function paths |
116
135
  | `legion://extensions/{name}` | Extension detail template |
117
136
 
137
+ ## MCP Client (Federation)
138
+
139
+ Connect to remote MCP servers via stdio or HTTP transport:
140
+
141
+ ```ruby
142
+ # Via settings (loaded at boot)
143
+ # settings/mcp.json:
144
+ # { "mcp": { "servers": { "code_server": { "transport": "stdio", "command": "npx @example/mcp-server" } } } }
145
+
146
+ Legion::MCP::Client.boot
147
+
148
+ # Or register at runtime
149
+ Legion::MCP::Client.register(:my_server, transport: :http, url: 'http://localhost:9393/mcp')
150
+
151
+ # All remote tools are registered into Settings::Extensions with dispatch_type: :mcp_remote
152
+ tools = Legion::MCP::Client::Pool.all_tools
153
+ ```
154
+
118
155
  ## Usage
119
156
 
120
157
  ### Standalone MCP server
@@ -123,7 +160,6 @@ All persistence wraps in `begin/rescue => nil` — failed writes never block Tie
123
160
  require 'legion/mcp'
124
161
 
125
162
  server = Legion::MCP.server
126
- # Start via stdio transport
127
163
  server.start
128
164
  ```
129
165
 
@@ -153,6 +189,13 @@ when 2 then puts "Escalate to cloud LLM"
153
189
  end
154
190
  ```
155
191
 
192
+ ### Identity-scoped server
193
+
194
+ ```ruby
195
+ server = Legion::MCP.server_for(token: jwt_token)
196
+ # Returns governance-filtered tool set based on JWT claims
197
+ ```
198
+
156
199
  ## Configuration
157
200
 
158
201
  All configuration is optional and read via `Legion::Settings` when available:
@@ -160,12 +203,40 @@ All configuration is optional and read via `Legion::Settings` when available:
160
203
  ```json
161
204
  {
162
205
  "mcp": {
163
- "context_guard": {
164
- "max_stale_seconds": 3600,
165
- "rapid_fire_threshold": 5,
166
- "rapid_fire_window_secs": 600,
167
- "anomaly_miss_threshold": 2
168
- }
206
+ "auth": {
207
+ "enabled": false,
208
+ "require_auth": false,
209
+ "jwt_secret": null,
210
+ "jwt_algorithm": "HS256",
211
+ "jwt_issuer": "legion",
212
+ "allowed_api_keys": []
213
+ },
214
+ "governance": {
215
+ "enabled": false,
216
+ "audit_invocations": true,
217
+ "tool_risk_tiers": {}
218
+ },
219
+ "deferred_loading": {
220
+ "enabled": true,
221
+ "always_loaded": []
222
+ },
223
+ "dynamic_tools": {
224
+ "enabled": false,
225
+ "max_injected": 10
226
+ },
227
+ "tier0": {
228
+ "guards": {
229
+ "max_stale_seconds": 3600,
230
+ "rapid_fire_threshold": 5,
231
+ "rapid_fire_window_seconds": 600
232
+ }
233
+ },
234
+ "auto_expose_runners": false,
235
+ "servers": {},
236
+ "cold_start": {
237
+ "patterns_path": null
238
+ },
239
+ "roles": {}
169
240
  }
170
241
  }
171
242
  ```
@@ -177,10 +248,32 @@ All configuration is optional and read via `Legion::Settings` when available:
177
248
  | `mcp` (~> 0.8) | Yes | MCP server SDK |
178
249
  | `legion-data` (>= 1.4) | Yes | Sequel models, migrations |
179
250
  | `legion-json` (>= 1.2) | Yes | JSON serialization |
180
- | `legion-logging` (>= 0.3) | Yes | Logging |
181
- | `legion-settings` (>= 0.3) | Yes | Configuration |
182
- | `legion-cache` | Optional | L1 pattern cache |
183
- | `legion-llm` | Optional | Embeddings for semantic matching |
251
+ | `legion-logging` (>= 1.4.3) | Yes | Structured logging via Helper |
252
+ | `legion-settings` (>= 1.4.0) | Yes | Configuration + Settings::Extensions |
253
+ | `legion-cache` | Optional | L1 pattern cache (memcached/redis) |
254
+ | `legion-llm` | Optional | Embeddings for semantic matching, Tier 1/2 LLM |
255
+
256
+ ## Guard Strategy
257
+
258
+ All optional dependencies use `defined?()` guards:
259
+ - `defined?(Legion::Cache)` for L1 cache operations
260
+ - `defined?(Legion::Data::Local)` for L2 SQLite persistence
261
+ - `defined?(Legion::LLM)` for embedding generation and tiered LLM routing
262
+ - `defined?(Legion::Settings::Extensions)` for central tool registry
263
+ - `defined?(Legion::MCP::EmbeddingIndex)` for semantic matching
264
+ - `defined?(Legion::MCP::TierRouter)` for Tier 0 routing
265
+
266
+ Every storage write wraps in `begin/rescue => nil` -- failed persistence never blocks Tier 0.
267
+
268
+ ## Key Conventions
269
+
270
+ - `Legion::JSON.load` returns **symbol keys** -- use `body[:data]`, not `body['data']`
271
+ - `Legion::JSON.dump` takes exactly 1 positional arg -- wrap kwargs in explicit `{}`
272
+ - `::Process` and `::JSON` must be explicit inside the `Legion::` namespace
273
+ - `Legion::MCP.server` is a memoized singleton -- call `Legion::MCP.reset!` in tests
274
+ - MCP tool naming: `legion.snake_case_name` (dot namespace)
275
+ - Every rescue block calls `handle_exception(e, level:, handled:, operation:)`
276
+ - Logging via `extend Legion::Logging::Helper` (modules) or `include Legion::Logging::Helper` (classes/class << self)
184
277
 
185
278
  ## Development
186
279
 
@@ -191,6 +284,37 @@ bundle exec rubocop -A # auto-fix
191
284
  bundle exec rubocop # lint check
192
285
  ```
193
286
 
287
+ ## File Map
288
+
289
+ | Path | Purpose |
290
+ |------|---------|
291
+ | `lib/legion/mcp.rb` | Entry point: `Legion::MCP.server` singleton factory |
292
+ | `lib/legion/mcp/version.rb` | `Legion::MCP::VERSION` constant |
293
+ | `lib/legion/mcp/utils.rb` | Pure-function summarization and formatting helpers |
294
+ | `lib/legion/mcp/server.rb` | MCP::Server builder, governance-aware build; reads tools from Settings::Extensions |
295
+ | `lib/legion/mcp/auth.rb` | JWT + API key authentication |
296
+ | `lib/legion/mcp/patterns.rb` | Barrel file for 13 Tier 0 pattern routing modules |
297
+ | `lib/legion/mcp/discovery.rb` | Barrel file for 12 tool discovery and adaptation modules |
298
+ | `lib/legion/mcp/tool_adapter.rb` | MCP::ToolAdapter -- wraps tools for MCP SDK; handles :mcp_remote dispatch |
299
+ | `lib/legion/mcp/deferred_registry.rb` | Deferred/always-loaded tool list management |
300
+ | `lib/legion/mcp/catalog_dispatcher.rb` | Dispatch layer routing through Legion::Ingress |
301
+ | `lib/legion/mcp/dynamic_injector.rb` | Context-aware tool injection/removal |
302
+ | `lib/legion/mcp/client.rb` | Client boot/shutdown, server registration |
303
+ | `lib/legion/mcp/client/pool.rb` | Connection pool; all_tools registers into Settings::Extensions |
304
+ | `lib/legion/mcp/client/connection.rb` | stdio/HTTP transport connections |
305
+ | `lib/legion/mcp/client/server_registry.rb` | Server health tracking and registration |
306
+ | `lib/legion/mcp/tools/do_action.rb` | Natural language intent routing with Tier 0 fast path |
307
+ | `lib/legion/mcp/tools/discover_tools.rb` | Dynamic tool discovery with context |
308
+ | `lib/legion/mcp/tools/run_task.rb` | Execute runner function via dot notation |
309
+ | `lib/legion/mcp/tools/plan_action.rb` | Agentic planning with action decomposition |
310
+ | `lib/legion/mcp/tools/skills.rb` | Skill list/describe/invoke/cancel tools |
311
+ | `lib/legion/mcp/tools/structural_index.rb` | Query precomputed structural index |
312
+ | `lib/legion/mcp/tools/tool_audit.rb` | Audit registered tools quality |
313
+ | `lib/legion/mcp/tools/state_diff.rb` | Changed system state since timestamp |
314
+ | `lib/legion/mcp/tools/search_sessions.rb` | Search past conversation sessions |
315
+ | `lib/legion/mcp/resources/runner_catalog.rb` | `legion://runners` resource |
316
+ | `lib/legion/mcp/resources/extension_info.rb` | `legion://extensions/{name}` resource template |
317
+
194
318
  ## License
195
319
 
196
320
  Apache-2.0
data/legion-mcp.gemspec CHANGED
@@ -30,6 +30,6 @@ Gem::Specification.new do |spec|
30
30
  spec.add_dependency 'legion-data', '>= 1.4.19'
31
31
  spec.add_dependency 'legion-json', '>= 1.2.0'
32
32
  spec.add_dependency 'legion-logging', '>= 1.4.3'
33
- spec.add_dependency 'legion-settings', '>= 1.3.12'
33
+ spec.add_dependency 'legion-settings', '>= 1.4.0'
34
34
  spec.add_dependency 'mcp', '~> 0.8'
35
35
  end
@@ -14,14 +14,13 @@ module Legion
14
14
  def generate_task? = false
15
15
 
16
16
  def time
17
- if defined?(Legion::Settings) && !Legion::Settings[:codegen].nil?
18
- Legion::Settings.dig(:codegen, :self_generate, :cycle_interval) || 300
19
- else
17
+ if Legion::Settings[:codegen].nil?
20
18
  300
19
+ else
20
+ Legion::Settings.dig(:codegen, :self_generate, :cycle_interval) || 300
21
21
  end
22
22
  rescue StandardError => e
23
23
  handle_exception(e, level: :warn, operation: 'legion.mcp.actors.self_generate_cycle.time')
24
- log.warn(e.message)
25
24
  300
26
25
  end
27
26
 
@@ -29,7 +28,6 @@ module Legion
29
28
  SelfGenerate.enabled?
30
29
  rescue StandardError => e
31
30
  handle_exception(e, level: :warn, operation: 'legion.mcp.actors.self_generate_cycle.enabled?')
32
- log.warn(e.message)
33
31
  false
34
32
  end
35
33
 
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module MCP
5
+ module Audit
6
+ extend Legion::Logging::Helper
7
+
8
+ ROUTING_KEYS = {
9
+ tool_call: 'mcp.audit.tool_call',
10
+ client_call: 'mcp.audit.client_call',
11
+ governance: 'mcp.audit.governance'
12
+ }.freeze
13
+
14
+ module_function
15
+
16
+ def emit_tool_call(**event)
17
+ publish(ROUTING_KEYS[:tool_call], event, :ToolCallEvent)
18
+ end
19
+
20
+ def emit_client_call(**event)
21
+ publish(ROUTING_KEYS[:client_call], event, :ClientCallEvent)
22
+ end
23
+
24
+ def emit_governance(**event)
25
+ publish(ROUTING_KEYS[:governance], event, :GovernanceEvent)
26
+ end
27
+
28
+ def transport_available?
29
+ !!(defined?(Legion::Transport::Message) &&
30
+ defined?(Legion::MCP::Transport::Exchanges::Audit))
31
+ end
32
+
33
+ def transport_connected?
34
+ Legion::Settings.dig(:transport, :connected) == true
35
+ rescue StandardError => e
36
+ handle_exception(e, level: :debug, handled: true, operation: 'mcp.audit.transport_connected?')
37
+ false
38
+ end
39
+
40
+ def publish(routing_key, event, message_sym)
41
+ return unless transport_available? && transport_connected?
42
+
43
+ message_class = Legion::MCP::Transport::Messages.const_get(message_sym)
44
+ message_class.new(routing_key: routing_key, **event).publish
45
+ rescue StandardError => e
46
+ handle_exception(e, level: :warn, handled: true, operation: 'mcp.audit.publish')
47
+ end
48
+ end
49
+ end
50
+ end