legion-mcp 0.4.0 → 0.4.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -0
- data/CODEOWNERS +39 -0
- data/lib/legion/mcp/server.rb +29 -1
- data/lib/legion/mcp/tools/ask_peer.rb +56 -0
- data/lib/legion/mcp/tools/broadcast_peers.rb +55 -0
- data/lib/legion/mcp/tools/dataset_list.rb +48 -0
- data/lib/legion/mcp/tools/dataset_show.rb +54 -0
- data/lib/legion/mcp/tools/eval_list.rb +48 -0
- data/lib/legion/mcp/tools/eval_results.rb +77 -0
- data/lib/legion/mcp/tools/eval_run.rb +57 -0
- data/lib/legion/mcp/tools/experiment_results.rb +77 -0
- data/lib/legion/mcp/tools/list_peers.rb +47 -0
- data/lib/legion/mcp/tools/mesh_status.rb +43 -0
- data/lib/legion/mcp/tools/notify_peer.rb +54 -0
- data/lib/legion/mcp/tools/prompt_list.rb +48 -0
- data/lib/legion/mcp/tools/prompt_run.rb +59 -0
- data/lib/legion/mcp/tools/prompt_show.rb +55 -0
- data/lib/legion/mcp/version.rb +1 -1
- metadata +16 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 423fca2b66220aa4bfd5dce33490a6fb92826da7e999a21be9afed00610fd435
|
|
4
|
+
data.tar.gz: 7e9a664b742ee174140f2463c008bd99475736afe074c056cf298d9742a7ec5d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d610777e7f105a5b72e4a58fb0a06cd129361639837638b49888de9b6feeb3697a5147184d5d415ba95315d2bafcf3011b9a6c5d7515b8ceec3155a8173477a9
|
|
7
|
+
data.tar.gz: 6c74faf7a062f61e8b9e10d79b6236dc9b361d6b5ff657bfe651bfa731f6a633a8c8acaf0a68f907b49cd333c4328d77e919d45a114c8e8b7a40323bbfeb9dac
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# legion-mcp Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.2] - 2026-03-22
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `legion.ask_peer` — synchronous RPC query to a specific mesh peer via `lex-mesh` `request_task`
|
|
7
|
+
- `legion.list_peers` — list all registered mesh agents, with optional capability filter via `find_agents`
|
|
8
|
+
- `legion.notify_peer` — fire-and-forget unicast notification to a specific mesh agent via `send_message`
|
|
9
|
+
- `legion.broadcast_peers` — broadcast to all agents or multicast to a capability group via `send_message`
|
|
10
|
+
- `legion.mesh_status` — retrieve current mesh network state via `mesh_status`
|
|
11
|
+
- All 5 tools registered in `TOOL_CLASSES`; total tool count raised from 45 to 50
|
|
12
|
+
- Specs for all 5 new tools (25 examples)
|
|
13
|
+
- Updated `server_spec.rb` tool count assertion from 45 to 50
|
|
14
|
+
|
|
15
|
+
## [0.4.1] - 2026-03-19
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- `legion.prompt_list` — list all stored prompt templates via lex-prompt Client
|
|
19
|
+
- `legion.prompt_show` — fetch a prompt by name, version, or tag via lex-prompt Client
|
|
20
|
+
- `legion.prompt_run` — render a prompt template with ERB variable substitution via lex-prompt Client
|
|
21
|
+
- `legion.dataset_list` — list all stored datasets via lex-dataset Client
|
|
22
|
+
- `legion.dataset_show` — fetch a dataset with all rows, optionally version-pinned, via lex-dataset Client
|
|
23
|
+
- `legion.experiment_results` — retrieve per-row results and summary for a named experiment from lex-dataset
|
|
24
|
+
- `legion.eval_list` — list available evaluator templates via lex-eval Client
|
|
25
|
+
- `legion.eval_run` — run a single input/output pair through a named evaluator via lex-eval Client
|
|
26
|
+
- `legion.eval_results` — retrieve stored experiment results via lex-dataset experiment store
|
|
27
|
+
- All 9 tools registered in `TOOL_CLASSES`; total tool count raised from 36 to 45
|
|
28
|
+
- Specs for all 9 new tools
|
|
29
|
+
|
|
3
30
|
## [0.4.0] - 2026-03-20
|
|
4
31
|
|
|
5
32
|
### Added
|
data/CODEOWNERS
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Default owner — all files
|
|
2
|
+
* @Esity
|
|
3
|
+
|
|
4
|
+
# Core library code
|
|
5
|
+
# lib/ @Esity @future-ai-team
|
|
6
|
+
|
|
7
|
+
# MCP tools (35 tool subclasses)
|
|
8
|
+
# lib/legion/mcp/tools/ @Esity @future-ai-team
|
|
9
|
+
|
|
10
|
+
# MCP resources
|
|
11
|
+
# lib/legion/mcp/resources/ @Esity @future-ai-team
|
|
12
|
+
|
|
13
|
+
# Tiered Behavioral Intelligence (TBI) — pattern store, tier router, context guard
|
|
14
|
+
# lib/legion/mcp/pattern_store.rb @Esity @future-ai-team
|
|
15
|
+
# lib/legion/mcp/tier_router.rb @Esity @future-ai-team
|
|
16
|
+
# lib/legion/mcp/context_guard.rb @Esity @future-ai-team
|
|
17
|
+
|
|
18
|
+
# Semantic matching and embeddings
|
|
19
|
+
# lib/legion/mcp/context_compiler.rb @Esity @future-ai-team
|
|
20
|
+
# lib/legion/mcp/embedding_index.rb @Esity @future-ai-team
|
|
21
|
+
|
|
22
|
+
# Observer pipeline and usage filter
|
|
23
|
+
# lib/legion/mcp/observer.rb @Esity @future-ai-team
|
|
24
|
+
# lib/legion/mcp/usage_filter.rb @Esity @future-ai-team
|
|
25
|
+
|
|
26
|
+
# Tool governance
|
|
27
|
+
# lib/legion/mcp/tool_governance.rb @Esity @future-security-team
|
|
28
|
+
|
|
29
|
+
# Authentication
|
|
30
|
+
# lib/legion/mcp/auth.rb @Esity @future-security-team
|
|
31
|
+
|
|
32
|
+
# Specs
|
|
33
|
+
# spec/ @Esity @future-contributors
|
|
34
|
+
|
|
35
|
+
# Documentation
|
|
36
|
+
# *.md @Esity @future-docs-team
|
|
37
|
+
|
|
38
|
+
# CI/CD
|
|
39
|
+
# .github/ @Esity
|
data/lib/legion/mcp/server.rb
CHANGED
|
@@ -35,12 +35,26 @@ require_relative 'tools/routing_stats'
|
|
|
35
35
|
require_relative 'tools/rbac_check'
|
|
36
36
|
require_relative 'tools/rbac_assignments'
|
|
37
37
|
require_relative 'tools/rbac_grants'
|
|
38
|
+
require_relative 'tools/prompt_list'
|
|
39
|
+
require_relative 'tools/prompt_show'
|
|
40
|
+
require_relative 'tools/prompt_run'
|
|
41
|
+
require_relative 'tools/dataset_list'
|
|
42
|
+
require_relative 'tools/dataset_show'
|
|
43
|
+
require_relative 'tools/experiment_results'
|
|
44
|
+
require_relative 'tools/eval_list'
|
|
45
|
+
require_relative 'tools/eval_run'
|
|
46
|
+
require_relative 'tools/eval_results'
|
|
38
47
|
require_relative 'context_compiler'
|
|
39
48
|
require_relative 'embedding_index'
|
|
40
49
|
require_relative 'cold_start'
|
|
41
50
|
require_relative 'tools/do_action'
|
|
42
51
|
require_relative 'tools/plan_action'
|
|
43
52
|
require_relative 'tools/discover_tools'
|
|
53
|
+
require_relative 'tools/ask_peer'
|
|
54
|
+
require_relative 'tools/list_peers'
|
|
55
|
+
require_relative 'tools/notify_peer'
|
|
56
|
+
require_relative 'tools/broadcast_peers'
|
|
57
|
+
require_relative 'tools/mesh_status'
|
|
44
58
|
require_relative 'resources/runner_catalog'
|
|
45
59
|
require_relative 'resources/extension_info'
|
|
46
60
|
|
|
@@ -81,9 +95,23 @@ module Legion
|
|
|
81
95
|
Tools::RbacCheck,
|
|
82
96
|
Tools::RbacAssignments,
|
|
83
97
|
Tools::RbacGrants,
|
|
98
|
+
Tools::PromptList,
|
|
99
|
+
Tools::PromptShow,
|
|
100
|
+
Tools::PromptRun,
|
|
101
|
+
Tools::DatasetList,
|
|
102
|
+
Tools::DatasetShow,
|
|
103
|
+
Tools::ExperimentResults,
|
|
104
|
+
Tools::EvalList,
|
|
105
|
+
Tools::EvalRun,
|
|
106
|
+
Tools::EvalResults,
|
|
84
107
|
Tools::DoAction,
|
|
85
108
|
Tools::PlanAction,
|
|
86
|
-
Tools::DiscoverTools
|
|
109
|
+
Tools::DiscoverTools,
|
|
110
|
+
Tools::AskPeer,
|
|
111
|
+
Tools::ListPeers,
|
|
112
|
+
Tools::NotifyPeer,
|
|
113
|
+
Tools::BroadcastPeers,
|
|
114
|
+
Tools::MeshStatus
|
|
87
115
|
].freeze
|
|
88
116
|
|
|
89
117
|
class << self
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module MCP
|
|
5
|
+
module Tools
|
|
6
|
+
class AskPeer < ::MCP::Tool
|
|
7
|
+
tool_name 'legion.ask_peer'
|
|
8
|
+
description 'Send a synchronous query to a specific mesh peer and return the result.'
|
|
9
|
+
|
|
10
|
+
input_schema(
|
|
11
|
+
properties: {
|
|
12
|
+
to: { type: 'string', description: 'Agent ID or capability name to route to' },
|
|
13
|
+
query: { type: 'string', description: 'The question or request to send to the peer' },
|
|
14
|
+
timeout: { type: 'integer', description: 'Seconds to wait for response (default 30)' }
|
|
15
|
+
},
|
|
16
|
+
required: %w[to query]
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
def call(to:, query:, timeout: 30)
|
|
21
|
+
return error_response('lex-mesh is not available') unless mesh_available?
|
|
22
|
+
|
|
23
|
+
result = mesh_client.request_task(
|
|
24
|
+
from: 'legion.mcp',
|
|
25
|
+
to: to,
|
|
26
|
+
task: 'query',
|
|
27
|
+
payload: { query: query },
|
|
28
|
+
timeout: timeout
|
|
29
|
+
)
|
|
30
|
+
text_response(result)
|
|
31
|
+
rescue StandardError => e
|
|
32
|
+
error_response("Failed to query peer: #{e.message}")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def mesh_available?
|
|
38
|
+
defined?(Legion::Extensions::Mesh::Client)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def mesh_client
|
|
42
|
+
@mesh_client ||= Legion::Extensions::Mesh::Client.new
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def text_response(data)
|
|
46
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump(data) }])
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def error_response(msg)
|
|
50
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump({ error: msg }) }], error: true)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module MCP
|
|
5
|
+
module Tools
|
|
6
|
+
class BroadcastPeers < ::MCP::Tool
|
|
7
|
+
tool_name 'legion.broadcast_peers'
|
|
8
|
+
description 'Broadcast a message to all mesh agents, or multicast to agents with a specific capability.'
|
|
9
|
+
|
|
10
|
+
input_schema(
|
|
11
|
+
properties: {
|
|
12
|
+
message: { type: 'string', description: 'Message content to broadcast' },
|
|
13
|
+
capability: { type: 'string', description: 'If provided, multicast only to agents with this capability' }
|
|
14
|
+
},
|
|
15
|
+
required: %w[message]
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
def call(message:, capability: nil)
|
|
20
|
+
return error_response('lex-mesh is not available') unless mesh_available?
|
|
21
|
+
|
|
22
|
+
pattern = capability ? :multicast : :broadcast
|
|
23
|
+
result = mesh_client.send_message(
|
|
24
|
+
from: 'legion.mcp',
|
|
25
|
+
to: capability || :all,
|
|
26
|
+
pattern: pattern,
|
|
27
|
+
payload: { message: message }
|
|
28
|
+
)
|
|
29
|
+
text_response(result)
|
|
30
|
+
rescue StandardError => e
|
|
31
|
+
error_response("Failed to broadcast: #{e.message}")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def mesh_available?
|
|
37
|
+
defined?(Legion::Extensions::Mesh::Client)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def mesh_client
|
|
41
|
+
@mesh_client ||= Legion::Extensions::Mesh::Client.new
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def text_response(data)
|
|
45
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump(data) }])
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def error_response(msg)
|
|
49
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump({ error: msg }) }], error: true)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module MCP
|
|
5
|
+
module Tools
|
|
6
|
+
class DatasetList < ::MCP::Tool
|
|
7
|
+
tool_name 'legion.dataset_list'
|
|
8
|
+
description 'List all stored datasets with their latest version and row counts.'
|
|
9
|
+
|
|
10
|
+
input_schema(properties: {})
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def call
|
|
14
|
+
return error_response('lex-dataset is not loaded') unless extension_loaded?('dataset')
|
|
15
|
+
|
|
16
|
+
require 'legion/extensions/dataset/client'
|
|
17
|
+
client = Legion::Extensions::Dataset::Client.new(db: db)
|
|
18
|
+
result = client.list_datasets
|
|
19
|
+
text_response(result)
|
|
20
|
+
rescue StandardError => e
|
|
21
|
+
error_response("Failed to list datasets: #{e.message}")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def extension_loaded?(name)
|
|
27
|
+
require "legion/extensions/#{name}"
|
|
28
|
+
true
|
|
29
|
+
rescue LoadError
|
|
30
|
+
false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def db
|
|
34
|
+
Legion::Data.db
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def text_response(data)
|
|
38
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump(data) }])
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def error_response(msg)
|
|
42
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump({ error: msg }) }], error: true)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module MCP
|
|
5
|
+
module Tools
|
|
6
|
+
class DatasetShow < ::MCP::Tool
|
|
7
|
+
tool_name 'legion.dataset_show'
|
|
8
|
+
description 'Retrieve a dataset by name including all rows, optionally pinned to a specific version.'
|
|
9
|
+
|
|
10
|
+
input_schema(
|
|
11
|
+
properties: {
|
|
12
|
+
name: { type: 'string', description: 'Name of the dataset' },
|
|
13
|
+
version: { type: 'integer', description: 'Specific version to fetch (default: latest)' }
|
|
14
|
+
},
|
|
15
|
+
required: ['name']
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
def call(name:, version: nil)
|
|
20
|
+
return error_response('lex-dataset is not loaded') unless extension_loaded?('dataset')
|
|
21
|
+
|
|
22
|
+
require 'legion/extensions/dataset/client'
|
|
23
|
+
client = Legion::Extensions::Dataset::Client.new(db: db)
|
|
24
|
+
result = client.get_dataset(name: name, version: version)
|
|
25
|
+
text_response(result)
|
|
26
|
+
rescue StandardError => e
|
|
27
|
+
error_response("Failed to fetch dataset: #{e.message}")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def extension_loaded?(name)
|
|
33
|
+
require "legion/extensions/#{name}"
|
|
34
|
+
true
|
|
35
|
+
rescue LoadError
|
|
36
|
+
false
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def db
|
|
40
|
+
Legion::Data.db
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def text_response(data)
|
|
44
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump(data) }])
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def error_response(msg)
|
|
48
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump({ error: msg }) }], error: true)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module MCP
|
|
5
|
+
module Tools
|
|
6
|
+
class EvalList < ::MCP::Tool
|
|
7
|
+
tool_name 'legion.eval_list'
|
|
8
|
+
description 'List all available evaluator templates (LLM-as-judge and code-based).'
|
|
9
|
+
|
|
10
|
+
input_schema(properties: {})
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def call
|
|
14
|
+
return error_response('lex-eval is not loaded') unless extension_loaded?('eval')
|
|
15
|
+
|
|
16
|
+
require 'legion/extensions/eval/client'
|
|
17
|
+
client = Legion::Extensions::Eval::Client.new(db: db)
|
|
18
|
+
result = client.list_evaluators
|
|
19
|
+
text_response(result)
|
|
20
|
+
rescue StandardError => e
|
|
21
|
+
error_response("Failed to list evaluators: #{e.message}")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def extension_loaded?(name)
|
|
27
|
+
require "legion/extensions/#{name}"
|
|
28
|
+
true
|
|
29
|
+
rescue LoadError
|
|
30
|
+
false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def db
|
|
34
|
+
Legion::Data.db
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def text_response(data)
|
|
38
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump(data) }])
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def error_response(msg)
|
|
42
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump({ error: msg }) }], error: true)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module MCP
|
|
5
|
+
module Tools
|
|
6
|
+
class EvalResults < ::MCP::Tool
|
|
7
|
+
tool_name 'legion.eval_results'
|
|
8
|
+
description 'Retrieve stored results for a named experiment from the dataset experiment store.'
|
|
9
|
+
|
|
10
|
+
input_schema(
|
|
11
|
+
properties: {
|
|
12
|
+
experiment_name: { type: 'string', description: 'Name of the experiment to retrieve results for' }
|
|
13
|
+
},
|
|
14
|
+
required: ['experiment_name']
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
def call(experiment_name:)
|
|
19
|
+
return error_response('lex-dataset is not loaded') unless extension_loaded?('dataset')
|
|
20
|
+
|
|
21
|
+
require 'legion/extensions/dataset/client'
|
|
22
|
+
client = Legion::Extensions::Dataset::Client.new(db: db)
|
|
23
|
+
result = fetch_experiment(client, experiment_name)
|
|
24
|
+
text_response(result)
|
|
25
|
+
rescue StandardError => e
|
|
26
|
+
error_response("Failed to fetch eval results: #{e.message}")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def fetch_experiment(client, name)
|
|
32
|
+
db_handle = client.instance_variable_get(:@db)
|
|
33
|
+
return { error: 'database_unavailable' } unless db_handle
|
|
34
|
+
|
|
35
|
+
exp = db_handle[:experiments].where(name: name).first
|
|
36
|
+
return { error: 'not_found' } unless exp
|
|
37
|
+
|
|
38
|
+
rows = db_handle[:experiment_results]
|
|
39
|
+
.where(experiment_id: exp[:id])
|
|
40
|
+
.order(:row_index)
|
|
41
|
+
.all
|
|
42
|
+
.map { |r| { row_index: r[:row_index], passed: r[:passed], latency_ms: r[:latency_ms] } }
|
|
43
|
+
|
|
44
|
+
summary = begin
|
|
45
|
+
::JSON.parse(exp[:summary], symbolize_names: true)
|
|
46
|
+
rescue StandardError
|
|
47
|
+
{}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
{ experiment_id: exp[:id], name: exp[:name], status: exp[:status],
|
|
51
|
+
created_at: exp[:created_at], completed_at: exp[:completed_at],
|
|
52
|
+
summary: summary, rows: rows }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def extension_loaded?(name)
|
|
56
|
+
require "legion/extensions/#{name}"
|
|
57
|
+
true
|
|
58
|
+
rescue LoadError
|
|
59
|
+
false
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def db
|
|
63
|
+
Legion::Data.db
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def text_response(data)
|
|
67
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump(data) }])
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def error_response(msg)
|
|
71
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump({ error: msg }) }], error: true)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module MCP
|
|
5
|
+
module Tools
|
|
6
|
+
class EvalRun < ::MCP::Tool
|
|
7
|
+
tool_name 'legion.eval_run'
|
|
8
|
+
description 'Run an evaluator against a single input/output pair and return pass/fail with score.'
|
|
9
|
+
|
|
10
|
+
input_schema(
|
|
11
|
+
properties: {
|
|
12
|
+
evaluator_name: { type: 'string', description: 'Name of the evaluator template to use' },
|
|
13
|
+
input: { type: 'string', description: 'The original input/prompt given to the model' },
|
|
14
|
+
output: { type: 'string', description: 'The model output to evaluate' },
|
|
15
|
+
expected: { type: 'string', description: 'Optional expected/reference output for comparison' }
|
|
16
|
+
},
|
|
17
|
+
required: %w[evaluator_name input output]
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
class << self
|
|
21
|
+
def call(evaluator_name:, input:, output:, expected: nil)
|
|
22
|
+
return error_response('lex-eval is not loaded') unless extension_loaded?('eval')
|
|
23
|
+
|
|
24
|
+
require 'legion/extensions/eval/client'
|
|
25
|
+
client = Legion::Extensions::Eval::Client.new(db: db)
|
|
26
|
+
inputs = [{ input: input, output: output, expected: expected }.compact]
|
|
27
|
+
result = client.run_evaluation(evaluator_name: evaluator_name, inputs: inputs)
|
|
28
|
+
text_response(result)
|
|
29
|
+
rescue StandardError => e
|
|
30
|
+
error_response("Failed to run evaluation: #{e.message}")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def extension_loaded?(name)
|
|
36
|
+
require "legion/extensions/#{name}"
|
|
37
|
+
true
|
|
38
|
+
rescue LoadError
|
|
39
|
+
false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def db
|
|
43
|
+
Legion::Data.db
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def text_response(data)
|
|
47
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump(data) }])
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def error_response(msg)
|
|
51
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump({ error: msg }) }], error: true)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module MCP
|
|
5
|
+
module Tools
|
|
6
|
+
class ExperimentResults < ::MCP::Tool
|
|
7
|
+
tool_name 'legion.experiment_results'
|
|
8
|
+
description 'Retrieve stored results for a named experiment, including per-row pass/fail and summary stats.'
|
|
9
|
+
|
|
10
|
+
input_schema(
|
|
11
|
+
properties: {
|
|
12
|
+
name: { type: 'string', description: 'Name of the experiment to retrieve results for' }
|
|
13
|
+
},
|
|
14
|
+
required: ['name']
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
def call(name:)
|
|
19
|
+
return error_response('lex-dataset is not loaded') unless extension_loaded?('dataset')
|
|
20
|
+
|
|
21
|
+
require 'legion/extensions/dataset/client'
|
|
22
|
+
client = Legion::Extensions::Dataset::Client.new(db: db)
|
|
23
|
+
result = fetch_experiment(client, name)
|
|
24
|
+
text_response(result)
|
|
25
|
+
rescue StandardError => e
|
|
26
|
+
error_response("Failed to fetch experiment results: #{e.message}")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def fetch_experiment(client, name)
|
|
32
|
+
db_handle = client.instance_variable_get(:@db)
|
|
33
|
+
return { error: 'database_unavailable' } unless db_handle
|
|
34
|
+
|
|
35
|
+
exp = db_handle[:experiments].where(name: name).first
|
|
36
|
+
return { error: 'not_found' } unless exp
|
|
37
|
+
|
|
38
|
+
rows = db_handle[:experiment_results]
|
|
39
|
+
.where(experiment_id: exp[:id])
|
|
40
|
+
.order(:row_index)
|
|
41
|
+
.all
|
|
42
|
+
.map { |r| { row_index: r[:row_index], passed: r[:passed], latency_ms: r[:latency_ms] } }
|
|
43
|
+
|
|
44
|
+
summary = begin
|
|
45
|
+
::JSON.parse(exp[:summary], symbolize_names: true)
|
|
46
|
+
rescue StandardError
|
|
47
|
+
{}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
{ experiment_id: exp[:id], name: exp[:name], status: exp[:status],
|
|
51
|
+
created_at: exp[:created_at], completed_at: exp[:completed_at],
|
|
52
|
+
summary: summary, rows: rows }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def extension_loaded?(name)
|
|
56
|
+
require "legion/extensions/#{name}"
|
|
57
|
+
true
|
|
58
|
+
rescue LoadError
|
|
59
|
+
false
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def db
|
|
63
|
+
Legion::Data.db
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def text_response(data)
|
|
67
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump(data) }])
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def error_response(msg)
|
|
71
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump({ error: msg }) }], error: true)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module MCP
|
|
5
|
+
module Tools
|
|
6
|
+
class ListPeers < ::MCP::Tool
|
|
7
|
+
tool_name 'legion.list_peers'
|
|
8
|
+
description 'List all registered mesh agents, optionally filtered by capability.'
|
|
9
|
+
|
|
10
|
+
input_schema(
|
|
11
|
+
properties: {
|
|
12
|
+
capability: { type: 'string', description: 'Filter agents by capability' }
|
|
13
|
+
}
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
def call(capability: nil)
|
|
18
|
+
return error_response('lex-mesh is not available') unless mesh_available?
|
|
19
|
+
|
|
20
|
+
result = mesh_client.find_agents(capability: capability)
|
|
21
|
+
text_response(result)
|
|
22
|
+
rescue StandardError => e
|
|
23
|
+
error_response("Failed to list peers: #{e.message}")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def mesh_available?
|
|
29
|
+
defined?(Legion::Extensions::Mesh::Client)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def mesh_client
|
|
33
|
+
@mesh_client ||= Legion::Extensions::Mesh::Client.new
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def text_response(data)
|
|
37
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump(data) }])
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def error_response(msg)
|
|
41
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump({ error: msg }) }], error: true)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module MCP
|
|
5
|
+
module Tools
|
|
6
|
+
class MeshStatus < ::MCP::Tool
|
|
7
|
+
tool_name 'legion.mesh_status'
|
|
8
|
+
description 'Get current mesh network status including registered agents and topology.'
|
|
9
|
+
|
|
10
|
+
input_schema(properties: {})
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def call
|
|
14
|
+
return error_response('lex-mesh is not available') unless mesh_available?
|
|
15
|
+
|
|
16
|
+
result = mesh_client.mesh_status
|
|
17
|
+
text_response(result)
|
|
18
|
+
rescue StandardError => e
|
|
19
|
+
error_response("Failed to get mesh status: #{e.message}")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def mesh_available?
|
|
25
|
+
defined?(Legion::Extensions::Mesh::Client)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def mesh_client
|
|
29
|
+
@mesh_client ||= Legion::Extensions::Mesh::Client.new
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def text_response(data)
|
|
33
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump(data) }])
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def error_response(msg)
|
|
37
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump({ error: msg }) }], error: true)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module MCP
|
|
5
|
+
module Tools
|
|
6
|
+
class NotifyPeer < ::MCP::Tool
|
|
7
|
+
tool_name 'legion.notify_peer'
|
|
8
|
+
description 'Send a fire-and-forget async notification to a specific mesh agent.'
|
|
9
|
+
|
|
10
|
+
input_schema(
|
|
11
|
+
properties: {
|
|
12
|
+
to: { type: 'string', description: 'Target agent ID' },
|
|
13
|
+
message: { type: 'string', description: 'Notification content to send' }
|
|
14
|
+
},
|
|
15
|
+
required: %w[to message]
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
def call(to:, message:)
|
|
20
|
+
return error_response('lex-mesh is not available') unless mesh_available?
|
|
21
|
+
|
|
22
|
+
result = mesh_client.send_message(
|
|
23
|
+
from: 'legion.mcp',
|
|
24
|
+
to: to,
|
|
25
|
+
pattern: :unicast,
|
|
26
|
+
payload: { message: message }
|
|
27
|
+
)
|
|
28
|
+
text_response(result)
|
|
29
|
+
rescue StandardError => e
|
|
30
|
+
error_response("Failed to notify peer: #{e.message}")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def mesh_available?
|
|
36
|
+
defined?(Legion::Extensions::Mesh::Client)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def mesh_client
|
|
40
|
+
@mesh_client ||= Legion::Extensions::Mesh::Client.new
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def text_response(data)
|
|
44
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump(data) }])
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def error_response(msg)
|
|
48
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump({ error: msg }) }], error: true)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module MCP
|
|
5
|
+
module Tools
|
|
6
|
+
class PromptList < ::MCP::Tool
|
|
7
|
+
tool_name 'legion.prompt_list'
|
|
8
|
+
description 'List all stored LLM prompt templates with their latest version and metadata.'
|
|
9
|
+
|
|
10
|
+
input_schema(properties: {})
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def call
|
|
14
|
+
return error_response('lex-prompt is not loaded') unless extension_loaded?('prompt')
|
|
15
|
+
|
|
16
|
+
require 'legion/extensions/prompt/client'
|
|
17
|
+
client = Legion::Extensions::Prompt::Client.new(db: db)
|
|
18
|
+
result = client.list_prompts
|
|
19
|
+
text_response(result)
|
|
20
|
+
rescue StandardError => e
|
|
21
|
+
error_response("Failed to list prompts: #{e.message}")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def extension_loaded?(name)
|
|
27
|
+
require "legion/extensions/#{name}"
|
|
28
|
+
true
|
|
29
|
+
rescue LoadError
|
|
30
|
+
false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def db
|
|
34
|
+
Legion::Data.db
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def text_response(data)
|
|
38
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump(data) }])
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def error_response(msg)
|
|
42
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump({ error: msg }) }], error: true)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module MCP
|
|
5
|
+
module Tools
|
|
6
|
+
class PromptRun < ::MCP::Tool
|
|
7
|
+
tool_name 'legion.prompt_run'
|
|
8
|
+
description 'Render a prompt template with variable substitution and return the final text.'
|
|
9
|
+
|
|
10
|
+
input_schema(
|
|
11
|
+
properties: {
|
|
12
|
+
name: { type: 'string', description: 'Name of the prompt template to render' },
|
|
13
|
+
variables: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
description: 'Key/value pairs to substitute into the ERB template',
|
|
16
|
+
additionalProperties: true
|
|
17
|
+
},
|
|
18
|
+
version: { type: 'integer', description: 'Specific version to render (default: latest)' }
|
|
19
|
+
},
|
|
20
|
+
required: ['name']
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
class << self
|
|
24
|
+
def call(name:, variables: {}, version: nil)
|
|
25
|
+
return error_response('lex-prompt is not loaded') unless extension_loaded?('prompt')
|
|
26
|
+
|
|
27
|
+
require 'legion/extensions/prompt/client'
|
|
28
|
+
client = Legion::Extensions::Prompt::Client.new(db: db)
|
|
29
|
+
result = client.render_prompt(name: name, variables: variables, version: version)
|
|
30
|
+
text_response(result)
|
|
31
|
+
rescue StandardError => e
|
|
32
|
+
error_response("Failed to render prompt: #{e.message}")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def extension_loaded?(name)
|
|
38
|
+
require "legion/extensions/#{name}"
|
|
39
|
+
true
|
|
40
|
+
rescue LoadError
|
|
41
|
+
false
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def db
|
|
45
|
+
Legion::Data.db
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def text_response(data)
|
|
49
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump(data) }])
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def error_response(msg)
|
|
53
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump({ error: msg }) }], error: true)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module MCP
|
|
5
|
+
module Tools
|
|
6
|
+
class PromptShow < ::MCP::Tool
|
|
7
|
+
tool_name 'legion.prompt_show'
|
|
8
|
+
description 'Retrieve a prompt template by name, optionally pinned to a specific version or tag.'
|
|
9
|
+
|
|
10
|
+
input_schema(
|
|
11
|
+
properties: {
|
|
12
|
+
name: { type: 'string', description: 'Name of the prompt template' },
|
|
13
|
+
version: { type: 'integer', description: 'Specific version number to fetch (default: latest)' },
|
|
14
|
+
tag: { type: 'string', description: 'Named tag to resolve (e.g. "stable", "production")' }
|
|
15
|
+
},
|
|
16
|
+
required: ['name']
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
def call(name:, version: nil, tag: nil)
|
|
21
|
+
return error_response('lex-prompt is not loaded') unless extension_loaded?('prompt')
|
|
22
|
+
|
|
23
|
+
require 'legion/extensions/prompt/client'
|
|
24
|
+
client = Legion::Extensions::Prompt::Client.new(db: db)
|
|
25
|
+
result = client.get_prompt(name: name, version: version, tag: tag)
|
|
26
|
+
text_response(result)
|
|
27
|
+
rescue StandardError => e
|
|
28
|
+
error_response("Failed to fetch prompt: #{e.message}")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def extension_loaded?(name)
|
|
34
|
+
require "legion/extensions/#{name}"
|
|
35
|
+
true
|
|
36
|
+
rescue LoadError
|
|
37
|
+
false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def db
|
|
41
|
+
Legion::Data.db
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def text_response(data)
|
|
45
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump(data) }])
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def error_response(msg)
|
|
49
|
+
::MCP::Tool::Response.new([{ type: 'text', text: Legion::JSON.dump({ error: msg }) }], error: true)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
data/lib/legion/mcp/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: legion-mcp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -110,6 +110,7 @@ files:
|
|
|
110
110
|
- ".rubocop.yml"
|
|
111
111
|
- CHANGELOG.md
|
|
112
112
|
- CLAUDE.md
|
|
113
|
+
- CODEOWNERS
|
|
113
114
|
- Gemfile
|
|
114
115
|
- LICENSE
|
|
115
116
|
- README.md
|
|
@@ -134,9 +135,13 @@ files:
|
|
|
134
135
|
- lib/legion/mcp/server.rb
|
|
135
136
|
- lib/legion/mcp/tier_router.rb
|
|
136
137
|
- lib/legion/mcp/tool_governance.rb
|
|
138
|
+
- lib/legion/mcp/tools/ask_peer.rb
|
|
139
|
+
- lib/legion/mcp/tools/broadcast_peers.rb
|
|
137
140
|
- lib/legion/mcp/tools/create_chain.rb
|
|
138
141
|
- lib/legion/mcp/tools/create_relationship.rb
|
|
139
142
|
- lib/legion/mcp/tools/create_schedule.rb
|
|
143
|
+
- lib/legion/mcp/tools/dataset_list.rb
|
|
144
|
+
- lib/legion/mcp/tools/dataset_show.rb
|
|
140
145
|
- lib/legion/mcp/tools/delete_chain.rb
|
|
141
146
|
- lib/legion/mcp/tools/delete_relationship.rb
|
|
142
147
|
- lib/legion/mcp/tools/delete_schedule.rb
|
|
@@ -146,6 +151,10 @@ files:
|
|
|
146
151
|
- lib/legion/mcp/tools/discover_tools.rb
|
|
147
152
|
- lib/legion/mcp/tools/do_action.rb
|
|
148
153
|
- lib/legion/mcp/tools/enable_extension.rb
|
|
154
|
+
- lib/legion/mcp/tools/eval_list.rb
|
|
155
|
+
- lib/legion/mcp/tools/eval_results.rb
|
|
156
|
+
- lib/legion/mcp/tools/eval_run.rb
|
|
157
|
+
- lib/legion/mcp/tools/experiment_results.rb
|
|
149
158
|
- lib/legion/mcp/tools/get_config.rb
|
|
150
159
|
- lib/legion/mcp/tools/get_extension.rb
|
|
151
160
|
- lib/legion/mcp/tools/get_status.rb
|
|
@@ -153,11 +162,17 @@ files:
|
|
|
153
162
|
- lib/legion/mcp/tools/get_task_logs.rb
|
|
154
163
|
- lib/legion/mcp/tools/list_chains.rb
|
|
155
164
|
- lib/legion/mcp/tools/list_extensions.rb
|
|
165
|
+
- lib/legion/mcp/tools/list_peers.rb
|
|
156
166
|
- lib/legion/mcp/tools/list_relationships.rb
|
|
157
167
|
- lib/legion/mcp/tools/list_schedules.rb
|
|
158
168
|
- lib/legion/mcp/tools/list_tasks.rb
|
|
159
169
|
- lib/legion/mcp/tools/list_workers.rb
|
|
170
|
+
- lib/legion/mcp/tools/mesh_status.rb
|
|
171
|
+
- lib/legion/mcp/tools/notify_peer.rb
|
|
160
172
|
- lib/legion/mcp/tools/plan_action.rb
|
|
173
|
+
- lib/legion/mcp/tools/prompt_list.rb
|
|
174
|
+
- lib/legion/mcp/tools/prompt_run.rb
|
|
175
|
+
- lib/legion/mcp/tools/prompt_show.rb
|
|
161
176
|
- lib/legion/mcp/tools/rbac_assignments.rb
|
|
162
177
|
- lib/legion/mcp/tools/rbac_check.rb
|
|
163
178
|
- lib/legion/mcp/tools/rbac_grants.rb
|