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/custom_tool.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/plugins/health/plugin.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/health/registry.rb,
lib/talk_to_your_app/plugins/jobs/tools/health.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/health/tools/run_check.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/health/tools/list_checks.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,
lib/generators/talk_to_your_app/health_check/health_check_generator.rb

Overview

Rails-native MCP server. See README for the configuration reference.

Defined Under Namespace

Modules: AuditLogger, Auth, ConnectionRegistry, Generators, Health, PluginRegistry, Plugins, Renderers, Transport Classes: Configuration, ConfigurationError, Current, CustomTool, Plugin, Railtie, Tool

Constant Summary collapse

APP_DIR =

Convention directory for host-app extensions: custom_tools/ holds CustomTool subclasses, health/ holds health-check files. The railtie tells Zeitwerk to ignore this tree (health files define no constant; tools we require here), so the gem loads its files itself via require_app_dir.

"app/talk_to_your_app"
VERSION =
"0.1.0.pre.1"

Class Method Summary collapse

Class Method Details

.configurationObject



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.

Yields:



30
31
32
33
# File 'lib/talk_to_your_app.rb', line 30

def configure
  yield(configuration) if block_given?
  configuration
end

.enabled_pluginsObject

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_appObject

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

.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/<subdir> so the files’ side effects run (CustomTool subclasses register; health checks call Health.register). require is idempotent, so calling this repeatedly loads each file once per process (edits need a restart). No-op when the directory or Rails is absent. Each file is loaded in isolation: one file that fails to load is logged and skipped so it can’t suppress the others. ScriptError is rescued alongside StandardError so a syntax/load error is reported the same way as a runtime error rather than escaping uncaught.



96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/talk_to_your_app.rb', line 96

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|
    require file
  rescue StandardError, ScriptError => e
    message = "talk_to_your_app: failed to load #{file}: #{e.class}: #{e.message}"
    logger = configuration.logger
    logger ? logger.error(message) : $stderr.puts(message)
  end
end

.required_connectionsObject

Connections required by the enabled plugins and their tools, as

connection_name, requester_label

pairs, for ConnectionRegistry.validate!.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/talk_to_your_app.rb', line 73

def required_connections
  requirements = []
  enabled_plugins.each do |name, plugin_class, _opts|
    next unless plugin_class

    Array(plugin_class.required_connections).each do |conn_name|
      requirements << [conn_name, "Plugin #{name.inspect}"]
    end
    plugin_class.tools.each do |tool_class|
      requirements << [tool_class.connection, "Tool #{tool_class.tool_name.inspect}"] if tool_class.connection
    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