Module: RubynCode::MCP::Discovery
- Defined in:
- lib/rubyn_code/mcp/discovery.rb
Overview
Discovers MCP server definitions from a project's .mcp.json file
(the Claude Code–style layout at the project root) and merges them
with the user-level .rubyn-code/mcp.json definitions parsed by
MCP::Config.
Project-level entries are tagged source: :project so /mcp can
distinguish them from user-level entries.
Defined Under Namespace
Classes: Entry
Constant Summary collapse
- PROJECT_CONFIG_FILENAME =
'.mcp.json'
Class Method Summary collapse
- .build_entry(name, server_def, source) ⇒ Object
- .discover(project_root) ⇒ Array<Entry>
- .load_project(project_root) ⇒ Array<Entry>
-
.remote_servers(entries) ⇒ Array<Entry>
Entries that need a network protocol (deferred).
-
.stdio_servers(entries) ⇒ Array<Entry>
Entries that the local runner can start right now.
- .to_entry(cfg, source) ⇒ Object
Class Method Details
.build_entry(name, server_def, source) ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/rubyn_code/mcp/discovery.rb', line 71 def build_entry(name, server_def, source) return nil unless server_def.is_a?(Hash) command = server_def['command'].to_s url = server_def['url'].to_s if command.empty? && url.empty? RubynCode::Debug.warn("[MCP::Discovery] Skipping #{name}: no command or url") return nil end Entry.new( name: name, command: command, args: Array(server_def['args']), env: server_def['env'].is_a?(Hash) ? server_def['env'].transform_keys(&:to_s) : {}, url: url, source: source ) end |
.discover(project_root) ⇒ Array<Entry>
24 25 26 27 28 |
# File 'lib/rubyn_code/mcp/discovery.rb', line 24 def discover(project_root) user = MCP::Config.load(project_root.to_s).map { |cfg| to_entry(cfg, :user) } proj = load_project(project_root) user + proj end |
.load_project(project_root) ⇒ Array<Entry>
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/rubyn_code/mcp/discovery.rb', line 32 def load_project(project_root) return [] if project_root.to_s.empty? path = File.join(project_root, PROJECT_CONFIG_FILENAME) return [] unless File.exist?(path) data = JSON.parse(File.read(path)) Array(data['mcpServers']).filter_map do |name, server_def| build_entry(name, server_def, :project) end rescue JSON::ParserError => e RubynCode::Debug.warn("[MCP::Discovery] Failed to parse #{path}: #{e.}") [] rescue SystemCallError => e RubynCode::Debug.warn("[MCP::Discovery] Could not read #{path}: #{e.}") [] end |
.remote_servers(entries) ⇒ Array<Entry>
Returns entries that need a network protocol (deferred).
56 57 58 |
# File 'lib/rubyn_code/mcp/discovery.rb', line 56 def remote_servers(entries) entries.select { |e| e.command.to_s.empty? && !e.url.to_s.empty? } end |
.stdio_servers(entries) ⇒ Array<Entry>
Returns entries that the local runner can start right now.
51 52 53 |
# File 'lib/rubyn_code/mcp/discovery.rb', line 51 def stdio_servers(entries) entries.reject { |e| e.command.to_s.empty? } end |
.to_entry(cfg, source) ⇒ Object
60 61 62 63 64 65 66 67 68 69 |
# File 'lib/rubyn_code/mcp/discovery.rb', line 60 def to_entry(cfg, source) Entry.new( name: cfg[:name], command: cfg[:command], args: cfg[:args], env: cfg[:env], url: cfg[:url], source: source ) end |