Module: TalkToYourApp
- Defined in:
- lib/talk_to_your_app.rb,
lib/talk_to_your_app/tool.rb,
lib/talk_to_your_app/plugin.rb,
lib/talk_to_your_app/current.rb,
lib/talk_to_your_app/railtie.rb,
lib/talk_to_your_app/version.rb,
lib/talk_to_your_app/auth/basic.rb,
lib/talk_to_your_app/audit_logger.rb,
lib/talk_to_your_app/auth/api_key.rb,
lib/talk_to_your_app/configuration.rb,
lib/talk_to_your_app/auth/middleware.rb,
lib/talk_to_your_app/plugin_registry.rb,
lib/talk_to_your_app/plugins/db/plugin.rb,
lib/talk_to_your_app/connection_registry.rb,
lib/talk_to_your_app/plugins/jobs/plugin.rb,
lib/talk_to_your_app/plugins/rake/plugin.rb,
lib/talk_to_your_app/renderers/html_table.rb,
lib/talk_to_your_app/transport/rails_mount.rb,
lib/talk_to_your_app/plugins/db/tools/query.rb,
lib/talk_to_your_app/plugins/flipper/plugin.rb,
lib/talk_to_your_app/plugins/jobs/interface.rb,
lib/talk_to_your_app/plugins/rake/tools/run.rb,
lib/talk_to_your_app/plugins/db/tools/schema.rb,
lib/talk_to_your_app/plugins/db/tools/tables.rb,
lib/talk_to_your_app/plugins/jobs/tools/base.rb,
lib/talk_to_your_app/plugins/custom_tools/plugin.rb,
lib/talk_to_your_app/plugins/jobs/adapters/sidekiq.rb,
lib/talk_to_your_app/plugins/jobs/tools/failed_jobs.rb,
lib/talk_to_your_app/plugins/jobs/tools/queue_sizes.rb,
lib/talk_to_your_app/plugins/jobs/tools/recent_jobs.rb,
lib/talk_to_your_app/plugins/flipper/tools/read_flag.rb,
lib/talk_to_your_app/plugins/jobs/tools/rate_metrics.rb,
lib/talk_to_your_app/plugins/flipper/tools/list_flags.rb,
lib/talk_to_your_app/plugins/flipper/tools/enable_flag.rb,
lib/talk_to_your_app/plugins/jobs/adapters/solid_queue.rb,
lib/talk_to_your_app/plugins/flipper/tools/disable_flag.rb,
lib/talk_to_your_app/plugins/flipper/tools/enabled_flags.rb,
lib/generators/talk_to_your_app/install/install_generator.rb,
lib/generators/talk_to_your_app/custom_tool/custom_tool_generator.rb
Overview
Rails-native MCP server. See README for the configuration reference.
Defined Under Namespace
Modules: AuditLogger, Auth, ConnectionRegistry, Generators, PluginRegistry, Plugins, Renderers, Transport Classes: Configuration, ConfigurationError, Current, Plugin, Railtie, Tool
Constant Summary collapse
- APP_DIR =
Convention directory for host-app extensions: custom_tools/ holds Tool subclasses. The railtie tells Zeitwerk to ignore this tree (the gem requires the files itself via require_app_dir), so eager loading doesn't expect constants the loader would otherwise infer from the path.
"app/talk_to_your_app"- VERSION =
"0.1.0.pre.4"
Class Method Summary collapse
- .configuration ⇒ Object
-
.configure {|configuration| ... } ⇒ Object
Yields the configuration singleton for the host app's initializer.
-
.enabled_plugins ⇒ Object
The enabled plugins as [name, plugin_class, options] triples, in the order they were enabled.
-
.rack_app ⇒ Object
The Rack application to mount in the host app's routes:.
-
.rails_hosts ⇒ Object
A literal Host allowlist derived from the host app's own
Rails.application.config.hosts— a sensible default forallowed_hostsso the MCP endpoint accepts exactly the hosts the app already serves. -
.register_plugin(name, plugin_class) ⇒ Object
Registers a plugin class under a name so it can be enabled in the initializer.
-
.require_app_dir(subdir) ⇒ Object
Requires every .rb under app/talk_to_your_app/
so the files' side effects run (each defines a Tool subclass the :custom_tools plugin collects). -
.required_connections ⇒ Object
Connections required by the enabled plugins and their tools, as [connection_name, requester_label] pairs, for ConnectionRegistry.validate!.
-
.reset_configuration! ⇒ Object
Test seam: drop all configuration back to defaults.
Class Method Details
.configuration ⇒ Object
35 36 37 |
# File 'lib/talk_to_your_app.rb', line 35 def configuration @configuration ||= Configuration.new end |
.configure {|configuration| ... } ⇒ Object
Yields the configuration singleton for the host app's initializer.
TalkToYourApp.configure do |config|
config.mount_at = "/mcp"
end
Calling this more than once merges into the same object rather than replacing it.
30 31 32 33 |
# File 'lib/talk_to_your_app.rb', line 30 def configure yield(configuration) if block_given? configuration end |
.enabled_plugins ⇒ Object
The enabled plugins as [name, plugin_class, options] triples, in the order they were enabled. plugin_class is nil if the name was never registered (validation surfaces that at boot).
65 66 67 68 69 |
# File 'lib/talk_to_your_app.rb', line 65 def enabled_plugins configuration.enabled_plugins.map do |name, opts| [name, PluginRegistry[name], opts] end end |
.rack_app ⇒ Object
The Rack application to mount in the host app's routes:
mount TalkToYourApp.rack_app, at: TalkToYourApp.configuration.mount_at
Built once from the enabled plugins. reset_configuration! clears it.
51 52 53 |
# File 'lib/talk_to_your_app.rb', line 51 def rack_app @rack_app ||= Transport::RailsMount.build end |
.rails_hosts ⇒ Object
A literal Host allowlist derived from the host app's own
Rails.application.config.hosts — a sensible default for allowed_hosts
so the MCP endpoint accepts exactly the hosts the app already serves. The
transport matches hosts literally (bare name or host:port), so Rails'
regexp, IPAddr, and dotted-subdomain (".example.com") entries can't be
forwarded — only plain host strings are kept. Returns [] when Rails is
absent or defines no string hosts (the transport's loopback defaults still
apply). Wildcard/regexp hosts must be added to allowed_hosts explicitly.
103 104 105 106 107 |
# File 'lib/talk_to_your_app.rb', line 103 def rails_hosts return [] unless defined?(::Rails) && ::Rails.respond_to?(:application) && ::Rails.application Array(::Rails.application.config.hosts).grep(String).reject { |h| h.start_with?(".") } end |
.register_plugin(name, plugin_class) ⇒ Object
Registers a plugin class under a name so it can be enabled in the initializer. Bundled plugins register themselves on load; plugin authors call this from their own code.
58 59 60 |
# File 'lib/talk_to_your_app.rb', line 58 def register_plugin(name, plugin_class) PluginRegistry.register(name, plugin_class) end |
.require_app_dir(subdir) ⇒ Object
Requires every .rb under app/talk_to_your_app/
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/talk_to_your_app.rb', line 117 def require_app_dir(subdir) return unless defined?(::Rails) && ::Rails.respond_to?(:root) && ::Rails.root dir = ::Rails.root.join(APP_DIR, subdir) return unless File.directory?(dir) Dir[File.join(dir, "**/*.rb")].sort.each do |file| if block_given? yield(file, -> { require file }) else require file end rescue StandardError, ScriptError => e = "talk_to_your_app: failed to load #{file}: #{e.class}: #{e.}" logger = configuration.logger logger ? logger.error() : $stderr.puts() end end |
.required_connections ⇒ Object
Connections required by the enabled plugins and their tools, as
[connection_name, requester_label] pairs, for ConnectionRegistry.validate!.
Two sources: the operator's connection: option on each enabled plugin,
and any tool that declares a static connection DSL (custom tools). Both
must be registered, so both fail closed at boot rather than at first call.
connection: false (opted out) and a missing option are skipped — the
missing-option error is owned by PluginRegistry.validate_enabled! — so only
a real name reaches ConnectionRegistry.registered?.
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/talk_to_your_app.rb', line 79 def required_connections requirements = [] enabled_plugins.each do |name, plugin_class, opts| next unless plugin_class conn_name = opts[:connection] requirements << [conn_name, "Plugin #{name.inspect}"] if conn_name # skips false/nil plugin_class.tools.each do |tool_class| tool_conn = tool_class.connection requirements << [tool_conn, "Tool #{tool_class.tool_name.inspect}"] if tool_conn end end requirements end |
.reset_configuration! ⇒ Object
Test seam: drop all configuration back to defaults.
40 41 42 43 44 |
# File 'lib/talk_to_your_app.rb', line 40 def reset_configuration! @configuration = Configuration.new @rack_app = nil ConnectionRegistry.reset! end |