Module: Legion::MCP::ToolQuality
- Defined in:
- lib/legion/mcp/tool_quality.rb
Constant Summary collapse
- MIN_DESCRIPTION_LENGTH =
20- MIN_PARAM_DESCRIPTION_LENGTH =
5- EXPANDED_CATEGORIES =
{ knowledge: { tools: %w[legion.query_knowledge legion.knowledge_health legion.knowledge_context legion.absorb], summary: 'Knowledge base operations — query, health, context retrieval, content absorption.' }, mesh: { tools: %w[legion.ask_peer legion.list_peers legion.notify_peer legion.broadcast_peers legion.mesh_status], summary: 'Agent mesh communication — peer queries, notifications, broadcasts, and mesh topology.' }, mind_growth: { tools: %w[legion.mind_growth_status legion.mind_growth_propose legion.mind_growth_approve legion.mind_growth_build_queue legion.mind_growth_cognitive_profile legion.mind_growth_health], summary: 'Cognitive growth — proposals, approvals, build queue, cognitive profiling, fitness scores.' }, prompts: { tools: %w[legion.prompt_list legion.prompt_show legion.prompt_run], summary: 'Prompt template management — list, view, and render prompt templates.' }, datasets: { tools: %w[legion.dataset_list legion.dataset_show legion.experiment_results], summary: 'Dataset and experiment browsing — list datasets, view rows, compare experiment results.' }, evals: { tools: %w[legion.eval_list legion.eval_run legion.eval_results], summary: 'Evaluation management — list evaluators, run evaluations, view results.' }, meta: { tools: %w[legion.do legion.tools legion.plan_action legion.structural_index], summary: 'Meta-tools — natural language routing, tool discovery, planning, structural index.' } }.freeze
Class Method Summary collapse
- .audit_all ⇒ Object
- .audit_tool(tool_class) ⇒ Object
- .capability_matrix ⇒ Object
- .check_description(tool_class) ⇒ Object
- .check_params(tool_class) ⇒ Object
- .reads?(tool_class) ⇒ Boolean
- .resolve_category(tool_class) ⇒ Object
- .summary ⇒ Object
- .writes?(tool_class) ⇒ Boolean
Class Method Details
.audit_all ⇒ Object
11 12 13 |
# File 'lib/legion/mcp/tool_quality.rb', line 11 def audit_all Server.tool_registry.map { |tc| audit_tool(tc) } end |
.audit_tool(tool_class) ⇒ Object
15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/legion/mcp/tool_quality.rb', line 15 def audit_tool(tool_class) issues = [] issues.concat(check_description(tool_class)) issues.concat(check_params(tool_class)) { name: tool_class.tool_name, description: tool_class.description, category: resolve_category(tool_class), issues: issues, quality: issues.empty? ? :pass : :warn } end |
.capability_matrix ⇒ Object
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/legion/mcp/tool_quality.rb', line 66 def capability_matrix Server.tool_registry.map do |tc| raw_schema = tc.input_schema schema = raw_schema.is_a?(Hash) ? raw_schema : raw_schema.to_h properties = schema[:properties] || {} required = schema[:required] || [] { name: tc.tool_name, category: resolve_category(tc), param_count: properties.size, required: required.map(&:to_s), reads: reads?(tc), writes: writes?(tc), catalog: tc.respond_to?(:catalog_entry) && tc.catalog_entry } end end |
.check_description(tool_class) ⇒ Object
29 30 31 32 33 34 35 |
# File 'lib/legion/mcp/tool_quality.rb', line 29 def check_description(tool_class) issues = [] desc = tool_class.description.to_s issues << 'description missing' if desc.empty? issues << "description too short (#{desc.length} chars, min #{MIN_DESCRIPTION_LENGTH})" if desc.length < MIN_DESCRIPTION_LENGTH issues end |
.check_params(tool_class) ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/legion/mcp/tool_quality.rb', line 37 def check_params(tool_class) issues = [] raw_schema = tool_class.input_schema schema = raw_schema.is_a?(Hash) ? raw_schema : raw_schema.to_h properties = schema[:properties] || {} properties.each do |param_name, | = .is_a?(Hash) ? : {} desc = [:description].to_s issues << "param '#{param_name}' missing or short description" if desc.length < MIN_PARAM_DESCRIPTION_LENGTH end issues end |
.reads?(tool_class) ⇒ Boolean
85 86 87 88 89 90 91 |
# File 'lib/legion/mcp/tool_quality.rb', line 85 def reads?(tool_class) name = tool_class.tool_name name.start_with?('legion.list_', 'legion.get_', 'legion.show_') || name.include?('query') || name.include?('search') || name.include?('status') || name.include?('health') || name.include?('stats') || name.include?('describe') end |
.resolve_category(tool_class) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/legion/mcp/tool_quality.rb', line 52 def resolve_category(tool_class) return tool_class.mcp_category.to_sym if tool_class.respond_to?(:mcp_category) && tool_class.mcp_category ContextCompiler::CATEGORIES.each do |cat, config| return cat if config[:tools].include?(tool_class.tool_name) end EXPANDED_CATEGORIES.each do |cat, config| return cat if config[:tools].include?(tool_class.tool_name) end :uncategorized end |
.summary ⇒ Object
102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/legion/mcp/tool_quality.rb', line 102 def summary results = audit_all pass_count = results.count { |r| r[:quality] == :pass } warn_count = results.count { |r| r[:quality] == :warn } categories = results.group_by { |r| r[:category] } { total_tools: results.size, passing: pass_count, warnings: warn_count, by_category: categories.transform_values(&:size), issues: results.select { |r| r[:quality] == :warn } } end |
.writes?(tool_class) ⇒ Boolean
93 94 95 96 97 98 99 100 |
# File 'lib/legion/mcp/tool_quality.rb', line 93 def writes?(tool_class) name = tool_class.tool_name name.start_with?('legion.create_', 'legion.update_', 'legion.delete_') || name.start_with?('legion.enable_', 'legion.disable_') || name.include?('run') || name.include?('approve') || name.include?('propose') || name.include?('absorb') || name.include?('broadcast') || name.include?('notify') end |