Module: TalkToYourApp::Transport::RailsMount

Defined in:
lib/talk_to_your_app/transport/rails_mount.rb

Overview

Builds the Rack application the host app mounts. It assembles an MCP::Server from the enabled plugins' tools, wraps it in the SDK's Streamable HTTP transport, and fronts the whole thing with the auth middleware. config.stateless selects the transport's stateless mode, which holds no per-session state — required when the host app runs more than one worker or replica.

Class Method Summary collapse

Class Method Details

.buildObject



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/talk_to_your_app/transport/rails_mount.rb', line 17

def build
  config = TalkToYourApp.configuration
  server = MCP::Server.new(
    name: config.server_name,
    title: config.server_title,
    version: config.server_version,
    description: config.server_description,
    instructions: config.instructions,
    tools: collect_tools,
  )
  transport = MCP::Server::Transports::StreamableHTTPTransport.new(
    server,
    stateless: config.stateless,
    enable_json_response: true,
    allowed_hosts: config.allowed_hosts,
  )
  app = Auth::Middleware.new(transport)

  # `config.enabled` is checked per request rather than captured here:
  # `rack_app` is memoized and the host mounts it in routes.rb, which can
  # be evaluated before the operator's initializer sets `enabled`. Reading
  # it live means toggling the flag is honored without a stale-app
  # surprise. The 503 sits in front of auth — nothing to protect when the
  # gem is disabled.
  lambda do |env|
    return disabled_response unless TalkToYourApp.configuration.enabled

    app.call(env)
  end
end

.collect_toolsObject

Compiles every enabled plugin's tools into MCP::Tool subclasses, each audit-wrapped with its plugin name and (optional) per-plugin log level.



56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/talk_to_your_app/transport/rails_mount.rb', line 56

def collect_tools
  TalkToYourApp.enabled_plugins.flat_map do |name, plugin_class, _opts|
    next [] unless plugin_class

    plugin_class.tools.map do |tool_class|
      # A plugin may tailor a tool's description to the operator's config
      # (e.g. the DB plugin flags a writable connection). nil -> the tool's
      # own static description is used.
      description = plugin_class.describe_tool(tool_class)
      tool_class.to_mcp_tool(plugin_name: name, log_level: plugin_class.log_level, description: description)
    end
  end
end

.disabled_responseObject

503 (not 404) so monitoring and host catch-all routes can tell "gem disabled" apart from "route missing".



50
51
52
# File 'lib/talk_to_your_app/transport/rails_mount.rb', line 50

def disabled_response
  [503, { "content-type" => "text/plain" }, ["talk_to_your_app: disabled"]]
end