Module: Ask::Rails::MCP

Defined in:
lib/ask/rails/mcp.rb,
lib/ask/rails/mcp/version.rb

Constant Summary collapse

PROTOCOL_VERSION =
"2025-06-18"
VERSION =
"0.2.0"

Class Method Summary collapse

Class Method Details

.handle(json_string) ⇒ Object

Handle a raw JSON-RPC message string. Returns a Hash suitable for JSON serialization.



25
26
27
28
29
30
# File 'lib/ask/rails/mcp.rb', line 25

def handle(json_string)
  msg = JSON.parse(json_string)
  process_message(msg)
rescue JSON::ParserError => e
  error_response(nil, -32700, "Parse error: #{e.message}")
end

.handle_initialize(id, params) ⇒ Object

Process an initialize request.



57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/ask/rails/mcp.rb', line 57

def handle_initialize(id, params)
  @initialized = true
  client_version = params["protocolVersion"] || PROTOCOL_VERSION
  success_response(id, {
    "protocolVersion" => client_version,
    "capabilities" => { "tools" => {} },
    "serverInfo" => {
      "name" => "ask-rails-harness-mcp",
      "version" => Ask::Rails::MCP::VERSION
    }
  })
end

.handle_tool_call(id, params) ⇒ Object

Process a tools/call request.



77
78
79
80
81
82
83
# File 'lib/ask/rails/mcp.rb', line 77

def handle_tool_call(id, params)
  tool_name = params["name"]
  arguments = params["arguments"] || {}

  result = tool_server.call(tool_name, arguments)
  success_response(id, result)
end

.handle_tools_list(id) ⇒ Object

Process a tools/list request.



71
72
73
74
# File 'lib/ask/rails/mcp.rb', line 71

def handle_tools_list(id)
  defs = tool_server.definitions
  success_response(id, { tools: defs })
end

.process_message(msg) ⇒ Object

Handle a parsed JSON-RPC message hash.



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/ask/rails/mcp.rb', line 33

def process_message(msg)
  method_name = msg["method"]
  id = msg["id"]
  params = msg["params"] || {}
  has_id = msg.key?("id")

  case method_name
  when "initialize"
    handle_initialize(id, params)
  when "notifications/initialized"
    @initialized = true
    nil  # Notification — no response
  when "tools/list"
    @initialized ? handle_tools_list(id) : error_response(id, -32000, "Server not initialized")
  when "tools/call"
    @initialized ? handle_tool_call(id, params) : error_response(id, -32000, "Server not initialized")
  when "ping"
    has_id ? success_response(id, {}) : nil
  else
    has_id ? error_response(id, -32601, "Method not found: #{method_name}") : nil
  end
end

.reset!Object

Get or clear the cache (useful in tests).



114
115
116
117
118
# File 'lib/ask/rails/mcp.rb', line 114

def reset!
  @tools = nil
  @tool_server = nil
  @initialized = false
end

.startObject

Start the MCP stdio server, auto-loading the Rails app.

$ ask-rails-harness-mcp

The server will listen for JSON-RPC messages on stdin and write responses to stdout — the standard MCP stdio transport. Register this executable as an MCP server in your client configuration:

"mcp": {
"servers": {
  "ask-rails-harness-mcp": {
    "type": "stdio",
    "command": "ask-rails-harness-mcp",
    "args": []
  }
}
}


102
103
104
105
106
107
108
109
110
111
# File 'lib/ask/rails/mcp.rb', line 102

def start
  load_rails_app

  Ask::MCP::Server.start_stdio(
    name: "ask-rails-harness-mcp",
    tools: tools,
    capabilities: { tools: {} },
    debug: ENV["DEBUG"] == "1"
  )
end

.tool_serverObject

Tool server adapter that dispatches tools/call requests.



19
20
21
# File 'lib/ask/rails/mcp.rb', line 19

def tool_server
  @tool_server ||= Ask::MCP::Adapters::ToolServer.new(tools)
end

.toolsObject

All ask-rails-harness tools wrapped as MCP tool instances.



14
15
16
# File 'lib/ask/rails/mcp.rb', line 14

def tools
  @tools ||= Ask::Rails::Harness::CORE_RAILS_TOOLS.map(&:new)
end