rails-ai-context 5.18.0 → 5.19.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 +4 -4
- data/CHANGELOG.md +67 -0
- data/CONTEXT.md +17 -0
- data/CONTRIBUTING.md +2 -2
- data/README.md +19 -13
- data/SECURITY.md +1 -1
- data/app/controllers/rails_ai_context/mcp_controller.rb +52 -10
- data/docs/ARCHITECTURE.md +8 -8
- data/docs/CLI.md +2 -2
- data/docs/COMPATIBILITY.md +3 -2
- data/docs/CONFIGURATION.md +3 -3
- data/docs/CUSTOM_TOOLS.md +2 -2
- data/docs/FAQ.md +4 -4
- data/docs/GUIDE.md +19 -16
- data/docs/INTROSPECTORS.md +8 -7
- data/docs/SECURITY.md +2 -2
- data/docs/SETUP.md +2 -2
- data/docs/STANDALONE.md +2 -2
- data/docs/TOOLS.md +50 -2
- data/docs/TROUBLESHOOTING.md +1 -1
- data/docs/_config.yml +1 -1
- data/docs/index.md +3 -3
- data/exe/rails-ai-context +1 -1
- data/lib/rails_ai_context/configuration.rb +1 -1
- data/lib/rails_ai_context/introspector.rb +1 -0
- data/lib/rails_ai_context/introspectors/env_config_introspector.rb +127 -0
- data/lib/rails_ai_context/json_budget.rb +301 -0
- data/lib/rails_ai_context/resources.rb +2 -2
- data/lib/rails_ai_context/serializers/tool_guide_helper.rb +7 -1
- data/lib/rails_ai_context/server.rb +13 -0
- data/lib/rails_ai_context/tools/base_tool.rb +1 -1
- data/lib/rails_ai_context/tools/get_active_support.rb +107 -0
- data/lib/rails_ai_context/tools/get_autoload.rb +71 -0
- data/lib/rails_ai_context/tools/get_engines.rb +56 -0
- data/lib/rails_ai_context/tools/get_env_config.rb +91 -0
- data/lib/rails_ai_context/tools/get_i18n.rb +135 -0
- data/lib/rails_ai_context/tools/get_mailers.rb +65 -0
- data/lib/rails_ai_context/version.rb +1 -1
- data/lib/rails_ai_context/vfs.rb +4 -4
- data/server.json +4 -4
- metadata +12 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 46645f9291dee2458c1a03529fcf888bfd4d11be1d742310f224ae439baed82f
|
|
4
|
+
data.tar.gz: 6c1a79f00117f9617970e5c0f1380337fdc17578fe350515d1a8d301a0fef3fd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6ffd81a61373550ad0df085b3c01dc647de386bd79773b148711d8f040969f8a7f71fffb4e059539deef7285fc1fa21480761bd9f04cbaabd7aaaebab0e84e9d
|
|
7
|
+
data.tar.gz: c83fe3ae8dfa8bb663bba3b09dd6cd8b3b29939e2ae7ce6cf379cb9df0f739a2b21e86976a6741bee103240324f6e6118ba1fdf85b70d36927a702521e48a159
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,73 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [5.19.0] - 2026-08-09
|
|
9
|
+
|
|
10
|
+
### Added - 6 new tools surfacing previously unserved introspection (45 tools total)
|
|
11
|
+
|
|
12
|
+
An audit found five introspectors whose data never reached the tool
|
|
13
|
+
surface (three only served context files; `:autoload` and
|
|
14
|
+
`:active_support` were unreachable entirely), plus one nervous-system
|
|
15
|
+
gap nothing introspected. All six are now first-class tools, registered
|
|
16
|
+
automatically in both MCP and CLI:
|
|
17
|
+
|
|
18
|
+
- **`rails_get_i18n`** - default/available locales, backend, locale files with
|
|
19
|
+
key counts, per-locale coverage vs the default locale, and fallbacks
|
|
20
|
+
(data: `:i18n` introspector, previously serializer-only).
|
|
21
|
+
- **`rails_get_mailers`** - every ActionMailer class with its delivery actions
|
|
22
|
+
and delivery method (data: `:jobs` introspector's mailer extraction,
|
|
23
|
+
previously serializer-only). Filter with `mailer:"UserMailer"`.
|
|
24
|
+
- **`rails_get_engines`** - engines mounted in `config/routes.rb` with
|
|
25
|
+
known-engine descriptions, plus loaded engine classes with route/model
|
|
26
|
+
counts (data: `:engines` introspector, previously resource-only).
|
|
27
|
+
- **`rails_get_autoload`** - Zeitwerk vs Classic mode, autoloaders with
|
|
28
|
+
collapsed/ignored dirs, autoload/eager-load paths, and custom inflections
|
|
29
|
+
(data: `:autoload` introspector, previously unreachable).
|
|
30
|
+
- **`rails_get_active_support`** - concerns registry, deprecators,
|
|
31
|
+
MessageVerifier/MessageEncryptor usage, tagged logging, subscribed
|
|
32
|
+
`on_load` hooks, and cache store (data: `:active_support` introspector,
|
|
33
|
+
previously unreachable).
|
|
34
|
+
- **`rails_get_env_config`** - per-environment configuration from
|
|
35
|
+
`config/environments/*.rb`: notable toggles (`force_ssl`, `eager_load`,
|
|
36
|
+
caching, log level, queue adapter, mailer delivery) and every config key
|
|
37
|
+
each environment sets. Backed by the new **EnvConfigIntrospector**
|
|
38
|
+
(40 introspectors total, wired into `PRESETS[:full]`; file-based, so it
|
|
39
|
+
also works in the static tier). Config keys and values are read with
|
|
40
|
+
`ConfigAssignmentListener`, so a multi-line value, an assignment nested in
|
|
41
|
+
a conditional, and the `Rails.application.config.x = y` form all read
|
|
42
|
+
correctly. The key list pages with `offset`/`limit`.
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- **Engine-mounted MCP returns JSON-RPC errors instead of Rails 500s.**
|
|
47
|
+
`McpController#handle` had no rescue around `handle_request` - a
|
|
48
|
+
transport-level exception escaped into a generic Rails HTML 500, breaking
|
|
49
|
+
the client's JSON-RPC loop. It now answers 500 with a JSON-RPC `-32603`
|
|
50
|
+
body, mirroring `RailsAiContext::Middleware`.
|
|
51
|
+
- **Standalone HTTP transport survives transport exceptions.** The Rack
|
|
52
|
+
lambda behind `rails-ai-context serve --transport http` let a
|
|
53
|
+
`handle_request` exception propagate to rackup (dropped connection). It
|
|
54
|
+
now returns the same JSON-RPC `-32603` body.
|
|
55
|
+
- **MCP resources honor `max_tool_response_chars` without breaking the JSON
|
|
56
|
+
contract.** Static resource, model, and VFS routes payloads were emitted
|
|
57
|
+
unbounded (a huge schema or routes table rode a single JSON-RPC frame).
|
|
58
|
+
They now fit the cap by dropping whole elements from the data rather than
|
|
59
|
+
slicing the serialized string, so a capped payload still parses as the
|
|
60
|
+
`application/json` it is labeled. What was dropped is reported under a
|
|
61
|
+
`_truncated` key, which also counts any over-long string value that had to
|
|
62
|
+
be cut. New `RailsAiContext::JsonBudget` owns the reduction.
|
|
63
|
+
- **A committed SSE stream is no longer overwritten by the error handler.**
|
|
64
|
+
`McpController#handle`'s rescue set a status, headers, and a JSON body on
|
|
65
|
+
responses that were already on the wire - closing a stream commits it, so
|
|
66
|
+
every streaming failure reached the rescue committed. Assigning a body
|
|
67
|
+
there swapped the stream out from under the thread draining it, turning a
|
|
68
|
+
truncated SSE response into a garbled one. Committed failures now re-raise
|
|
69
|
+
to `ActionController::Live`, which logs them with a backtrace and closes
|
|
70
|
+
the connection; uncommitted failures still get the JSON-RPC `-32603` body.
|
|
71
|
+
- **`server.json` tool count** said 38 while the gem served 39; now tracks
|
|
72
|
+
the real count (45). Broken `RAILS_NERVOUS_SYSTEM.md` link in
|
|
73
|
+
`docs/INTROSPECTORS.md` replaced with a plain reference.
|
|
74
|
+
|
|
8
75
|
## [5.18.0] - 2026-08-09
|
|
9
76
|
|
|
10
77
|
### Added
|
data/CONTEXT.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Context
|
|
2
|
+
|
|
3
|
+
Terms this project uses in a narrower sense than everyday English. One entry per term that has caused a naming collision or an ambiguous read.
|
|
4
|
+
|
|
5
|
+
## Environment
|
|
6
|
+
|
|
7
|
+
Overloaded. Always qualify it; never use "environment" bare in a tool name, an introspector name, or a config key.
|
|
8
|
+
|
|
9
|
+
**Process environment** - the environment variables a running app sees, plus the places they are declared (`ENV[]` call sites, `.env.example`, Dockerfile, credentials keys). Served by `rails_get_env`, read by `EnvIntrospector`.
|
|
10
|
+
|
|
11
|
+
**Environment config** - what `config/environments/*.rb` declares per environment: the assigned `config.*` keys and the values of the notable toggles. Served by `rails_get_env_config`, read by `EnvConfigIntrospector`.
|
|
12
|
+
|
|
13
|
+
**Environment** (unqualified, as data) - a single named Rails environment: development, production, staging. This is the only sense in which the bare word is allowed, and only as a value, never as a name. The `:environments` payload key means "the list of these", which is why it kept its name when the introspector was renamed.
|
|
14
|
+
|
|
15
|
+
## Static tier
|
|
16
|
+
|
|
17
|
+
The mode where the app did not boot, or `--no-boot` was passed. An introspector answers in this tier only if it defines `static_call`. Two shapes count as defining it: reading a different source (parsing `db/schema.rb` instead of querying the connection), and reading the same source a booted app would (a file-based introspector, where `static_call` is the same work under another name).
|
data/CONTRIBUTING.md
CHANGED
|
@@ -19,8 +19,8 @@ The test suite uses [Combustion](https://github.com/pat/combustion) to boot a mi
|
|
|
19
19
|
```
|
|
20
20
|
lib/rails_ai_context/
|
|
21
21
|
├── cli/ # CLI tool runner (tool_runner.rb) - executes MCP tools from rake/Thor
|
|
22
|
-
├── introspectors/ #
|
|
23
|
-
├── tools/ #
|
|
22
|
+
├── introspectors/ # 40 introspectors (schema, models, routes, etc.)
|
|
23
|
+
├── tools/ # 45 MCP tools with detail levels and pagination
|
|
24
24
|
├── serializers/ # Per-assistant formatters + shared ToolGuideHelper
|
|
25
25
|
├── server.rb # MCP server setup (stdio + HTTP)
|
|
26
26
|
├── live_reload.rb # MCP live reload (file watcher + cache invalidation)
|
data/README.md
CHANGED
|
@@ -68,7 +68,7 @@ rails-ai-context serve # start MCP server
|
|
|
68
68
|
|
|
69
69
|
</div>
|
|
70
70
|
|
|
71
|
-
Now your AI doesn't guess - it **asks your app directly.**
|
|
71
|
+
Now your AI doesn't guess - it **asks your app directly.** 45 tools and 5 resource templates that query your schema, models, routes, controllers, views, and conventions on demand. Model introspection uses Prism AST parsing - every result carries a `[VERIFIED]` or `[INFERRED]` confidence tag so AI knows what's ground truth and what needs runtime checking.
|
|
72
72
|
|
|
73
73
|
<br>
|
|
74
74
|
|
|
@@ -163,7 +163,7 @@ Native Rails controller transport. No separate process needed.
|
|
|
163
163
|
|
|
164
164
|
### CLI
|
|
165
165
|
|
|
166
|
-
Same
|
|
166
|
+
Same 45 tools, no server needed. Works in any terminal, any AI tool.
|
|
167
167
|
|
|
168
168
|
```bash
|
|
169
169
|
rails 'ai:tool[search_code]' pattern="publishable?" match_type=trace
|
|
@@ -245,7 +245,7 @@ rails 'ai:tool[stimulus]' controller=chart
|
|
|
245
245
|
|
|
246
246
|
<br>
|
|
247
247
|
|
|
248
|
-
##
|
|
248
|
+
## 45 Tools
|
|
249
249
|
|
|
250
250
|
Every tool is **read-only** and returns data verified against your actual app - not guesses, not training data.
|
|
251
251
|
|
|
@@ -331,6 +331,12 @@ Every tool is **read-only** and returns data verified against your actual app -
|
|
|
331
331
|
| `get_service_pattern` | Interface, dependencies, side effects, callers |
|
|
332
332
|
| `get_job_pattern` | Queue, retries, guard clauses, broadcasts, schedules |
|
|
333
333
|
| `get_component_catalog` | ViewComponent/Phlex: props, slots, previews, sidecar assets |
|
|
334
|
+
| `get_i18n` | Locales, translation files with key counts, per-locale coverage, fallbacks |
|
|
335
|
+
| `get_mailers` | Mailer classes with delivery actions and delivery method |
|
|
336
|
+
| `get_engines` | Mounted engines (with known-engine descriptions) + loaded engine classes |
|
|
337
|
+
| `get_autoload` | Zeitwerk mode, autoload/eager-load paths, collapsed dirs, custom inflections |
|
|
338
|
+
| `get_active_support` | Concerns registry, deprecators, MessageVerifier usage, on_load hooks, cache store |
|
|
339
|
+
| `get_env_config` | Per-environment config: notable toggles + every config key each env sets |
|
|
334
340
|
|
|
335
341
|
</details>
|
|
336
342
|
|
|
@@ -351,7 +357,7 @@ Every tool is **read-only** and returns data verified against your actual app -
|
|
|
351
357
|
|
|
352
358
|
</details>
|
|
353
359
|
|
|
354
|
-
> **[All
|
|
360
|
+
> **[All 45 tools with parameters →](docs/TOOLS.md)** | **[Real-world recipes →](docs/RECIPES.md)**
|
|
355
361
|
|
|
356
362
|
<br>
|
|
357
363
|
|
|
@@ -397,12 +403,12 @@ Enabled by default. Disable with `config.anti_hallucination_rules = false` if yo
|
|
|
397
403
|
|
|
398
404
|
```mermaid
|
|
399
405
|
graph TD
|
|
400
|
-
A["Your Rails App\nmodels + schema + routes + controllers + views + jobs"] -->|"
|
|
406
|
+
A["Your Rails App\nmodels + schema + routes + controllers + views + jobs"] -->|"40 introspectors"| B
|
|
401
407
|
|
|
402
408
|
B["rails-ai-context\nPrism AST parsing · Cached · Confidence-tagged\nVFS: rails-ai-context:// URIs introspected fresh"]
|
|
403
409
|
|
|
404
|
-
B --> C["MCP Server\nstdio / HTTP\
|
|
405
|
-
B --> D["CLI Tools\nRake / Thor\nSame
|
|
410
|
+
B --> C["MCP Server\nstdio / HTTP\n45 tools · 5 templates"]
|
|
411
|
+
B --> D["CLI Tools\nRake / Thor\nSame 45 tools"]
|
|
406
412
|
B --> E["Static Files\nCLAUDE.md · .cursor/rules/ · .cursorrules\n.github/instructions/"]
|
|
407
413
|
|
|
408
414
|
style A fill:#4a9eff,stroke:#2d7ad4,color:#fff
|
|
@@ -440,7 +446,7 @@ Both paths ask which AI tools you use (Claude Code, Cursor, GitHub Copilot, Open
|
|
|
440
446
|
| In-Gemfile | Standalone | What it does |
|
|
441
447
|
|:-----------|:-----------|:------------|
|
|
442
448
|
| `rails ai:context` | `rails-ai-context context` | Generate context files |
|
|
443
|
-
| `rails 'ai:tool[NAME]'` | `rails-ai-context tool NAME` | Run any of the
|
|
449
|
+
| `rails 'ai:tool[NAME]'` | `rails-ai-context tool NAME` | Run any of the 45 tools |
|
|
444
450
|
| `rails ai:tool` | `rails-ai-context tool --list` | List all available tools |
|
|
445
451
|
| `rails ai:serve` | `rails-ai-context serve` | Start MCP server (stdio) |
|
|
446
452
|
| `rails ai:doctor` | `rails-ai-context doctor` | Diagnostics + AI readiness score |
|
|
@@ -491,13 +497,13 @@ empty listings.
|
|
|
491
497
|
| | |
|
|
492
498
|
|:------|:------------|
|
|
493
499
|
| **[Quickstart](docs/QUICKSTART.md)** | 5-minute getting started |
|
|
494
|
-
| **[Tools Reference](docs/TOOLS.md)** | All
|
|
500
|
+
| **[Tools Reference](docs/TOOLS.md)** | All 45 tools with every parameter |
|
|
495
501
|
| **[Recipes](docs/RECIPES.md)** | Real-world workflows and examples |
|
|
496
502
|
| **[Custom Tools](docs/CUSTOM_TOOLS.md)** | Build and test your own MCP tools |
|
|
497
503
|
| **[Configuration](docs/CONFIGURATION.md)** | 40+ config options with defaults |
|
|
498
504
|
| **[AI Tool Setup](docs/SETUP.md)** | Claude, Cursor, Copilot, OpenCode, Codex |
|
|
499
505
|
| **[Architecture](docs/ARCHITECTURE.md)** | System design and internals |
|
|
500
|
-
| **[Introspectors](docs/INTROSPECTORS.md)** | All
|
|
506
|
+
| **[Introspectors](docs/INTROSPECTORS.md)** | All 40 introspectors and AST engine |
|
|
501
507
|
| **[Security](docs/SECURITY.md)** | 4-layer SQL safety and file blocking |
|
|
502
508
|
| **[CLI Reference](docs/CLI.md)** | Commands and argument syntax |
|
|
503
509
|
| **[Standalone](docs/STANDALONE.md)** | Use without Gemfile entry |
|
|
@@ -509,7 +515,7 @@ empty listings.
|
|
|
509
515
|
|
|
510
516
|
## Build your own tools
|
|
511
517
|
|
|
512
|
-
Register custom MCP tools alongside the
|
|
518
|
+
Register custom MCP tools alongside the 45 built-in ones:
|
|
513
519
|
|
|
514
520
|
```ruby
|
|
515
521
|
# app/mcp_tools/rails_get_business_metrics.rb
|
|
@@ -547,7 +553,7 @@ if defined?(RailsAiContext)
|
|
|
547
553
|
RailsAiContext.configure do |config|
|
|
548
554
|
config.ai_tools = %i[claude cursor] # Which AI tools to generate for
|
|
549
555
|
config.tool_mode = :mcp # :mcp (default) or :cli
|
|
550
|
-
config.preset = :full # :full (
|
|
556
|
+
config.preset = :full # :full (40 introspectors) or :standard (17)
|
|
551
557
|
end
|
|
552
558
|
end
|
|
553
559
|
```
|
|
@@ -583,7 +589,7 @@ end
|
|
|
583
589
|
## About
|
|
584
590
|
|
|
585
591
|
Built by a Rails developer with 10+ years of production experience.<br>
|
|
586
|
-
|
|
592
|
+
2727 tests + 158-example e2e harness. 45 tools. 5 resource templates. 40 introspectors. Standalone or in-Gemfile.<br>
|
|
587
593
|
MIT licensed. [Contributions welcome.](CONTRIBUTING.md)
|
|
588
594
|
|
|
589
595
|
<br>
|
data/SECURITY.md
CHANGED
|
@@ -39,7 +39,7 @@ If you discover a security vulnerability in rails-ai-context, please report it r
|
|
|
39
39
|
|
|
40
40
|
## Security Design
|
|
41
41
|
|
|
42
|
-
- All
|
|
42
|
+
- All 45 MCP tools are **read-only** and never modify your application or database.
|
|
43
43
|
- **Sensitive file blocking** - configurable `sensitive_patterns` blocks access to `.env`, `*.key`, `*.pem`, `credentials.yml.enc` across all search and read tools. Patterns are checked in `rails_search_code`, `rails_get_edit_context`, and all new tools.
|
|
44
44
|
- **Path traversal protection** - all file-reading tools validate paths with `File.realpath()` against `Rails.root` to prevent directory escape.
|
|
45
45
|
- **Command injection prevention** - code search uses `Open3.capture2` with array arguments (never shell strings). The `--` flag separator prevents pattern injection.
|
|
@@ -29,15 +29,7 @@ module RailsAiContext
|
|
|
29
29
|
def handle
|
|
30
30
|
status_code, rack_headers, body = self.class.mcp_transport.handle_request(request)
|
|
31
31
|
self.status = status_code
|
|
32
|
-
rack_headers
|
|
33
|
-
# mcp >= 1.0 returns Rack 3-style lowercase header keys. Rails 7.0's
|
|
34
|
-
# response header hash is case-sensitive (Rack 2), so a lowercase
|
|
35
|
-
# "content-type" never registers and Rails falls back to text/html on
|
|
36
|
-
# an otherwise valid JSON body. Write it with the canonical case.
|
|
37
|
-
key = k.casecmp("content-type").zero? ? "Content-Type" : k
|
|
38
|
-
response.headers[key] = v
|
|
39
|
-
end
|
|
40
|
-
|
|
32
|
+
apply_transport_headers(rack_headers)
|
|
41
33
|
if body.respond_to?(:each)
|
|
42
34
|
# Plain enumerable body (initialize, errors, JSON mode): join to a
|
|
43
35
|
# string so Content-Length/ETag semantics stay conventional.
|
|
@@ -75,16 +67,66 @@ module RailsAiContext
|
|
|
75
67
|
else
|
|
76
68
|
self.response_body = body
|
|
77
69
|
end
|
|
70
|
+
rescue => e
|
|
71
|
+
# Once the response is committed the status and headers are already on
|
|
72
|
+
# the wire, so a JSON-RPC frame written here cannot reach the client.
|
|
73
|
+
# Worse, assigning a body swaps the stream out from under the thread
|
|
74
|
+
# still draining the old one, turning a truncated SSE stream into a
|
|
75
|
+
# garbled one. The streaming branch's ensure always closes the stream,
|
|
76
|
+
# and closing commits, so every streaming failure lands here committed.
|
|
77
|
+
# Hand those to Live, which logs them with a backtrace and tears the
|
|
78
|
+
# connection down.
|
|
79
|
+
raise if response.committed?
|
|
80
|
+
|
|
81
|
+
# Mirror Middleware#json_rpc_error_response: a transport failure must
|
|
82
|
+
# still answer in JSON-RPC shape. Without this the exception escapes
|
|
83
|
+
# into a generic Rails 500 (HTML), breaking the client's JSON-RPC loop.
|
|
84
|
+
RailsAiContext.log_warn "[rails-ai-context] MCP request failed: #{e.class}: #{e.message}"
|
|
85
|
+
self.status = 500
|
|
86
|
+
response.headers["Content-Type"] = "application/json"
|
|
87
|
+
self.response_body = {
|
|
88
|
+
jsonrpc: "2.0",
|
|
89
|
+
error: { code: -32603, message: "Internal error: #{e.message}" },
|
|
90
|
+
id: nil
|
|
91
|
+
}.to_json
|
|
78
92
|
end
|
|
79
93
|
|
|
80
94
|
private
|
|
81
95
|
|
|
96
|
+
# Rack 3 transports name their headers in lowercase, and the MCP SDK
|
|
97
|
+
# switched to that in 1.0. Rails 7.0 keeps response headers in a
|
|
98
|
+
# case-sensitive Hash, so a lowercase "content-type" is invisible to the
|
|
99
|
+
# canonical lookup Rails makes when it commits, and it labels the response
|
|
100
|
+
# with its own text/html default - a correct JSON-RPC body under a type no
|
|
101
|
+
# client will parse. Rails 7.1 moved to case-insensitive Rack::Headers and
|
|
102
|
+
# does not have the problem. Only the type needs the canonical spelling,
|
|
103
|
+
# because Rails is the only reader that looks a header up by name; the
|
|
104
|
+
# value is passed through so nothing gains a charset it did not have.
|
|
105
|
+
def apply_transport_headers(rack_headers)
|
|
106
|
+
rack_headers.each do |name, value|
|
|
107
|
+
key = name.to_s.casecmp?("content-type") ? "Content-Type" : name
|
|
108
|
+
response.headers[key] = value
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
82
112
|
def wait_for_stream_close
|
|
83
|
-
sleep 0.5 until
|
|
113
|
+
sleep 0.5 until stream_finished?
|
|
84
114
|
rescue IOError
|
|
85
115
|
nil
|
|
86
116
|
end
|
|
87
117
|
|
|
118
|
+
# A client hangup aborts the buffer instead of closing it, so `closed?`
|
|
119
|
+
# alone leaves this thread parked until the transport's next keepalive
|
|
120
|
+
# write notices the hangup - up to the keepalive interval per dropped
|
|
121
|
+
# client. Live's buffer reports the hangup through `connected?`; the plain
|
|
122
|
+
# buffer used off the streaming path does not define it, so ask first.
|
|
123
|
+
def stream_finished?
|
|
124
|
+
stream = response.stream
|
|
125
|
+
return true if stream.closed?
|
|
126
|
+
|
|
127
|
+
stream.respond_to?(:connected?) && !stream.connected?
|
|
128
|
+
end
|
|
129
|
+
|
|
88
130
|
class << self
|
|
89
131
|
# Class-level memoization - transport persists across requests.
|
|
90
132
|
# Thread-safe: MCP::Server and transport are stateless for reads.
|
data/docs/ARCHITECTURE.md
CHANGED
|
@@ -18,19 +18,19 @@ graph TD
|
|
|
18
18
|
A["models + schema + routes + controllers + views + jobs + config"]
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
A -->|"
|
|
21
|
+
A -->|"40 introspectors"| gem
|
|
22
22
|
|
|
23
23
|
subgraph gem["rails-ai-context"]
|
|
24
24
|
direction TB
|
|
25
25
|
|
|
26
26
|
subgraph engine["Introspection Engine"]
|
|
27
27
|
direction LR
|
|
28
|
-
I["Introspectors\
|
|
28
|
+
I["Introspectors\n40 modules\nPresets\nCached"]
|
|
29
29
|
AST["AST Engine\nPrism\n24 listeners\nConfidence tags"]
|
|
30
30
|
H["Hydration Layer\nSchema hints\ninjected into\ntool responses"]
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
engine --> R["Tool Registry\
|
|
33
|
+
engine --> R["Tool Registry\n45 tools auto-discovered via inherited\n+ custom_tools - skip_tools = active tools"]
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
R --> MCP
|
|
@@ -40,7 +40,7 @@ graph TD
|
|
|
40
40
|
subgraph outputs["Output"]
|
|
41
41
|
direction LR
|
|
42
42
|
MCP["MCP Server\nstdio / HTTP\nResources\nVFS URIs"]
|
|
43
|
-
CLI["CLI Runner\nRake / Thor\nSame
|
|
43
|
+
CLI["CLI Runner\nRake / Thor\nSame 45 tools\nNo server needed"]
|
|
44
44
|
S["Serializers\n14 modules\nStatic files\nPer-AI-tool"]
|
|
45
45
|
end
|
|
46
46
|
|
|
@@ -73,7 +73,7 @@ sequenceDiagram
|
|
|
73
73
|
alt cache hit (TTL + fingerprint valid)
|
|
74
74
|
Cache-->>TR: cached context
|
|
75
75
|
else cache miss
|
|
76
|
-
Cache->>App: introspect (
|
|
76
|
+
Cache->>App: introspect (40 modules)
|
|
77
77
|
App-->>Cache: structured data
|
|
78
78
|
Cache-->>TR: fresh context
|
|
79
79
|
end
|
|
@@ -101,7 +101,7 @@ flowchart LR
|
|
|
101
101
|
|
|
102
102
|
### Introspectors (`lib/rails_ai_context/introspectors/`)
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
40 modules that extract structured data from your Rails app. Each introspector:
|
|
105
105
|
|
|
106
106
|
- Returns a Hash (never raises - wraps errors in `{ error: msg }`)
|
|
107
107
|
- Is registered in `INTROSPECTOR_MAP` with a symbol key
|
|
@@ -192,7 +192,7 @@ Thor-based CLI that works standalone (no Gemfile entry):
|
|
|
192
192
|
|
|
193
193
|
- `ToolRunner` - Parses CLI args, resolves tool names, executes tools, formats output
|
|
194
194
|
- Supports `--json` mode for machine-readable output
|
|
195
|
-
- Same
|
|
195
|
+
- Same 45 tools available as MCP and CLI
|
|
196
196
|
|
|
197
197
|
### Caching
|
|
198
198
|
|
|
@@ -215,7 +215,7 @@ SHA256-based change detection:
|
|
|
215
215
|
## Key design decisions
|
|
216
216
|
|
|
217
217
|
1. **Official MCP SDK** - Not a custom protocol. Uses `mcp` gem's `MCP::Tool`, `MCP::Server`, transports.
|
|
218
|
-
2. **Read-only tools** - All
|
|
218
|
+
2. **Read-only tools** - All 45 tools annotated as non-destructive. Defense-in-depth for query tool.
|
|
219
219
|
3. **Graceful degradation** - Works without database (parses schema.rb as text), without Brakeman, without ripgrep, without listen gem.
|
|
220
220
|
4. **Zeitwerk autoloading** - Files loaded on-demand. No `require_relative` in the gem.
|
|
221
221
|
5. **Diff-aware generation** - Context file regeneration skips unchanged files using fingerprinting.
|
data/docs/CLI.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
| In-Gemfile (Rake) | `rails ai:` | `rails 'ai:tool[schema]' table=users` |
|
|
18
18
|
| Standalone (Thor) | `rails-ai-context` | `rails-ai-context tool schema --table users` |
|
|
19
19
|
|
|
20
|
-
Both provide the same
|
|
20
|
+
Both provide the same 45 tools and functionality.
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
@@ -40,7 +40,7 @@ rails-ai-context serve --transport http --port 6029 # HTTP transport
|
|
|
40
40
|
|
|
41
41
|
### `tool`
|
|
42
42
|
|
|
43
|
-
Run any of the
|
|
43
|
+
Run any of the 45 MCP tools from the terminal.
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
46
|
# Rake syntax
|
data/docs/COMPATIBILITY.md
CHANGED
|
@@ -80,6 +80,7 @@ that define a `static_call` path can answer without a booted app:
|
|
|
80
80
|
| `routes` | `config/routes.rb` parsed with a dedicated Prism listener |
|
|
81
81
|
| `models` | `app/models/**/*.rb` (plus packs/engines/extra paths) parsed, not constantized |
|
|
82
82
|
| `controllers` | `app/controllers/**/*.rb` (plus packs/engines/extra paths) parsed, not constantized |
|
|
83
|
+
| `env_config` | `config/environments/*.rb` read from disk - file-based, so the static tier serves the same data as a booted app |
|
|
83
84
|
|
|
84
85
|
The other 34 introspectors (views, jobs, gems, turbo, i18n, active_storage,
|
|
85
86
|
auth, api, and the rest) have no static path and report `{ unavailable: reason
|
|
@@ -167,11 +168,11 @@ Proof sources:
|
|
|
167
168
|
prints to stdout, writes via the `STDOUT` constant, hangs past the
|
|
168
169
|
timeout) plus `spec/e2e/static_tier_spec.rb` ("broken-boot app over the
|
|
169
170
|
CLI", "broken-boot app over MCP stdio", "syntax error in one model file").
|
|
170
|
-
8. No introspector outside the
|
|
171
|
+
8. No introspector outside the six in the operating-tiers table defines
|
|
171
172
|
`static_call` (`lib/rails_ai_context/introspectors/view_introspector.rb`
|
|
172
173
|
has none); `Introspector#run_introspector` reports `{ unavailable: reason
|
|
173
174
|
}` for every such section regardless of shape.
|
|
174
|
-
9. `spec/e2e/empty_app_spec.rb` - all
|
|
175
|
+
9. `spec/e2e/empty_app_spec.rb` - all 45 built-in tools swept against an app
|
|
175
176
|
with no scaffold, no models, no controllers beyond
|
|
176
177
|
`ApplicationController`, no routes beyond root.
|
|
177
178
|
10. `spec/e2e/massive_app_spec.rb` - `schema`, `model_details`, and `routes`
|
data/docs/CONFIGURATION.md
CHANGED
|
@@ -56,7 +56,7 @@ preset: full
|
|
|
56
56
|
|
|
57
57
|
| Option | Type | Default | Description |
|
|
58
58
|
|:-------|:-----|:--------|:------------|
|
|
59
|
-
| `preset` | Symbol | `:full` | `:full` (
|
|
59
|
+
| `preset` | Symbol | `:full` | `:full` (40 introspectors) or `:standard` (17 introspectors) |
|
|
60
60
|
| `context_mode` | Symbol | `:compact` | `:compact` (context files capped at ~150 lines) or `:full` (no line cap) |
|
|
61
61
|
| `introspectors` | Array of symbols | (from preset) | Override the introspector list directly |
|
|
62
62
|
| `generate_root_files` | Boolean | `true` | Set `false` to generate split rules only, no root CLAUDE.md/AGENTS.md |
|
|
@@ -69,7 +69,7 @@ preset: full
|
|
|
69
69
|
|:-------|:-----|:--------|:-----------|:------------|
|
|
70
70
|
| `server_name` | String | `"rails-ai-context"` | - | MCP server name |
|
|
71
71
|
| `cache_ttl` | Integer | `60` | Must be positive | Cache time-to-live in seconds |
|
|
72
|
-
| `max_tool_response_chars` | Integer | `200_000` | Must be positive | Safety cap for tool
|
|
72
|
+
| `max_tool_response_chars` | Integer | `200_000` | Must be positive | Safety cap for tool responses and MCP resource payloads. An over-cap resource keeps its JSON shape: whole elements are dropped and reported under a `_truncated` key |
|
|
73
73
|
| `live_reload` | Symbol/Boolean | `:auto` | - | `:auto` (uses `listen` gem if available), `true`, or `false` |
|
|
74
74
|
| `live_reload_debounce` | Float | `1.5` | - | Seconds to wait before processing file changes |
|
|
75
75
|
| `auto_mount` | Boolean | `false` | - | Auto-mount Rack middleware for HTTP transport |
|
|
@@ -166,7 +166,7 @@ preset: full
|
|
|
166
166
|
|
|
167
167
|
## Presets
|
|
168
168
|
|
|
169
|
-
### `:full` (default) -
|
|
169
|
+
### `:full` (default) - 40 introspectors
|
|
170
170
|
|
|
171
171
|
All available introspectors. Maximum context.
|
|
172
172
|
|
data/docs/CUSTOM_TOOLS.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Custom Tools
|
|
4
4
|
|
|
5
|
-
**Build your own MCP tools that run alongside the
|
|
5
|
+
**Build your own MCP tools that run alongside the 45 built-in ones.**
|
|
6
6
|
|
|
7
7
|
[Tools Reference](TOOLS.md) · [Configuration](CONFIGURATION.md) · [Architecture](ARCHITECTURE.md) · [FAQ](FAQ.md)
|
|
8
8
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
> [!NOTE]
|
|
14
|
-
> Custom tools have full access to your Rails environment - ActiveRecord, services, mailers, everything. They appear alongside the
|
|
14
|
+
> Custom tools have full access to your Rails environment - ActiveRecord, services, mailers, everything. They appear alongside the 45 built-in tools in both MCP and CLI.
|
|
15
15
|
|
|
16
16
|
## Creating a custom tool
|
|
17
17
|
|
data/docs/FAQ.md
CHANGED
|
@@ -25,7 +25,7 @@ Claude Code, Cursor, GitHub Copilot, OpenCode, and Codex CLI. Each gets tailored
|
|
|
25
25
|
No. The gem works three ways:
|
|
26
26
|
1. **MCP server** - AI calls tools via the protocol (best experience)
|
|
27
27
|
2. **Static files** - Generated context files (CLAUDE.md, .cursor/rules/, etc.)
|
|
28
|
-
3. **CLI** - Same
|
|
28
|
+
3. **CLI** - Same 45 tools from the terminal, no server needed
|
|
29
29
|
|
|
30
30
|
### Is this safe for production?
|
|
31
31
|
|
|
@@ -47,7 +47,7 @@ Yes. The gem gracefully degrades - it parses `db/schema.rb` as text when no data
|
|
|
47
47
|
|
|
48
48
|
### Can I switch between Gemfile and standalone?
|
|
49
49
|
|
|
50
|
-
Yes, freely. Both generate identical context files and provide the same
|
|
50
|
+
Yes, freely. Both generate identical context files and provide the same 45 tools. Just re-run the install/init to update MCP config files.
|
|
51
51
|
|
|
52
52
|
### Do I need to commit the generated files?
|
|
53
53
|
|
|
@@ -69,7 +69,7 @@ Start with `rails_onboard` for an app overview, `rails_analyze_feature` for feat
|
|
|
69
69
|
|
|
70
70
|
### Can I add my own tools?
|
|
71
71
|
|
|
72
|
-
Yes. See [Custom Tools](CUSTOM_TOOLS.md). Create an `MCP::Tool` subclass, register it via `config.custom_tools`, and it appears alongside the
|
|
72
|
+
Yes. See [Custom Tools](CUSTOM_TOOLS.md). Create an `MCP::Tool` subclass, register it via `config.custom_tools`, and it appears alongside the 45 built-in tools.
|
|
73
73
|
|
|
74
74
|
### Can I remove built-in tools?
|
|
75
75
|
|
|
@@ -99,7 +99,7 @@ PostgreSQL, MySQL, and SQLite. Each gets database-specific safety mechanisms (re
|
|
|
99
99
|
|
|
100
100
|
### What's the difference between `:full` and `:standard` preset?
|
|
101
101
|
|
|
102
|
-
- **`:full`** (default) -
|
|
102
|
+
- **`:full`** (default) - 40 introspectors. Full context for every aspect of your app.
|
|
103
103
|
- **`:standard`** - 17 introspectors. Faster, covers the essentials (schema, models, routes, controllers, tests, etc.).
|
|
104
104
|
|
|
105
105
|
### What's `:compact` vs `:full` context mode?
|
data/docs/GUIDE.md
CHANGED
|
@@ -18,13 +18,13 @@
|
|
|
18
18
|
| Guide | Description |
|
|
19
19
|
|:------|:------------|
|
|
20
20
|
| [Quickstart](QUICKSTART.md) | Get running in 5 minutes |
|
|
21
|
-
| [Tools Reference](TOOLS.md) | All
|
|
21
|
+
| [Tools Reference](TOOLS.md) | All 45 MCP tools with parameters |
|
|
22
22
|
| [Recipes](RECIPES.md) | Real-world workflows and examples |
|
|
23
23
|
| [Custom Tools](CUSTOM_TOOLS.md) | Build your own MCP tools |
|
|
24
24
|
| [Configuration](CONFIGURATION.md) | Every config option |
|
|
25
25
|
| [AI Tool Setup](SETUP.md) | Per-editor setup |
|
|
26
26
|
| [Architecture](ARCHITECTURE.md) | System design and internals |
|
|
27
|
-
| [Introspectors](INTROSPECTORS.md) | All
|
|
27
|
+
| [Introspectors](INTROSPECTORS.md) | All 40 introspectors |
|
|
28
28
|
| [Security](SECURITY.md) | Security model and SQL safety |
|
|
29
29
|
| [CLI Reference](CLI.md) | All commands and argument syntax |
|
|
30
30
|
| [Standalone Mode](STANDALONE.md) | Use without Gemfile |
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
- [Generated Files](#generated-files)
|
|
41
41
|
- [All Commands](#all-commands)
|
|
42
42
|
- [CLI Tools](#cli-tools)
|
|
43
|
-
- [MCP Tools -
|
|
43
|
+
- [MCP Tools - Common Reference](#mcp-tools--common-reference)
|
|
44
44
|
- [MCP Resources](#mcp-resources)
|
|
45
45
|
- [MCP Server Setup](#mcp-server-setup)
|
|
46
46
|
- [Configuration - All Options](#configuration--all-options)
|
|
@@ -283,7 +283,7 @@ rails ai:context:claude # Use this instead (no quoting needed)
|
|
|
283
283
|
|
|
284
284
|
## CLI Tools
|
|
285
285
|
|
|
286
|
-
All
|
|
286
|
+
All 45 MCP tools can be run directly from the terminal - no MCP server or AI client needed.
|
|
287
287
|
|
|
288
288
|
### Rake
|
|
289
289
|
|
|
@@ -347,9 +347,11 @@ The `tool_mode` is selected during `rails generate rails_ai_context:install`.
|
|
|
347
347
|
|
|
348
348
|
---
|
|
349
349
|
|
|
350
|
-
## MCP Tools -
|
|
350
|
+
## MCP Tools - Common Reference
|
|
351
351
|
|
|
352
|
-
|
|
352
|
+
The 24 tools below are the ones worth reading about before you start. [Tools Reference](TOOLS.md) carries all 45 with their parameters.
|
|
353
|
+
|
|
354
|
+
Every tool is **read-only** and **idempotent** - they never modify your application or database.
|
|
353
355
|
|
|
354
356
|
### rails_get_schema
|
|
355
357
|
|
|
@@ -1169,7 +1171,7 @@ if defined?(RailsAiContext)
|
|
|
1169
1171
|
end
|
|
1170
1172
|
```
|
|
1171
1173
|
|
|
1172
|
-
Both transports are **read-only** - they expose the same
|
|
1174
|
+
Both transports are **read-only** - they expose the same 45 tools and never modify your app.
|
|
1173
1175
|
|
|
1174
1176
|
### Controller Transport (Alternative)
|
|
1175
1177
|
|
|
@@ -1192,7 +1194,7 @@ if defined?(RailsAiContext)
|
|
|
1192
1194
|
RailsAiContext.configure do |config|
|
|
1193
1195
|
# --- Introspectors ---
|
|
1194
1196
|
|
|
1195
|
-
# Presets: :full (
|
|
1197
|
+
# Presets: :full (40 introspectors, default) or :standard (17)
|
|
1196
1198
|
config.preset = :full
|
|
1197
1199
|
|
|
1198
1200
|
# Cherry-pick on top of a preset
|
|
@@ -1311,10 +1313,10 @@ end
|
|
|
1311
1313
|
| Option | Type | Default | Description |
|
|
1312
1314
|
|--------|------|---------|-------------|
|
|
1313
1315
|
| `preset` | Symbol | `:full` | Introspector preset (`:full` or `:standard`) |
|
|
1314
|
-
| `introspectors` | Array |
|
|
1316
|
+
| `introspectors` | Array | 40 (full preset) | Which introspectors to run |
|
|
1315
1317
|
| `context_mode` | Symbol | `:compact` | `:compact` or `:full` |
|
|
1316
1318
|
| `claude_max_lines` | Integer | `150` | Max lines for CLAUDE.md in compact mode |
|
|
1317
|
-
| `max_tool_response_chars` | Integer | `200_000` | Safety cap for MCP tool responses |
|
|
1319
|
+
| `max_tool_response_chars` | Integer | `200_000` | Safety cap for MCP tool responses and resource payloads |
|
|
1318
1320
|
| `cache_ttl` | Integer | `60` | Cache TTL in seconds for introspection results |
|
|
1319
1321
|
| `custom_tools` | Array | `[]` | Additional MCP tool classes to register alongside built-in tools |
|
|
1320
1322
|
| `skip_tools` | Array | `[]` | Built-in tool names to exclude (e.g. `%w[rails_security_scan]`) |
|
|
@@ -1398,7 +1400,7 @@ Core Rails structure only. Use `config.preset = :standard` for a lighter footpri
|
|
|
1398
1400
|
| `performance` | N+1 query risks, missing counter_cache, missing FK indexes, Model.all anti-patterns, eager load candidates. |
|
|
1399
1401
|
| `i18n` | Default locale, available locales, locale files with key counts, backend class, parse errors. |
|
|
1400
1402
|
|
|
1401
|
-
### Full preset (
|
|
1403
|
+
### Full preset (40 introspectors) - default
|
|
1402
1404
|
|
|
1403
1405
|
Includes all standard introspectors plus:
|
|
1404
1406
|
|
|
@@ -1415,6 +1417,7 @@ Includes all standard introspectors plus:
|
|
|
1415
1417
|
| `seeds` | db/seeds.rb analysis (Faker usage, environment conditionals), seed files in db/seeds/, models seeded. |
|
|
1416
1418
|
| `middleware` | Custom Rack middleware in app/middleware/ with detected patterns (auth, rate limiting, tenant isolation, logging). Full middleware stack. |
|
|
1417
1419
|
| `engines` | Mounted Rails engines from routes.rb with paths and descriptions for 23+ known engines (Sidekiq::Web, Flipper::UI, PgHero, ActiveAdmin, etc.). |
|
|
1420
|
+
| `env_config` | Per-environment config files (`config/environments/*.rb`): notable toggles (`force_ssl`, `eager_load`, caching, log level, queue adapter, mailer delivery) with URI credentials redacted, assigned config keys. |
|
|
1418
1421
|
| `multi_database` | Multiple databases, replicas, sharding config, model-specific `connects_to` declarations. database.yml parsing fallback. |
|
|
1419
1422
|
| `frontend_frameworks` | Frontend JS framework detection (React/Vue/Svelte/Angular), mounting strategy (Inertia/react-rails), TypeScript config, state management, package manager. |
|
|
1420
1423
|
| `database_stats` | PostgreSQL approximate row counts via `pg_stat_user_tables`. Gracefully skips on non-PostgreSQL adapters. |
|
|
@@ -1534,11 +1537,11 @@ OpenCode uses **per-directory lazy-loading**: when the agent reads a file, it wa
|
|
|
1534
1537
|
|
|
1535
1538
|
| Setup | Coverage | Notes |
|
|
1536
1539
|
|-------|----------|-------|
|
|
1537
|
-
| Rails full-stack (ERB + Hotwire) |
|
|
1538
|
-
| Rails + Inertia.js (React/Vue) | ~
|
|
1539
|
-
| Rails API + React/Next.js SPA | ~
|
|
1540
|
-
| Rails API + mobile app | ~
|
|
1541
|
-
| Rails engine (mountable gem) | ~
|
|
1540
|
+
| Rails full-stack (ERB + Hotwire) | 40/40 | All introspectors relevant |
|
|
1541
|
+
| Rails + Inertia.js (React/Vue) | ~34/40 | Views/Turbo partially useful, backend fully covered |
|
|
1542
|
+
| Rails API + React/Next.js SPA | ~32/40 | Schema, models, routes, API, auth, jobs - all covered |
|
|
1543
|
+
| Rails API + mobile app | ~32/40 | Same as SPA - backend introspection is identical |
|
|
1544
|
+
| Rails engine (mountable gem) | ~27/40 | Core introspectors (schema, models, routes, gems) work |
|
|
1542
1545
|
|
|
1543
1546
|
Frontend introspectors (views, Turbo, Stimulus, assets) degrade gracefully - they report nothing when those features aren't present.
|
|
1544
1547
|
|