ask-rails-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0735b5cd8de91ba218011330ce945aaf843cb2b84d1a98e382b54cbe3f7ef51f
4
+ data.tar.gz: 6cec7455ddc917a6b3482fe5cb9e72c2ffefd168f3f1310b93fbf6570ad0ece3
5
+ SHA512:
6
+ metadata.gz: 972281e304211d8939710ef5c6167313fcd0945b7d30204642d5be58f6c8f7c4f41fc3ea1159d4c04cb013059ad7b803fe455d086175e5383327e90709a1f970
7
+ data.tar.gz: 61f31bccee0eb59227266ea2e23637fe4b5f616db9d4a6d40d6b2df56c3251a951e8c31294503ba5b3209e8c59635a78c897852b125c93046371ef1a831ab24e
data/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ ## [0.1.0] — 2026-07-23
2
+
3
+ ### Added
4
+
5
+ - **MCP endpoint** — Mount an MCP server in your Rails app that exposes all ask-rails tools (SchemaGraph, QueryDatabase, ReadModel, RouteInspector, ReadLog, SearchCodebase, ReadFile, RunCommand, ReadRoutes) over the Model Context Protocol.
6
+ - **JSON-RPC handler** — `Ask::Rails::MCP.handle` processes MCP messages (`initialize`, `tools/list`, `tools/call`, `ping`) and returns proper JSON-RPC responses.
7
+ - **Tool definitions** — All 9 tools are automatically registered with their names, descriptions, and JSON Schema input definitions.
8
+ - **Auth** — The MCP endpoint respects `Ask::Rails::Auth.check` for authentication.
9
+
10
+ ### Usage
11
+
12
+ Mount in `config/routes.rb`:
13
+
14
+ ```ruby
15
+ Rails.application.routes.draw do
16
+ # Protect behind your auth
17
+ authenticate :user, ->(u) { u.admin? } do
18
+ post "ask/mcp", to: "ask/rails/mcp#handle"
19
+ end
20
+ end
21
+ ```
data/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # ask-rails-mcp
2
+
3
+ MCP server for Rails app introspection. Exposes all [ask-rails](https://github.com/ask-rb/ask-rails) tools over the [Model Context Protocol](https://modelcontextprotocol.io/).
4
+
5
+ Coding agents like Claude Code, Cursor, or any MCP-compatible client can connect to inspect your Rails schema, query your database, read models, and more — all through the same tools ask-rails uses internally.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ bundle add ask-rails-mcp
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ Mount in `config/routes.rb` behind your existing auth:
16
+
17
+ ```ruby
18
+ Rails.application.routes.draw do
19
+ authenticate :user, ->(u) { u.admin? } do
20
+ post "ask/mcp", to: "ask/rails/mcp#handle"
21
+ end
22
+ end
23
+ ```
24
+
25
+ Then configure any MCP-compatible agent to connect to your app. For example, in Claude Code's `~/.claude/settings.json`:
26
+
27
+ ```json
28
+ {
29
+ "mcp": {
30
+ "servers": {
31
+ "ask-rails-mcp": {
32
+ "type": "url",
33
+ "url": "https://myapp.com/ask/mcp"
34
+ }
35
+ }
36
+ }
37
+ }
38
+ ```
39
+
40
+ The agent discovers 9 tools automatically:
41
+
42
+ | Tool | What it does |
43
+ |---|---|
44
+ | `schema_graph` | Full schema introspection — all models, tables, columns, associations, validations |
45
+ | `query_database` | Read-only SQL queries with safety guards |
46
+ | `read_model` | Introspect a single ActiveRecord model |
47
+ | `route_inspector` | Parsed route table with filters |
48
+ | `read_log` | Read Rails log files with level/search filtering |
49
+ | `search_codebase` | Full-text grep search |
50
+ | `read_file` | Read any file from `Rails.root` |
51
+ | `run_command` | Run shell commands in the app root |
52
+ | `read_routes` | Read the raw `config/routes.rb` |
53
+
54
+ ### Authentication
55
+
56
+ The MCP endpoint uses the same `Ask::Rails::Auth` system as the chat UI:
57
+
58
+ ```ruby
59
+ Ask::Rails::Auth.check = -> {
60
+ redirect_to main_app.login_path unless current_user&.admin?
61
+ }
62
+ ```
63
+
64
+ ### Safety
65
+
66
+ All ask-rails safety features apply automatically:
67
+ - **Permissions** — access modes (`:read_only`, `:ask_before_changes`, `:full_access`)
68
+ - **Command allowlists** — `allowed_commands` / `denied_commands` for `RunCommand`
69
+ - **Write guards** — `INSERT`/`UPDATE`/`DELETE` blocked by `QueryDatabase`
70
+ - **Audit log** — every tool call recorded in `ask_audit_logs`
71
+
72
+ ## Development
73
+
74
+ ```bash
75
+ bundle exec rake test
76
+ ```
77
+
78
+ ## License
79
+
80
+ MIT
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ask
4
+ module Rails
5
+ module MCP
6
+ VERSION = "0.1.0"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ask/rails"
4
+ require "ask/mcp"
5
+ require_relative "mcp/version"
6
+
7
+ module Ask
8
+ module Rails
9
+ module MCP
10
+ PROTOCOL_VERSION = "2025-06-18"
11
+
12
+ class << self
13
+ # All ask-rails tools wrapped as MCP tool instances.
14
+ def tools
15
+ @tools ||= Ask::Rails::CORE_RAILS_TOOLS.map(&:new)
16
+ end
17
+
18
+ # Tool server adapter that dispatches tools/call requests.
19
+ def tool_server
20
+ @tool_server ||= Ask::MCP::Adapters::ToolServer.new(tools)
21
+ end
22
+
23
+ # Handle a raw JSON-RPC message string.
24
+ # Returns a Hash suitable for JSON serialization.
25
+ def handle(json_string)
26
+ msg = JSON.parse(json_string)
27
+ process_message(msg)
28
+ rescue JSON::ParserError => e
29
+ error_response(nil, -32700, "Parse error: #{e.message}")
30
+ end
31
+
32
+ # Handle a parsed JSON-RPC message hash.
33
+ def process_message(msg)
34
+ method_name = msg["method"]
35
+ id = msg["id"]
36
+ params = msg["params"] || {}
37
+ has_id = msg.key?("id")
38
+
39
+ case method_name
40
+ when "initialize"
41
+ handle_initialize(id, params)
42
+ when "notifications/initialized"
43
+ @initialized = true
44
+ nil # Notification — no response
45
+ when "tools/list"
46
+ @initialized ? handle_tools_list(id) : error_response(id, -32000, "Server not initialized")
47
+ when "tools/call"
48
+ @initialized ? handle_tool_call(id, params) : error_response(id, -32000, "Server not initialized")
49
+ when "ping"
50
+ has_id ? success_response(id, {}) : nil
51
+ else
52
+ has_id ? error_response(id, -32601, "Method not found: #{method_name}") : nil
53
+ end
54
+ end
55
+
56
+ # Process an initialize request.
57
+ def handle_initialize(id, params)
58
+ @initialized = true
59
+ client_version = params["protocolVersion"] || PROTOCOL_VERSION
60
+ success_response(id, {
61
+ "protocolVersion" => client_version,
62
+ "capabilities" => { "tools" => {} },
63
+ "serverInfo" => {
64
+ "name" => "ask-rails-mcp",
65
+ "version" => Ask::Rails::MCP::VERSION
66
+ }
67
+ })
68
+ end
69
+
70
+ # Process a tools/list request.
71
+ def handle_tools_list(id)
72
+ defs = tool_server.definitions
73
+ success_response(id, { tools: defs })
74
+ end
75
+
76
+ # Process a tools/call request.
77
+ def handle_tool_call(id, params)
78
+ tool_name = params["name"]
79
+ arguments = params["arguments"] || {}
80
+
81
+ result = tool_server.call(tool_name, arguments)
82
+ success_response(id, result)
83
+ end
84
+
85
+ # Get or clear the cache (useful in tests).
86
+ def reset!
87
+ @tools = nil
88
+ @tool_server = nil
89
+ @initialized = false
90
+ end
91
+
92
+ private
93
+
94
+ def success_response(id, result)
95
+ { "jsonrpc" => "2.0", "id" => id, "result" => deep_stringify_keys(result) }
96
+ end
97
+
98
+ def error_response(id, code, message)
99
+ { "jsonrpc" => "2.0", "id" => id, "error" => { "code" => code, "message" => message } }
100
+ end
101
+
102
+ def deep_stringify_keys(obj)
103
+ case obj
104
+ when Hash then obj.each_with_object({}) { |(k, v), h| h[k.to_s] = deep_stringify_keys(v) }
105
+ when Array then obj.map { |v| deep_stringify_keys(v) }
106
+ else obj
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ask/rails/mcp"
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ask-rails-mcp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kaka Ruto
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ask-rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0.10'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0.10'
26
+ - !ruby/object:Gem::Dependency
27
+ name: ask-mcp
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0.1'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0.1'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rails
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '7.1'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '7.1'
54
+ - !ruby/object:Gem::Dependency
55
+ name: minitest
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '5.25'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '5.25'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rake
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '13.0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '13.0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: mocha
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '2.0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '2.0'
96
+ description: |
97
+ Mount an MCP endpoint in your Rails app that exposes SchemaGraph, QueryDatabase,
98
+ ReadModel, RouteInspector, and all other ask-rails tools as MCP tools.
99
+ Coding agents like Claude Code, Cursor, or any MCP-compatible client can
100
+ connect to inspect your schema, query your database, read models, and more.
101
+ email:
102
+ - kaka@myrrlabs.com
103
+ executables: []
104
+ extensions: []
105
+ extra_rdoc_files: []
106
+ files:
107
+ - CHANGELOG.md
108
+ - README.md
109
+ - lib/ask-rails-mcp.rb
110
+ - lib/ask/rails/mcp.rb
111
+ - lib/ask/rails/mcp/version.rb
112
+ homepage: https://github.com/ask-rb/ask-rails-mcp
113
+ licenses:
114
+ - MIT
115
+ metadata:
116
+ homepage_uri: https://github.com/ask-rb/ask-rails-mcp
117
+ source_code_uri: https://github.com/ask-rb/ask-rails-mcp
118
+ changelog_uri: https://github.com/ask-rb/ask-rails-mcp/blob/master/CHANGELOG.md
119
+ rdoc_options: []
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '3.2'
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubygems_version: 4.0.3
134
+ specification_version: 4
135
+ summary: MCP server for Rails app introspection — exposes ask-rails tools over the
136
+ Model Context Protocol
137
+ test_files: []