Module: RailsAiContext::McpEdge

Defined in:
lib/rails_ai_context/mcp_edge.rb

Overview

What the three HTTP entry points - the standalone rack app, the Rack middleware and the engine controller - have in common: how a transport is built, and what a transport failure looks like on the wire. Their genuine differences (streaming, header handling, memoization lifetime) stay with them.

Constant Summary collapse

INTERNAL_ERROR =
-32603

Class Method Summary collapse

Class Method Details

.build_transport(app = nil) ⇒ Object

Memoization is the caller's: the middleware holds one per instance, the controller one per class, and the standalone server one per process.



37
38
39
40
# File 'lib/rails_ai_context/mcp_edge.rb', line 37

def build_transport(app = nil)
  app ||= Rails.application
  MCP::Server::Transports::StreamableHTTPTransport.new(Server.new(app, transport: :http).build)
end

.internal_error_frame(error) ⇒ Object

None of the three sits behind anything that would turn an exception into a JSON-RPC reply, so each has to answer in JSON-RPC shape itself or leave the client's request loop hanging on a non-JSON body.



19
20
21
22
23
24
25
# File 'lib/rails_ai_context/mcp_edge.rb', line 19

def internal_error_frame(error)
  {
    jsonrpc: "2.0",
    error: { code: INTERNAL_ERROR, message: "Internal error: #{error.message}" },
    id: nil
  }.to_json
end

.internal_error_response(error) ⇒ Object

The Rack-shaped answer, for the two entry points that return triples.



28
29
30
31
32
# File 'lib/rails_ai_context/mcp_edge.rb', line 28

def internal_error_response(error)
  RailsAiContext.log_warn "[rails-ai-context] MCP request failed: #{error.class}: #{error.message}"

  [ 500, { "Content-Type" => "application/json" }, [ internal_error_frame(error) ] ]
end