Class: RailsAiContext::McpController
- Inherits:
-
ActionController::API
- Object
- ActionController::API
- RailsAiContext::McpController
- Includes:
- ActionController::Live
- Defined in:
- app/controllers/rails_ai_context/mcp_controller.rb
Overview
Rails controller for serving MCP over Streamable HTTP. Alternative to the Rack middleware - integrates with Rails routing, authentication, and middleware stack.
Mount in routes: mount RailsAiContext::Engine, at: "/mcp"
Defined Under Namespace
Classes: FlushableStream
Class Method Summary collapse
-
.mcp_transport ⇒ Object
Class-level memoization - transport persists across requests.
- .reset_transport! ⇒ Object
Instance Method Summary collapse
Class Method Details
.mcp_transport ⇒ Object
Class-level memoization - transport persists across requests. Thread-safe: MCP::Server and transport are stateless for reads.
135 136 137 |
# File 'app/controllers/rails_ai_context/mcp_controller.rb', line 135 def mcp_transport @transport_mutex.synchronize { @mcp_transport ||= McpEdge.build_transport } end |
.reset_transport! ⇒ Object
139 140 141 |
# File 'app/controllers/rails_ai_context/mcp_controller.rb', line 139 def reset_transport! @transport_mutex.synchronize { @mcp_transport = nil } end |
Instance Method Details
#handle ⇒ Object
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'app/controllers/rails_ai_context/mcp_controller.rb', line 29 def handle Tools::BaseTool.with_session_for(request.env) do serve_transport end rescue => e # Once the response is committed the status and headers are already on # the wire, so a JSON-RPC frame written here cannot reach the client. # Worse, assigning a body swaps the stream out from under the thread # still draining the old one, turning a truncated SSE stream into a # garbled one. The streaming branch's ensure always closes the stream, # and closing commits, so every streaming failure lands here committed. # Hand those to Live, which logs them with a backtrace and tears the # connection down. raise if response.committed? # A transport failure must still answer in JSON-RPC shape. Without this # the exception escapes into a generic Rails 500 (HTML), breaking the # client's JSON-RPC loop. RailsAiContext.log_warn "[rails-ai-context] MCP request failed: #{e.class}: #{e.}" self.status = 500 response.headers["Content-Type"] = "application/json" self.response_body = McpEdge.internal_error_frame(e) end |