rails-ai-context 5.4.0 → 5.6.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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/CONTRIBUTING.md +1 -1
  4. data/README.md +5 -4
  5. data/SECURITY.md +4 -0
  6. data/app/controllers/rails_ai_context/mcp_controller.rb +1 -1
  7. data/docs/GUIDE.md +59 -33
  8. data/exe/rails-ai-context +44 -29
  9. data/lib/generators/rails_ai_context/install/install_generator.rb +42 -37
  10. data/lib/rails_ai_context/cli/tool_runner.rb +1 -1
  11. data/lib/rails_ai_context/configuration.rb +1 -1
  12. data/lib/rails_ai_context/doctor.rb +142 -21
  13. data/lib/rails_ai_context/mcp_config_generator.rb +293 -0
  14. data/lib/rails_ai_context/serializers/claude_rules_serializer.rb +17 -0
  15. data/lib/rails_ai_context/serializers/context_file_serializer.rb +19 -4
  16. data/lib/rails_ai_context/serializers/copilot_instructions_serializer.rb +8 -0
  17. data/lib/rails_ai_context/serializers/cursor_rules_serializer.rb +3 -3
  18. data/lib/rails_ai_context/serializers/markdown_serializer.rb +1 -1
  19. data/lib/rails_ai_context/serializers/tool_guide_helper.rb +3 -3
  20. data/lib/rails_ai_context/server.rb +23 -42
  21. data/lib/rails_ai_context/tasks/rails_ai_context.rake +40 -30
  22. data/lib/rails_ai_context/test_helper.rb +123 -0
  23. data/lib/rails_ai_context/tools/base_tool.rb +69 -0
  24. data/lib/rails_ai_context/tools/get_concern.rb +10 -3
  25. data/lib/rails_ai_context/tools/get_partial_interface.rb +5 -0
  26. data/lib/rails_ai_context/tools/query.rb +5 -5
  27. data/lib/rails_ai_context/tools/runtime_info.rb +4 -0
  28. data/lib/rails_ai_context/tools/search_code.rb +6 -1
  29. data/lib/rails_ai_context/version.rb +1 -1
  30. data/server.json +3 -3
  31. metadata +5 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43e8a71c793404bd950627b8f3319b4e909a0ba7a2cfe937a8d1027e2ede4af0
4
- data.tar.gz: f296960edc1f3f336e90b9549618fd4b48d51d31330d95a65de9304ba47aa0af
3
+ metadata.gz: b89e4a41169c9ddd8a649833f96c6792b6c75ec24101ac220d9c3f431c6126e2
4
+ data.tar.gz: b2e4c8d0fa16d62b8fd2b67e0dddcd6f9754eb085a1b0cbae59d7b6e0d6898d0
5
5
  SHA512:
6
- metadata.gz: bc10e825ef820ad955e89ded454469104160f954b967958866876794b1c114d9b708564e99781f1252cf4faa683d468bf5143823ee3418080de43ef745508ab2
7
- data.tar.gz: 127d4159c5d88da79ae7fcb7389ffb84cca28621e0181da7b7e495e6af6fe0f8c0073b37dff3087a67be78c236e6f09e36aaef82e070461406e8a2027d88d2a8
6
+ metadata.gz: 670a61dd4524c7cae544b3277947191fe4e8ab6f0de54eca4e14eb27c8255e1b1b36c6ad10c99e76e9eac46bc4954676fae9f4680d7108dbc3134422ab9e5d13
7
+ data.tar.gz: f6ca688b6ae4e2c1c41cde1fdba672596cc25815f72d32cd85f039e64596c8c61f30cd24ff2d0996cd7acacb8217abace18eee32ae5ea1d0569d2376d217fad8
data/CHANGELOG.md CHANGED
@@ -5,6 +5,56 @@ 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.6.0] — 2026-04-09
9
+
10
+ ### Added — Auto-Registration, TestHelper & Bug Fixes
11
+
12
+ Developer experience improvements inspired by action_mcp patterns, plus 5 security/correctness bug fixes.
13
+
14
+ - **Auto-registration via `inherited` hook** — Tools are now auto-discovered from `BaseTool` subclasses. No manual list to maintain — drop a file in `tools/` and it's registered. `Server.builtin_tools` is the new public API. Thread-safe via `@registry_mutex` with deadlock-free design (const_get runs outside mutex to avoid recursive locking from inherited). `Server::TOOLS` preserved as deprecated `const_missing` shim for backwards compatibility.
15
+
16
+ - **`abstract!` pattern** — `BaseTool.abstract!` excludes a class from the registry. `BaseTool` itself is abstract. Subclasses are concrete by default.
17
+
18
+ - **TestHelper module** (`lib/rails_ai_context/test_helper.rb`) — Reusable test helper for custom_tools users. Methods: `execute_tool` (by name, short name, or class), `execute_tool_with_error`, `assert_tool_findable`, `assert_tool_response_includes`, `assert_tool_response_excludes`, `extract_response_text`. Works with both RSpec and Minitest. Supports fuzzy name resolution (`schema` → `rails_get_schema`).
19
+
20
+ ### Fixed
21
+
22
+ - **SQL comment stripping validation bypass** (HIGH) — `#` comment stripping now restricted to line-start only, preventing validation bypass via hash characters in string literals. PostgreSQL JSONB operators (`#>>`) preserved.
23
+
24
+ - **SHARED_CACHE read outside mutex** (MEDIUM) — `redact_results` now uses `cached_context` for thread-safe access to encrypted column data.
25
+
26
+ - **McpController double-checked locking** (MEDIUM) — Removed unsynchronized read outside mutex, fixing unsafe pattern on non-GVL Rubies (JRuby/TruffleRuby).
27
+
28
+ - **PG EXPLAIN parser bare rescue** (LOW) — Changed from `rescue` to `rescue JSON::ParserError`, preventing silent swallowing of bugs in `extract_pg_nodes`.
29
+
30
+ - **GetConcern `class_methods` block closing** (LOW) — Indent-based tracking to detect the closing `end`, so `def self.` methods after the block are no longer lost.
31
+
32
+ - **Query spec graceful degradation** — Replaced permanently-pending spec (sqlite3 2.x removed `set_progress_handler`) with a spec that verifies queries execute correctly without it.
33
+
34
+ ## [5.5.0] — 2026-04-08
35
+
36
+ ### Added — Universal MCP Auto-Discovery & Per-Tool Context Optimization (#51-#56)
37
+
38
+ Every AI tool now gets its own MCP config file — auto-detected on project open. No manual setup needed for any supported tool.
39
+
40
+ - **McpConfigGenerator** (`lib/rails_ai_context/mcp_config_generator.rb`) — Shared infrastructure for per-tool MCP config generation. Writes `.mcp.json` (Claude Code), `.cursor/mcp.json` (Cursor), `.vscode/mcp.json` (GitHub Copilot), `opencode.json` (OpenCode), `.codex/config.toml` (Codex CLI). Merge-safe — only manages the `rails-ai-context` entry, preserves other servers. Supports standalone mode and CLI skip.
41
+
42
+ - **Codex CLI support** (#51) — 5th supported AI tool. Reuses `AGENTS.md` (shared with OpenCode) and `OpencodeRulesSerializer` for directory-level split rules. Config via `.codex/config.toml` (TOML format) with `[mcp_servers.rails-ai-context.env]` subsection that snapshots Ruby environment variables at install time — required because Codex CLI `env_clear()`s the process before spawning MCP servers. Works with all Ruby version managers (rbenv, rvm, asdf, mise, chruby, system). Added to all 3 install paths (generator, CLI, rake), doctor checks, and search exclusions.
43
+
44
+ - **Cursor improvements** (#52) — `.cursor/mcp.json` auto-generated for MCP auto-discovery. MCP tools rule changed from `alwaysApply: true` to `alwaysApply: false` with descriptive text for agent-requested (Type 3) loading.
45
+
46
+ - **OpenCode improvements** (#53) — `opencode.json` auto-generated for MCP auto-discovery.
47
+
48
+ - **Claude Code improvements** (#54) — `paths:` YAML frontmatter added to `.claude/rules/` schema, models, and components rules for conditional loading. Context and mcp-tools rules remain unconditional.
49
+
50
+ - **Copilot improvements** (#55) — `.vscode/mcp.json` auto-generated for MCP auto-discovery. `name:` and `description:` YAML frontmatter added to all `.github/instructions/` files. Updated `excludeAgent` spec to validate `code-review`, `coding-agent`, and `workspace` per GitHub Copilot docs.
51
+
52
+ - **All 3 install paths updated** — Install generator, standalone CLI (`rails-ai-context init`), and rake task (`rails ai:setup`) all delegate to McpConfigGenerator. Codex added as option "5" in interactive tool selection.
53
+
54
+ - **Doctor expanded** — `check_mcp_json` now validates per-tool MCP configs based on configured `ai_tools` (JSON parse validation + TOML existence check).
55
+
56
+ - **Search exclusions** — `.codex/`, `.vscode/mcp.json`, `opencode.json` added to `search_code` tool exclusions.
57
+
8
58
  ## [5.4.0] — 2026-04-08
9
59
 
10
60
  ### Added — Phase 3: Dynamic VFS & Live Resource Architecture (Ground Truth Engine Blueprint #39)
data/CONTRIBUTING.md CHANGED
@@ -41,7 +41,7 @@ lib/rails_ai_context/
41
41
  1. Create `lib/rails_ai_context/tools/your_tool.rb` inheriting from `BaseTool` (auto-loaded by Zeitwerk)
42
42
  2. Define `tool_name`, `description`, `input_schema`, and `annotations`
43
43
  3. Implement `def self.call(...)` returning `text_response(string)`
44
- 4. Register in `Server::TOOLS`
44
+ 4. Auto-registered no manual list to update (BaseTool.inherited tracks it)
45
45
  5. Write specs in `spec/lib/rails_ai_context/tools/your_tool_spec.rb`
46
46
 
47
47
  ## Adding a Prism Listener
data/README.md CHANGED
@@ -9,6 +9,7 @@
9
9
  <a href="https://cursor.com"><img src="https://img.shields.io/badge/Cursor-000000?style=for-the-badge&logo=cursor&logoColor=white" alt="Cursor"></a>
10
10
  <a href="https://github.com/features/copilot"><img src="https://img.shields.io/badge/GitHub_Copilot-000000?style=for-the-badge&logo=githubcopilot&logoColor=white" alt="GitHub Copilot"></a>
11
11
  <a href="https://opencode.ai"><img src="https://img.shields.io/badge/OpenCode-4285F4?style=for-the-badge&logoColor=white" alt="OpenCode"></a>
12
+ <a href="https://codex.openai.com"><img src="https://img.shields.io/badge/Codex_CLI-412991?style=for-the-badge&logo=openai&logoColor=white" alt="Codex CLI"></a>
12
13
  <a href="#-cli--works-everywhere"><img src="https://img.shields.io/badge/Any_Terminal-4EAA25?style=for-the-badge&logo=gnubash&logoColor=white" alt="Any Terminal"></a>
13
14
 
14
15
 
@@ -20,7 +21,7 @@
20
21
  <br>
21
22
  [![Ruby](https://img.shields.io/badge/Ruby-3.2%20%7C%203.3%20%7C%203.4-CC342D)](https://github.com/crisnahine/rails-ai-context)
22
23
  [![Rails](https://img.shields.io/badge/Rails-7.1%20%7C%207.2%20%7C%208.0-CC0000)](https://github.com/crisnahine/rails-ai-context)
23
- [![Tests](https://img.shields.io/badge/Tests-1813%20passing-brightgreen)](https://github.com/crisnahine/rails-ai-context/actions)
24
+ [![Tests](https://img.shields.io/badge/Tests-1889%20passing-brightgreen)](https://github.com/crisnahine/rails-ai-context/actions)
24
25
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
25
26
 
26
27
  </div>
@@ -131,7 +132,7 @@ Compare what AI outputs with and without these tools wired in. The difference is
131
132
 
132
133
  ### MCP Server (stdio)
133
134
 
134
- AI calls tools directly via the protocol. Auto-discovered through `.mcp.json`.
135
+ AI calls tools directly via the protocol. Each AI tool gets its own config file — auto-detected on project open.
135
136
 
136
137
  ```
137
138
  rails ai:serve
@@ -438,7 +439,7 @@ cd your-rails-app
438
439
  rails-ai-context init
439
440
  ```
440
441
 
441
- Both paths ask which AI tools you use and whether you want MCP or CLI mode. `.mcp.json` is auto-detected by Claude Code and Cursor.
442
+ Both paths ask which AI tools you use (Claude Code, Cursor, GitHub Copilot, OpenCode, Codex CLI) and whether you want MCP or CLI mode. Each tool gets its own MCP config file — auto-detected on project open.
442
443
 
443
444
  <br>
444
445
 
@@ -518,7 +519,7 @@ Events: `rails_ai_context.tools.call`, `rails_ai_context.resources.read`, and mo
518
519
  ## About
519
520
 
520
521
  Built by a Rails developer with 10+ years of production experience.<br>
521
- 1813 tests. 38 tools. 5 resource templates. 31 introspectors. Standalone or in-Gemfile.<br>
522
+ 1889 tests. 38 tools. 5 resource templates. 31 introspectors. Standalone or in-Gemfile.<br>
522
523
  MIT licensed. [Contributions welcome.](CONTRIBUTING.md)
523
524
 
524
525
  <br>
data/SECURITY.md CHANGED
@@ -4,6 +4,10 @@
4
4
 
5
5
  | Version | Supported |
6
6
  |---------|--------------------|
7
+ | 5.5.x | :white_check_mark: |
8
+ | 5.4.x | :white_check_mark: |
9
+ | 5.3.x | :white_check_mark: |
10
+ | 5.2.x | :white_check_mark: |
7
11
  | 5.1.x | :white_check_mark: |
8
12
  | 5.0.x | :white_check_mark: |
9
13
  | 4.7.x | :white_check_mark: |
@@ -18,7 +18,7 @@ module RailsAiContext
18
18
  # Class-level memoization — transport persists across requests.
19
19
  # Thread-safe: MCP::Server and transport are stateless for reads.
20
20
  def mcp_transport
21
- @mcp_transport || @transport_mutex.synchronize do
21
+ @transport_mutex.synchronize do
22
22
  @mcp_transport ||= begin
23
23
  server = RailsAiContext::Server.new(Rails.application, transport: :http).build
24
24
  MCP::Server::Transports::StreamableHTTPTransport.new(server)
data/docs/GUIDE.md CHANGED
@@ -47,7 +47,7 @@ rails ai:context
47
47
  This creates:
48
48
  1. `config/initializers/rails_ai_context.rb` — configuration file
49
49
  2. `.rails-ai-context.yml` — standalone config (enables switching later)
50
- 3. `.mcp.json`MCP auto-discovery for Claude Code and Cursor
50
+ 3. Per-tool MCP config files — auto-discovery for Claude Code, Cursor, Copilot, OpenCode, and Codex
51
51
  4. Context files — tailored for each AI assistant
52
52
 
53
53
  ### Option B: Standalone (no Gemfile entry needed)
@@ -60,16 +60,16 @@ rails-ai-context init
60
60
 
61
61
  This creates:
62
62
  1. `.rails-ai-context.yml` — configuration file
63
- 2. `.mcp.json`MCP auto-discovery (if MCP mode selected)
63
+ 2. Per-tool MCP config files — auto-discovery (if MCP mode selected)
64
64
  3. Context files — tailored for each AI assistant
65
65
 
66
66
  No Gemfile entry, no initializer, no files in your project besides config and context.
67
67
 
68
68
  ### What the install generator does
69
69
 
70
- 1. Creates `.mcp.json` in project root (MCP auto-discovery)
70
+ 1. Creates per-tool MCP config files (`.mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json`, `opencode.json`, `.codex/config.toml`)
71
71
  2. Creates `config/initializers/rails_ai_context.rb` with commented defaults
72
- 3. Asks which AI tools you use (Claude, Cursor, Copilot, OpenCode)
72
+ 3. Asks which AI tools you use (Claude, Cursor, Copilot, OpenCode, Codex)
73
73
  4. Asks whether to enable MCP server (`tool_mode: :mcp`) or use CLI-only mode (`tool_mode: :cli`)
74
74
  5. Adds `.ai-context.json` to `.gitignore` (JSON cache — markdown files should be committed)
75
75
  6. Generates all context files
@@ -159,7 +159,7 @@ end
159
159
  | `.cursor/rules/rails-project.mdc` | Project overview | `alwaysApply: true` — loaded in every conversation. |
160
160
  | `.cursor/rules/rails-models.mdc` | Model reference | `globs: app/models/**/*.rb` — auto-attaches when editing models. |
161
161
  | `.cursor/rules/rails-controllers.mdc` | Controller reference | `globs: app/controllers/**/*.rb` — auto-attaches when editing controllers. |
162
- | `.cursor/rules/rails-mcp-tools.mdc` | MCP tool reference | `alwaysApply: true` — always available. |
162
+ | `.cursor/rules/rails-mcp-tools.mdc` | MCP tool reference | `alwaysApply: false` — agent-requested when relevant. |
163
163
 
164
164
  ### GitHub Copilot (5 files)
165
165
 
@@ -193,6 +193,7 @@ Commit **all files except `.ai-context.json`** (which is gitignored). This gives
193
193
  | `rails ai:context:full` | full | all | Generate all files in full mode |
194
194
  | `rails ai:context:claude` | compact | Claude | CLAUDE.md + .claude/rules/ |
195
195
  | `rails ai:context:opencode` | compact | OpenCode | AGENTS.md + per-directory AGENTS.md |
196
+ | `rails ai:context:codex` | compact | Codex | AGENTS.md + .codex/config.toml |
196
197
  | `rails ai:context:cursor` | compact | Cursor | .cursor/rules/ |
197
198
  | `rails ai:context:copilot` | compact | Copilot | copilot-instructions.md + .github/instructions/ |
198
199
  | `rails ai:context:json` | — | JSON | .ai-context.json |
@@ -212,7 +213,7 @@ Commit **all files except `.ai-context.json`** (which is gitignored). This gives
212
213
 
213
214
  | Command | Transport | Description |
214
215
  |---------|-----------|-------------|
215
- | `rails ai:serve` | stdio | Start MCP server for Claude Code / Cursor. Auto-discovered via `.mcp.json`. |
216
+ | `rails ai:serve` | stdio | Start MCP server. Auto-discovered by each AI tool via its own config file. |
216
217
  | `rails ai:serve_http` | HTTP | Start MCP server at `http://127.0.0.1:6029/mcp`. For remote clients. |
217
218
 
218
219
  ### Utilities
@@ -228,7 +229,7 @@ Commit **all files except `.ai-context.json`** (which is gitignored). This gives
228
229
  The gem ships a `rails-ai-context` executable that works **without adding the gem to your Gemfile**. Install globally with `gem install rails-ai-context`, then run from any Rails app directory.
229
230
 
230
231
  ```bash
231
- rails-ai-context init # Interactive setup (creates .rails-ai-context.yml + .mcp.json)
232
+ rails-ai-context init # Interactive setup (creates .rails-ai-context.yml + MCP configs)
232
233
  rails-ai-context serve # Start MCP server (stdio)
233
234
  rails-ai-context serve --transport http # Start MCP server (HTTP, port 6029)
234
235
  rails-ai-context serve --transport http --port 8080 # Custom port
@@ -1036,9 +1037,19 @@ curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=rails-ai-cont
1036
1037
 
1037
1038
  ### Auto-discovery (recommended)
1038
1039
 
1039
- The install generator (or `rails-ai-context init`) creates `.mcp.json` in your project root:
1040
+ The install generator (or `rails-ai-context init`) creates per-tool MCP config files based on your selected AI tools:
1040
1041
 
1041
- **In-Gemfile:**
1042
+ | AI Tool | Config File | Root Key | Format |
1043
+ |---------|------------|----------|--------|
1044
+ | Claude Code | `.mcp.json` | `mcpServers` | JSON |
1045
+ | Cursor | `.cursor/mcp.json` | `mcpServers` | JSON |
1046
+ | GitHub Copilot | `.vscode/mcp.json` | `servers` | JSON |
1047
+ | OpenCode | `opencode.json` | `mcp` | JSON |
1048
+ | Codex CLI | `.codex/config.toml` | `[mcp_servers]` | TOML |
1049
+
1050
+ Each file is merge-safe — only the `rails-ai-context` entry is managed, other servers are preserved.
1051
+
1052
+ **Example: `.mcp.json` (Claude Code)**
1042
1053
  ```json
1043
1054
  {
1044
1055
  "mcpServers": {
@@ -1050,19 +1061,20 @@ The install generator (or `rails-ai-context init`) creates `.mcp.json` in your p
1050
1061
  }
1051
1062
  ```
1052
1063
 
1053
- **Standalone:**
1054
- ```json
1055
- {
1056
- "mcpServers": {
1057
- "rails-ai-context": {
1058
- "command": "rails-ai-context",
1059
- "args": ["serve"]
1060
- }
1061
- }
1062
- }
1064
+ **Example: `.codex/config.toml` (Codex CLI)**
1065
+ ```toml
1066
+ [mcp_servers.rails-ai-context]
1067
+ command = "bundle"
1068
+ args = ["exec", "rails", "ai:serve"]
1069
+
1070
+ [mcp_servers.rails-ai-context.env]
1071
+ PATH = "/home/user/.rbenv/shims:/usr/local/bin:/usr/bin"
1072
+ GEM_HOME = "/home/user/.rbenv/versions/3.3.0/lib/ruby/gems/3.3.0"
1063
1073
  ```
1064
1074
 
1065
- **Claude Code** and **Cursor** auto-detect this file. No manual config needed just open your project.
1075
+ > **Why the `[env]` section?** Codex CLI `env_clear()`s the process before spawning MCP servers, stripping Ruby version manager paths. The install generator snapshots your current Ruby environment (PATH, GEM\_HOME, GEM\_PATH, GEM\_ROOT, RUBY\_VERSION, BUNDLE\_PATH) so the MCP server can find gems regardless of version manager (rbenv, rvm, asdf, mise, chruby, or system Ruby).
1076
+
1077
+ Each AI tool auto-detects its own config file. No manual config needed — just open your project.
1066
1078
 
1067
1079
  ### Claude Code
1068
1080
 
@@ -1096,7 +1108,7 @@ Or for standalone: replace `"command": "bundle"` / `"args": ["exec", "rails", "a
1096
1108
 
1097
1109
  ### Cursor
1098
1110
 
1099
- Auto-discovered via `.mcp.json`. Or add manually in **Cursor Settings > MCP**:
1111
+ Auto-discovered via `.cursor/mcp.json`. Or add manually in **Cursor Settings > MCP**:
1100
1112
 
1101
1113
  ```json
1102
1114
  {
@@ -1282,7 +1294,7 @@ end
1282
1294
  | `custom_tools` | Array | `[]` | Additional MCP tool classes to register alongside built-in tools |
1283
1295
  | `skip_tools` | Array | `[]` | Built-in tool names to exclude (e.g. `%w[rails_security_scan]`) |
1284
1296
  | `tool_mode` | Symbol | `:mcp` | `:mcp` (MCP primary + CLI fallback) or `:cli` (CLI only, no MCP server needed) |
1285
- | `ai_tools` | Array | `nil` (all) | AI tools to generate context for: `%i[claude cursor copilot opencode]`. Selected during install. |
1297
+ | `ai_tools` | Array | `nil` (all) | AI tools to generate context for: `%i[claude cursor copilot opencode codex]`. Selected during install. |
1286
1298
  | `excluded_models` | Array | internal Rails models | Models to skip |
1287
1299
  | `excluded_paths` | Array | `node_modules tmp log vendor .git` | Paths excluded from code search |
1288
1300
  | `sensitive_patterns` | Array | `.env`, `.key`, `.pem`, credentials | File patterns blocked from search and read tools |
@@ -1408,13 +1420,13 @@ config.introspectors = %i[schema models routes gems auth api]
1408
1420
 
1409
1421
  **Context files loaded:**
1410
1422
  - `CLAUDE.md` — read at conversation start
1411
- - `.claude/rules/*.md` — auto-loaded alongside CLAUDE.md
1423
+ - `.claude/rules/*.md` — auto-loaded alongside CLAUDE.md (schema, models, and components rules use `paths:` frontmatter for conditional loading)
1412
1424
 
1413
1425
  **MCP tools:** Available immediately via `.mcp.json`.
1414
1426
 
1415
1427
  ### Cursor
1416
1428
 
1417
- **Auto-discovery:** Opens `.mcp.json` automatically. No setup needed.
1429
+ **Auto-discovery:** Opens `.cursor/mcp.json` automatically. No setup needed.
1418
1430
 
1419
1431
  **Context files loaded:**
1420
1432
  - `.cursor/rules/*.mdc` — loaded based on `alwaysApply` and `globs` settings
@@ -1422,13 +1434,13 @@ config.introspectors = %i[schema models routes gems auth api]
1422
1434
  **MDC rule activation modes:**
1423
1435
  | Mode | When it activates |
1424
1436
  |------|-------------------|
1425
- | `alwaysApply: true` | Every conversation (project overview, MCP tools) |
1437
+ | `alwaysApply: true` | Every conversation (project overview) |
1426
1438
  | `globs: ["app/models/**/*.rb"]` | When editing files matching the glob pattern |
1427
- | `alwaysApply: false` + `description` | When the AI decides it's relevant based on description |
1439
+ | `alwaysApply: false` + `description` | Agent-requested loaded when AI decides it's relevant (MCP tools rule) |
1428
1440
 
1429
1441
  ### OpenCode
1430
1442
 
1431
- **MCP config:** Add to `opencode.json`:
1443
+ **Auto-discovery:** `opencode.json` is auto-generated by the install generator. Or add manually:
1432
1444
 
1433
1445
  ```json
1434
1446
  {
@@ -1462,13 +1474,15 @@ For standalone: use `"command": ["rails-ai-context", "serve"]` instead.
1462
1474
 
1463
1475
  OpenCode uses **per-directory lazy-loading**: when the agent reads a file, it walks up the directory tree and auto-loads any `AGENTS.md` it finds. This is how split rules work — no globs or frontmatter needed.
1464
1476
 
1465
- **MCP tools:** Available via `opencode.json` config above.
1477
+ **MCP tools:** Available via `opencode.json` (auto-generated or manual config above).
1466
1478
 
1467
1479
  ### GitHub Copilot
1468
1480
 
1481
+ **Auto-discovery:** `.vscode/mcp.json` is auto-generated by the install generator.
1482
+
1469
1483
  **Context files loaded:**
1470
1484
  - `.github/copilot-instructions.md` — repo-wide instructions
1471
- - `.github/instructions/*.instructions.md` — path-specific, activated by `applyTo` glob
1485
+ - `.github/instructions/*.instructions.md` — path-specific, activated by `applyTo` glob (with `name:` and `description:` frontmatter)
1472
1486
 
1473
1487
  **applyTo patterns:**
1474
1488
  | Pattern | When it activates |
@@ -1477,6 +1491,17 @@ OpenCode uses **per-directory lazy-loading**: when the agent reads a file, it wa
1477
1491
  | `app/controllers/**/*.rb` | Editing controller files |
1478
1492
  | `**/*` | All files (MCP tool reference) |
1479
1493
 
1494
+ ### Codex CLI
1495
+
1496
+ **Auto-discovery:** `.codex/config.toml` is auto-generated by the install generator, including an `[env]` subsection that snapshots your Ruby environment for sandbox compatibility.
1497
+
1498
+ **Context files loaded:**
1499
+ - `AGENTS.md` — project overview + MCP tool guide (shared with OpenCode)
1500
+ - `app/models/AGENTS.md` — model listing, auto-loaded when agent reads model files
1501
+ - `app/controllers/AGENTS.md` — controller listing, auto-loaded when agent reads controller files
1502
+
1503
+ **MCP tools:** Available via `.codex/config.toml`.
1504
+
1480
1505
  ---
1481
1506
 
1482
1507
  ## Stack Compatibility
@@ -1628,11 +1653,12 @@ Works in:
1628
1653
 
1629
1654
  ## Troubleshooting
1630
1655
 
1631
- ### MCP server not detected by Claude Code / Cursor
1656
+ ### MCP server not detected by your AI tool
1632
1657
 
1633
- 1. Check `.mcp.json` exists in project root
1634
- 2. Verify it contains `"command": "bundle"` and `"args": ["exec", "rails", "ai:serve"]`
1635
- 3. Restart Claude Code / Cursor
1658
+ 1. Run `rails ai:doctor` it checks per-tool MCP config files
1659
+ 2. Verify the correct config file exists for your tool (`.mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json`, `opencode.json`, `.codex/config.toml`)
1660
+ 3. Re-run install (`rails generate rails_ai_context:install` or `rails-ai-context init`) to regenerate configs
1661
+ 4. Restart your AI tool
1636
1662
 
1637
1663
  ### Context files are too large
1638
1664
 
data/exe/rails-ai-context CHANGED
@@ -167,8 +167,8 @@ class RailsAiContextCLI < Thor
167
167
  # --- Write .rails-ai-context.yml ---
168
168
  write_yaml_config(ai_tools, tool_mode)
169
169
 
170
- # --- Write .mcp.json (MCP mode only) ---
171
- write_standalone_mcp_json if tool_mode == :mcp
170
+ # --- Write per-tool MCP config files (MCP mode only) ---
171
+ write_mcp_configs(ai_tools, tool_mode)
172
172
 
173
173
  # --- Add .ai-context.json to .gitignore ---
174
174
  add_to_gitignore
@@ -200,7 +200,8 @@ class RailsAiContextCLI < Thor
200
200
  "1" => { key: :claude, name: "Claude Code", files: "CLAUDE.md + .claude/rules/" },
201
201
  "2" => { key: :cursor, name: "Cursor", files: ".cursor/rules/" },
202
202
  "3" => { key: :copilot, name: "GitHub Copilot", files: ".github/copilot-instructions.md + .github/instructions/" },
203
- "4" => { key: :opencode, name: "OpenCode", files: "AGENTS.md" }
203
+ "4" => { key: :opencode, name: "OpenCode", files: "AGENTS.md" },
204
+ "5" => { key: :codex, name: "Codex CLI", files: "AGENTS.md + .codex/config.toml" }
204
205
  }.freeze
205
206
 
206
207
  def prompt_ai_tools
@@ -233,7 +234,7 @@ class RailsAiContextCLI < Thor
233
234
  $stderr.puts ""
234
235
  $stderr.puts "Do you also want MCP server support?"
235
236
  $stderr.puts ""
236
- $stderr.puts " 1. Yes — MCP server (generates .mcp.json)"
237
+ $stderr.puts " 1. Yes — MCP server (generates per-tool MCP config files)"
237
238
  $stderr.puts " 2. No — CLI only (no server needed)"
238
239
  $stderr.puts ""
239
240
  $stderr.print "Enter number (default: 1): "
@@ -245,12 +246,15 @@ class RailsAiContextCLI < Thor
245
246
  mode
246
247
  end
247
248
 
248
- # Files/dirs generated per AI tool format — used for cleanup on tool removal
249
+ # Files/dirs generated per AI tool format — used for cleanup on tool removal.
250
+ # MCP config files are NOT listed here — they use merge-safe removal via
251
+ # McpConfigGenerator.remove to preserve other servers' entries.
249
252
  FORMAT_PATHS = {
250
253
  claude: %w[CLAUDE.md .claude/rules],
251
254
  cursor: %w[.cursor/rules],
252
255
  copilot: %w[.github/copilot-instructions.md .github/instructions],
253
- opencode: %w[AGENTS.md app/models/AGENTS.md app/controllers/AGENTS.md]
256
+ opencode: %w[AGENTS.md app/models/AGENTS.md app/controllers/AGENTS.md],
257
+ codex: %w[AGENTS.md app/models/AGENTS.md app/controllers/AGENTS.md]
254
258
  }.freeze
255
259
 
256
260
  def read_previous_ai_tools
@@ -297,10 +301,17 @@ class RailsAiContextCLI < Thor
297
301
  return if to_remove.empty?
298
302
 
299
303
  require "fileutils"
304
+ # Collect paths still needed by remaining tools to avoid deleting shared files
305
+ kept_paths = current.map(&:to_sym).flat_map { |f| FORMAT_PATHS[f] || [] }.to_set
306
+
300
307
  to_remove.each do |fmt|
301
308
  tool = AI_TOOL_OPTIONS.values.find { |t| t[:key] == fmt }
309
+
310
+ # Remove context files (skip if another selected tool still needs them)
302
311
  paths = FORMAT_PATHS[fmt] || []
303
312
  paths.each do |rel_path|
313
+ next if kept_paths.include?(rel_path)
314
+
304
315
  full = File.join(Dir.pwd, rel_path)
305
316
  if File.directory?(full)
306
317
  FileUtils.rm_rf(full)
@@ -310,6 +321,11 @@ class RailsAiContextCLI < Thor
310
321
  $stderr.puts " Removed #{rel_path}"
311
322
  end
312
323
  end
324
+
325
+ # Merge-safe MCP config cleanup — removes only the rails-ai-context entry
326
+ cleaned = RailsAiContext::McpConfigGenerator.remove(tools: [ fmt ], output_dir: Dir.pwd)
327
+ cleaned.each { |f| $stderr.puts " Removed MCP entry from #{File.basename(f)}" }
328
+
313
329
  $stderr.puts " #{tool[:name]} files removed" if tool
314
330
  end
315
331
  end
@@ -339,26 +355,17 @@ class RailsAiContextCLI < Thor
339
355
  $stderr.puts "Created .rails-ai-context.yml"
340
356
  end
341
357
 
342
- def write_standalone_mcp_json
343
- require "json"
344
- mcp_path = File.join(Dir.pwd, ".mcp.json")
345
- server_entry = { "command" => "rails-ai-context", "args" => [ "serve" ] }
346
-
347
- if File.exist?(mcp_path)
348
- existing = JSON.parse(File.read(mcp_path)) rescue {}
349
- existing["mcpServers"] ||= {}
350
- if existing["mcpServers"]["rails-ai-context"] == server_entry
351
- $stderr.puts ".mcp.json already up to date — skipped"
352
- else
353
- existing["mcpServers"]["rails-ai-context"] = server_entry
354
- File.write(mcp_path, JSON.pretty_generate(existing) + "\n")
355
- $stderr.puts "Updated rails-ai-context in .mcp.json"
356
- end
357
- else
358
- content = JSON.pretty_generate({ mcpServers: { "rails-ai-context" => server_entry } }) + "\n"
359
- File.write(mcp_path, content)
360
- $stderr.puts "Created .mcp.json (auto-discovered by Claude Code, Cursor, etc.)"
361
- end
358
+ def write_mcp_configs(ai_tools, tool_mode)
359
+ require_relative "../lib/rails_ai_context/mcp_config_generator"
360
+ generator = ::RailsAiContext::McpConfigGenerator.new(
361
+ tools: ai_tools,
362
+ output_dir: Dir.pwd,
363
+ standalone: true,
364
+ tool_mode: tool_mode
365
+ )
366
+ result = generator.call
367
+ result[:written].each { |f| $stderr.puts " Created/Updated #{f}" }
368
+ result[:skipped].each { |f| $stderr.puts " #{f} unchanged — skipped" }
362
369
  end
363
370
 
364
371
  def show_standalone_instructions(ai_tools, tool_mode)
@@ -375,7 +382,7 @@ class RailsAiContextCLI < Thor
375
382
  $stderr.puts ""
376
383
  $stderr.puts "Commands:"
377
384
  $stderr.puts " rails-ai-context context # Regenerate context files"
378
- $stderr.puts " rails-ai-context tool NAME # Run any of the 38 tools"
385
+ $stderr.puts " rails-ai-context tool NAME # Run any of the #{tool_count_str} tools"
379
386
  if tool_mode == :mcp
380
387
  $stderr.puts " rails-ai-context serve # Start MCP server"
381
388
  end
@@ -383,8 +390,8 @@ class RailsAiContextCLI < Thor
383
390
  $stderr.puts ""
384
391
  if tool_mode == :mcp
385
392
  $stderr.puts "MCP auto-discovery:"
386
- $stderr.puts " .mcp.json is auto-detected by Claude Code and Cursor."
387
- $stderr.puts " No manual config needed — just open your project."
393
+ $stderr.puts " Each AI tool gets its own config file — auto-detected on project open."
394
+ $stderr.puts " No manual config needed."
388
395
  else
389
396
  $stderr.puts "CLI tools:"
390
397
  $stderr.puts " AI agents can run `rails-ai-context tool schema table=users` directly."
@@ -394,6 +401,14 @@ class RailsAiContextCLI < Thor
394
401
  $stderr.puts ""
395
402
  end
396
403
 
404
+ def tool_count_str
405
+ if defined?(::RailsAiContext::Server)
406
+ ::RailsAiContext::Server.builtin_tools.size.to_s
407
+ else
408
+ "38"
409
+ end
410
+ end
411
+
397
412
  def print_context_result(result)
398
413
  (result[:written] || []).each { |f| $stderr.puts " Written: #{f}" }
399
414
  (result[:skipped] || []).each { |f| $stderr.puts " Skipped: #{f} (unchanged)" }