Module: RosettAi::Mcp::Plugins
- Defined in:
- lib/rosett_ai/mcp/plugins.rb
Overview
Plugin auto-discovery and loading for MCP server.
Discovers installed gems named rosett-ai-mcp-* and loads them
as MCP server plugins. Convention: gem rosett-ai-mcp-foo defines
RosettAiMcp::Plugins::Foo with a .register(server) method.
Governance (built-in tools) is always loaded first.
Constant Summary collapse
- PLUGIN_MUTEX =
Mutex.new
- PLUGIN_NAMESPACE =
Allowlist prefix for plugin constant resolution.
'RosettAiMcp::Plugins::'
Class Method Summary collapse
-
.available?(name) ⇒ Boolean
Check if a plugin is available (registered or gem installed).
-
.discover_plugin_gems ⇒ Array<Symbol>
Discover installed gems named rosett-ai-mcp-*.
-
.load_all(server, plugin_names = []) ⇒ Array<Symbol>
Load all discovered plugins and register with server.
-
.load_plugin(server, name) ⇒ Boolean
Load a single plugin by name.
-
.plugin_registry ⇒ Hash
Thread-safe access to the plugin store.
-
.register(name, plugin_class)
Register a plugin class by name.
-
.registry ⇒ Hash
Read-only access to the plugin registry.
-
.reset!
Reset the registry (for testing).
Class Method Details
.available?(name) ⇒ Boolean
Check if a plugin is available (registered or gem installed).
36 37 38 |
# File 'lib/rosett_ai/mcp/plugins.rb', line 36 def available?(name) PLUGIN_MUTEX.synchronize { plugin_registry.key?(name.to_sym) } || gem_available?(name) end |
.discover_plugin_gems ⇒ Array<Symbol>
Discover installed gems named rosett-ai-mcp-*.
79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/rosett_ai/mcp/plugins.rb', line 79 def discover_plugin_gems discovered = [] Gem::Specification.each do |spec| next unless spec.name.start_with?('rosett-ai-mcp-') && spec.name != 'rosett-ai-mcp' plugin_name = spec.name.sub('rosett-ai-mcp-', '') discovered << plugin_name.to_sym end discovered rescue StandardError [] end |
.load_all(server, plugin_names = []) ⇒ Array<Symbol>
Load all discovered plugins and register with server.
45 46 47 48 49 50 51 52 53 54 |
# File 'lib/rosett_ai/mcp/plugins.rb', line 45 def load_all(server, plugin_names = []) discovered = discover_plugin_gems all_plugins = (discovered + plugin_names.map(&:to_sym)).uniq loaded = [] all_plugins.each do |name| loaded << name if load_plugin(server, name) end loaded end |
.load_plugin(server, name) ⇒ Boolean
Load a single plugin by name.
61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/rosett_ai/mcp/plugins.rb', line 61 def load_plugin(server, name) sym = name.to_sym plugin_class = PLUGIN_MUTEX.synchronize { plugin_registry[sym] } if plugin_class plugin_class.register(server) return true end require_and_register_gem(server, sym) rescue StandardError, LoadError => e warn "[rai-mcp] Plugin load failed for #{name}: #{e.}" false end |
.plugin_registry ⇒ Hash
Thread-safe access to the plugin store.
109 110 111 |
# File 'lib/rosett_ai/mcp/plugins.rb', line 109 def plugin_registry @plugin_registry ||= {} # rubocop:disable ThreadSafety/ClassInstanceVariable end |
.register(name, plugin_class)
This method returns an undefined value.
Register a plugin class by name.
28 29 30 |
# File 'lib/rosett_ai/mcp/plugins.rb', line 28 def register(name, plugin_class) PLUGIN_MUTEX.synchronize { plugin_registry[name.to_sym] = plugin_class } end |
.registry ⇒ Hash
Read-only access to the plugin registry.
95 96 97 |
# File 'lib/rosett_ai/mcp/plugins.rb', line 95 def registry PLUGIN_MUTEX.synchronize { plugin_registry.dup } end |
.reset!
This method returns an undefined value.
Reset the registry (for testing).
102 103 104 |
# File 'lib/rosett_ai/mcp/plugins.rb', line 102 def reset! PLUGIN_MUTEX.synchronize { plugin_registry.clear } end |