Class: Pgbus::MCP::RackApp

Inherits:
Object
  • Object
show all
Defined in:
lib/pgbus/mcp/rack_app.rb

Overview

A turnkey, gated Rack app that serves the read-only pgbus diagnostic MCP server over HTTP. Mount it inside your existing Rails app — no second process, same DB credentials, same connection pool:

# config/routes.rb
mount Pgbus::MCP.rack_app(token: ENV["PGBUS_MCP_TOKEN"]) => "/pgbus/mcp"

The underlying transport runs in stateless + JSON-response mode, which:

* makes every request a self-contained POST returning a single JSON
  object (no in-memory session, no SSE stream, no reaper thread), so it
  is safe behind multiple Puma/Falcon workers — any worker can answer
  any request;
* is exactly what a read-only diagnostic server needs (it never pushes
  server-initiated messages to the client).

Security: requests are rejected with 401 unless they carry the configured token (or pass the supplied auth callable). Run it on an internal network / behind your VPN, never internet-exposed.

Constant Summary collapse

BEARER_PREFIX =
"Bearer "
UNAUTHORIZED =
[
  401,
  { "Content-Type" => "application/json", "WWW-Authenticate" => "Bearer" },
  [{ jsonrpc: "2.0", id: nil, error: { code: -32_001, message: "Unauthorized" } }.to_json]
].freeze

Instance Method Summary collapse

Constructor Details

#initialize(data_source: Pgbus::Web::DataSource.new, allow_payloads: false, token: nil, auth: nil) ⇒ RackApp

Returns a new instance of RackApp.

Parameters:

  • data_source (Pgbus::Web::DataSource) (defaults to: Pgbus::Web::DataSource.new)

    read layer the tools query. Built once and shared: DataSource acquires Pgbus::BusRecord.connection from the ActiveRecord pool on each call, so a single instance is request-safe across threads/workers.

  • allow_payloads (Boolean) (defaults to: false)

    when true, tools honor a per-call include_payloads flag; otherwise message bodies are always redacted.

  • token (String, nil) (defaults to: nil)

    shared secret. When present, requests must send ‘Authorization: Bearer <token>`. nil disables the built-in gate.

  • auth (#call, nil) (defaults to: nil)

    custom authenticator taking a Rack::Request and returning truthy to allow. Mirrors Pgbus.configuration.web_auth. Takes precedence over token when both are given.



42
43
44
45
46
47
48
49
50
# File 'lib/pgbus/mcp/rack_app.rb', line 42

def initialize(data_source: Pgbus::Web::DataSource.new, allow_payloads: false, token: nil, auth: nil)
  @token = token
  @auth = auth
  @server = Server.build(data_source: data_source, allow_payloads: allow_payloads)
  @transport = ::MCP::Server::Transports::StreamableHTTPTransport.new(
    @server, stateless: true, enable_json_response: true
  )
  warn_unauthenticated! if @token.nil? && @auth.nil?
end

Instance Method Details

#call(env) ⇒ Object

Mount THIS object, never the bare transport. The auth gate lives here and runs before handle_request for every HTTP method (POST/GET/ DELETE/…). Mounting @transport directly would skip authorization —the gem’s transport has no auth of its own.



56
57
58
59
60
61
# File 'lib/pgbus/mcp/rack_app.rb', line 56

def call(env)
  request = Rack::Request.new(env)
  return UNAUTHORIZED unless authorized?(request)

  @transport.handle_request(request)
end