ask-rails-harness-mcp 0.2.0 → 0.2.1
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 +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +35 -53
- data/bin/ask-rails-harness-mcp +24 -1
- data/lib/ask/rails/mcp/version.rb +1 -1
- data/lib/ask/rails/mcp.rb +34 -3
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 557e26401f98e088d56827ac4c52341bffd6cff9226ccd1495bfb925da0b6068
|
|
4
|
+
data.tar.gz: c6d51e57bdc815908e58c973efe84ef123fa9e11b4f04b9517f8c294206d0a86
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0fc972965204065aea34a061fd29678b6addb1f079bc04ed503e9c307fc0e30b12f5f421811da77854f36a928ce169797824654816ba650d9643897d30cd92ed
|
|
7
|
+
data.tar.gz: 79231eb99d2e958bf1517633b3e2e20e047bf54b604cc1b52d1c49ee46cc8381b595cbf2c425d60264cad06d88eecfefb4fcc702b6b60715f9f203797aa228a5
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [0.2.1] — 2026-08-08
|
|
2
|
+
|
|
3
|
+
### Fixed
|
|
4
|
+
|
|
5
|
+
- **Boot under the app's Bundler context** — the `ask-rails-harness-mcp` binary now requires `bundler/setup` before loading the gem, so gem versions resolve against the host app's `Gemfile.lock` instead of whatever is newest on the system. Without this, loading the harness could activate conflicting gem versions (e.g. two `securerandom` releases) and abort the app boot with a misleading "config/environment.rb not found" error. The gem must be present in the app's Gemfile (`bundle add ask-rails-harness-mcp`); a clear hint is printed if it isn't.
|
|
6
|
+
|
|
1
7
|
## [0.2.0] — 2026-07-23
|
|
2
8
|
|
|
3
9
|
### Added
|
data/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# ask-rails-harness-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://badge.fury.io/rb/ask-rails-harness-mcp)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
MCP server for Rails app introspection. Exposes all [ask-rails-harness](https://github.com/ask-rb/ask-rails-harness) tools over the [Model Context Protocol](https://modelcontextprotocol.io/), so coding agents like Claude Code and Cursor can inspect your Rails schema, query your database, read models, and more.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -10,18 +10,16 @@ Coding agents like Claude Code, Cursor, or any MCP-compatible client can connect
|
|
|
10
10
|
bundle add ask-rails-harness-mcp
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Quick Start
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
Run from your Rails app root:
|
|
15
|
+
Add the gem to your Rails app, then run the server from the app root via Bundler — this pins gem versions to the app's `Gemfile.lock`, which is required (loading the harness against the newest installed gems can activate conflicting versions and abort the boot):
|
|
18
16
|
|
|
19
17
|
```bash
|
|
20
|
-
|
|
21
|
-
ask-rails-harness-mcp
|
|
18
|
+
bundle add ask-rails-harness-mcp
|
|
19
|
+
bundle exec ask-rails-harness-mcp
|
|
22
20
|
```
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
Configure it in your agent's MCP config, pointing `cwd` at your Rails app root (add `timeoutMs` if the app is slow to boot):
|
|
25
23
|
|
|
26
24
|
```json
|
|
27
25
|
{
|
|
@@ -29,72 +27,56 @@ This boots your Rails app and starts an MCP stdio server. Configure in your agen
|
|
|
29
27
|
"servers": {
|
|
30
28
|
"ask-rails-harness-mcp": {
|
|
31
29
|
"type": "stdio",
|
|
32
|
-
"command": "
|
|
33
|
-
"args": []
|
|
30
|
+
"command": "bundle",
|
|
31
|
+
"args": ["exec", "ask-rails-harness-mcp"],
|
|
32
|
+
"cwd": "/path/to/my-rails-app",
|
|
33
|
+
"timeoutMs": 60000
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
Mount in `config/routes.rb` behind your existing auth:
|
|
43
|
-
|
|
44
|
-
```ruby
|
|
45
|
-
Rails.application.routes.draw do
|
|
46
|
-
authenticate :user, ->(u) { u.admin? } do
|
|
47
|
-
post "ask/mcp", to: "ask/rails/mcp#handle"
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
```
|
|
40
|
+
stdio is the only shipped transport.
|
|
51
41
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
```json
|
|
55
|
-
{
|
|
56
|
-
"mcp": {
|
|
57
|
-
"servers": {
|
|
58
|
-
"ask-rails-harness-mcp": {
|
|
59
|
-
"type": "http",
|
|
60
|
-
"url": "https://myapp.com/ask/mcp"
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
```
|
|
42
|
+
## Tools
|
|
66
43
|
|
|
67
|
-
The agent discovers
|
|
44
|
+
The agent discovers 7 tools:
|
|
68
45
|
|
|
69
46
|
| Tool | What it does |
|
|
70
47
|
|---|---|
|
|
71
|
-
| `schema_graph` | Full schema introspection
|
|
48
|
+
| `schema_graph` | Full schema introspection: models, tables, columns, associations |
|
|
72
49
|
| `query_database` | Read-only SQL queries with safety guards |
|
|
73
50
|
| `read_model` | Introspect a single ActiveRecord model |
|
|
74
51
|
| `route_inspector` | Parsed route table with filters |
|
|
75
|
-
| `read_log` | Read
|
|
76
|
-
| `search_codebase` | Full-text grep search |
|
|
77
|
-
| `read_file` | Read any file from `Rails.root` |
|
|
52
|
+
| `read_log` | Read log files with level/search filtering |
|
|
78
53
|
| `run_command` | Run shell commands in the app root |
|
|
79
|
-
| `
|
|
54
|
+
| `run_tests` | Structured test results with failure reruns (minitest/rspec) |
|
|
80
55
|
|
|
81
|
-
|
|
56
|
+
The six generic tools come from `ask-ruby-harness` (usable in any Ruby
|
|
57
|
+
project — see the [Ruby Harness MCP guide](https://ask-rb.github.io/ask-docs/ruby/mcp));
|
|
58
|
+
`route_inspector` is Rails-native.
|
|
82
59
|
|
|
83
|
-
|
|
60
|
+
## Essential API
|
|
61
|
+
|
|
62
|
+
`Ask::Rails::MCP` exposes four entry points:
|
|
63
|
+
|
|
64
|
+
- `start`: start the stdio server (what the `ask-rails-harness-mcp` binary runs)
|
|
65
|
+
- `handle(json_string)`: handle a raw JSON-RPC message string
|
|
66
|
+
- `process_message(msg)`: handle a parsed JSON-RPC message hash
|
|
67
|
+
- `tools`: the harness tools as MCP tool instances
|
|
68
|
+
|
|
69
|
+
To serve MCP from your own endpoint, parse the request body and return the response hash:
|
|
84
70
|
|
|
85
71
|
```ruby
|
|
86
|
-
Ask::Rails::
|
|
87
|
-
redirect_to main_app.login_path unless current_user&.admin?
|
|
88
|
-
}
|
|
72
|
+
render json: Ask::Rails::MCP.process_message(JSON.parse(request.body.read))
|
|
89
73
|
```
|
|
90
74
|
|
|
91
|
-
|
|
75
|
+
All ask-rails-harness safety features apply: environment permission modes, `allowed_commands`/`denied_commands`, read-only query guards, and audit logging of every tool call.
|
|
76
|
+
|
|
77
|
+
## Full documentation
|
|
92
78
|
|
|
93
|
-
|
|
94
|
-
- **Permissions** — access modes (`:read_only`, `:ask_before_changes`, `:full_access`)
|
|
95
|
-
- **Command allowlists** — `allowed_commands` / `denied_commands` for `RunCommand`
|
|
96
|
-
- **Write guards** — `INSERT`/`UPDATE`/`DELETE` blocked by `QueryDatabase`
|
|
97
|
-
- **Audit log** — every tool call recorded in `ask_audit_logs`
|
|
79
|
+
The full ask-rb documentation lives at https://ask-rb.github.io/ask-docs. [Rails MCP](https://ask-rb.github.io/ask-docs/rails/mcp) covers ask-rails-harness-mcp in depth. API reference: https://ask-rb.github.io/ask-docs/reference/api.
|
|
98
80
|
|
|
99
81
|
## Development
|
|
100
82
|
|
data/bin/ask-rails-harness-mcp
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
# Boot the host Rails app's Bundler environment BEFORE loading the gem, so
|
|
5
|
+
# gem versions (rails, securerandom, ...) resolve against the app's
|
|
6
|
+
# Gemfile.lock instead of whatever is newest on the system. Loading the
|
|
7
|
+
# harness against the newest gems can activate conflicting versions and
|
|
8
|
+
# break the app boot with a misleading "config/environment.rb not found"
|
|
9
|
+
# error. Requires the gem to be present in the app's Gemfile.
|
|
10
|
+
begin
|
|
11
|
+
require "bundler/setup"
|
|
12
|
+
rescue StandardError => e
|
|
13
|
+
# The rubygems bin stub activates the gem's newest dependencies before
|
|
14
|
+
# this script runs, which can conflict with the app's pinned versions.
|
|
15
|
+
# Prefer running via `bundle exec`; fall back to plain resolution and
|
|
16
|
+
# let Ask::Rails::MCP.start report the missing app root.
|
|
17
|
+
warn "ask-rails-harness-mcp: bundler/setup failed (#{e.class}: #{e.message})"
|
|
18
|
+
warn "If you're inside a Rails app, run: bundle exec ask-rails-harness-mcp"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
begin
|
|
22
|
+
require "ask-rails-harness-mcp"
|
|
23
|
+
rescue LoadError => e
|
|
24
|
+
warn "ask-rails-harness-mcp: cannot load the gem (#{e.message})"
|
|
25
|
+
warn "If you're inside a Rails app, add it to your Gemfile first: bundle add ask-rails-harness-mcp"
|
|
26
|
+
exit 1
|
|
27
|
+
end
|
|
5
28
|
|
|
6
29
|
Ask::Rails::MCP.start
|
data/lib/ask/rails/mcp.rb
CHANGED
|
@@ -100,7 +100,8 @@ module Ask
|
|
|
100
100
|
# }
|
|
101
101
|
# }
|
|
102
102
|
def start
|
|
103
|
-
|
|
103
|
+
load_rails_app_quietly
|
|
104
|
+
silence_stdout_loggers
|
|
104
105
|
|
|
105
106
|
Ask::MCP::Server.start_stdio(
|
|
106
107
|
name: "ask-rails-harness-mcp",
|
|
@@ -119,11 +120,41 @@ module Ask
|
|
|
119
120
|
|
|
120
121
|
private
|
|
121
122
|
|
|
123
|
+
# Boot the app with stdout pointed at stderr: Rails/OTel loggers
|
|
124
|
+
# default to STDOUT in development, and boot-time output would
|
|
125
|
+
# corrupt the JSON-RPC stream the server writes to stdout.
|
|
126
|
+
def load_rails_app_quietly
|
|
127
|
+
real_stdout = STDOUT.dup
|
|
128
|
+
STDOUT.reopen(STDERR)
|
|
129
|
+
begin
|
|
130
|
+
load_rails_app
|
|
131
|
+
ensure
|
|
132
|
+
STDOUT.reopen(real_stdout)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Point app loggers at stderr so tool calls (AR queries, shell
|
|
137
|
+
# commands, ...) can't interleave with JSON-RPC responses.
|
|
138
|
+
def silence_stdout_loggers
|
|
139
|
+
require "logger"
|
|
140
|
+
$stderr.sync = true
|
|
141
|
+
stderr_logger = ::Logger.new($stderr)
|
|
142
|
+
stderr_logger.level = ::Logger::INFO
|
|
143
|
+
if defined?(OpenTelemetry) && OpenTelemetry.respond_to?(:logger=)
|
|
144
|
+
OpenTelemetry.logger = stderr_logger
|
|
145
|
+
end
|
|
146
|
+
return unless defined?(Rails)
|
|
147
|
+
|
|
148
|
+
Rails.logger = stderr_logger if Rails.respond_to?(:logger=)
|
|
149
|
+
ActiveRecord::Base.logger = stderr_logger if defined?(ActiveRecord::Base)
|
|
150
|
+
end
|
|
151
|
+
|
|
122
152
|
def load_rails_app
|
|
123
153
|
return if defined?(::Rails) && ::Rails.application
|
|
124
154
|
require File.expand_path("config/environment")
|
|
125
|
-
rescue LoadError
|
|
126
|
-
warn "ask-rails-harness-mcp:
|
|
155
|
+
rescue LoadError => e
|
|
156
|
+
warn "ask-rails-harness-mcp: failed to boot the Rails app: #{e.message}"
|
|
157
|
+
warn "Run it from your Rails app root with the gem in the Gemfile (bundle add ask-rails-harness-mcp)."
|
|
127
158
|
exit 1
|
|
128
159
|
end
|
|
129
160
|
|