legion-mcp 0.4.0 → 0.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/lib/legion/mcp/server.rb +18 -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/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 +10 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 37f03ddf4778cc238c0e69b039b9cd6c55acc2ada6207ef32c777c844d60d5e1
|
|
4
|
+
data.tar.gz: ffe2b6b6da0930d795ca3965b86f5edabdf8f6b698232b110bd3ae6187ce0c20
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a4c148c867a384494dd92847f6ec4f25b6010d73ce3f076e6d01c0f5ae47c8a7c6d8cfa909da58f067334612f7b7420e87e388c0910fad2edf604ea45a670206
|
|
7
|
+
data.tar.gz: eb8d5f6536df790298e4613a92f61f8a4e136d16d83ede2a99c92a9507ddfd8f7301fe52a12678f891b0227e1568660b77db008b09bd9e1a98f55644e64a56b2
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# legion-mcp Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.1] - 2026-03-19
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `legion.prompt_list` — list all stored prompt templates via lex-prompt Client
|
|
7
|
+
- `legion.prompt_show` — fetch a prompt by name, version, or tag via lex-prompt Client
|
|
8
|
+
- `legion.prompt_run` — render a prompt template with ERB variable substitution via lex-prompt Client
|
|
9
|
+
- `legion.dataset_list` — list all stored datasets via lex-dataset Client
|
|
10
|
+
- `legion.dataset_show` — fetch a dataset with all rows, optionally version-pinned, via lex-dataset Client
|
|
11
|
+
- `legion.experiment_results` — retrieve per-row results and summary for a named experiment from lex-dataset
|
|
12
|
+
- `legion.eval_list` — list available evaluator templates via lex-eval Client
|
|
13
|
+
- `legion.eval_run` — run a single input/output pair through a named evaluator via lex-eval Client
|
|
14
|
+
- `legion.eval_results` — retrieve stored experiment results via lex-dataset experiment store
|
|
15
|
+
- All 9 tools registered in `TOOL_CLASSES`; total tool count raised from 36 to 45
|
|
16
|
+
- Specs for all 9 new tools
|
|
17
|
+
|
|
3
18
|
## [0.4.0] - 2026-03-20
|
|
4
19
|
|
|
5
20
|
### Added
|
data/lib/legion/mcp/server.rb
CHANGED
|
@@ -35,6 +35,15 @@ 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'
|
|
@@ -81,6 +90,15 @@ module Legion
|
|
|
81
90
|
Tools::RbacCheck,
|
|
82
91
|
Tools::RbacAssignments,
|
|
83
92
|
Tools::RbacGrants,
|
|
93
|
+
Tools::PromptList,
|
|
94
|
+
Tools::PromptShow,
|
|
95
|
+
Tools::PromptRun,
|
|
96
|
+
Tools::DatasetList,
|
|
97
|
+
Tools::DatasetShow,
|
|
98
|
+
Tools::ExperimentResults,
|
|
99
|
+
Tools::EvalList,
|
|
100
|
+
Tools::EvalRun,
|
|
101
|
+
Tools::EvalResults,
|
|
84
102
|
Tools::DoAction,
|
|
85
103
|
Tools::PlanAction,
|
|
86
104
|
Tools::DiscoverTools
|
|
@@ -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,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.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -137,6 +137,8 @@ files:
|
|
|
137
137
|
- lib/legion/mcp/tools/create_chain.rb
|
|
138
138
|
- lib/legion/mcp/tools/create_relationship.rb
|
|
139
139
|
- lib/legion/mcp/tools/create_schedule.rb
|
|
140
|
+
- lib/legion/mcp/tools/dataset_list.rb
|
|
141
|
+
- lib/legion/mcp/tools/dataset_show.rb
|
|
140
142
|
- lib/legion/mcp/tools/delete_chain.rb
|
|
141
143
|
- lib/legion/mcp/tools/delete_relationship.rb
|
|
142
144
|
- lib/legion/mcp/tools/delete_schedule.rb
|
|
@@ -146,6 +148,10 @@ files:
|
|
|
146
148
|
- lib/legion/mcp/tools/discover_tools.rb
|
|
147
149
|
- lib/legion/mcp/tools/do_action.rb
|
|
148
150
|
- lib/legion/mcp/tools/enable_extension.rb
|
|
151
|
+
- lib/legion/mcp/tools/eval_list.rb
|
|
152
|
+
- lib/legion/mcp/tools/eval_results.rb
|
|
153
|
+
- lib/legion/mcp/tools/eval_run.rb
|
|
154
|
+
- lib/legion/mcp/tools/experiment_results.rb
|
|
149
155
|
- lib/legion/mcp/tools/get_config.rb
|
|
150
156
|
- lib/legion/mcp/tools/get_extension.rb
|
|
151
157
|
- lib/legion/mcp/tools/get_status.rb
|
|
@@ -158,6 +164,9 @@ files:
|
|
|
158
164
|
- lib/legion/mcp/tools/list_tasks.rb
|
|
159
165
|
- lib/legion/mcp/tools/list_workers.rb
|
|
160
166
|
- lib/legion/mcp/tools/plan_action.rb
|
|
167
|
+
- lib/legion/mcp/tools/prompt_list.rb
|
|
168
|
+
- lib/legion/mcp/tools/prompt_run.rb
|
|
169
|
+
- lib/legion/mcp/tools/prompt_show.rb
|
|
161
170
|
- lib/legion/mcp/tools/rbac_assignments.rb
|
|
162
171
|
- lib/legion/mcp/tools/rbac_check.rb
|
|
163
172
|
- lib/legion/mcp/tools/rbac_grants.rb
|