Ruby gem · MCP server · CLI · MIT · v5.39.1

Your coding agent guesses your Rails app. Let it ask.

rails-ai-context turns your Rails app into the source of truth for Claude Code, Cursor, GitHub Copilot, OpenCode and Codex CLI: 35 read-only tools answer from the schema, the models, the routes, the controllers and the views, over MCP or in any terminal.

Every answer is read from the app, says how sure it is, and still comes when the app cannot boot.

$ bundle add rails-ai-context --group development
$ bin/rails generate rails_ai_context:install --tools claude --defaults
Plate 1One answer, as the assistant reads it
# Article [STATIC]## Columns- **id** | bigint | primary key | NOT NULL- **title** | string | NOT NULL... 8 more## Associations- `belongs_to` **author** (class: User) (inverse_of: :articles) (fk: author_id)- `has_many` **comments** dependent: destroy## Validations- `presence` on author _(implicit from belongs_to)_- `presence` on title... 4 more## Scopes- `recent` → order(published_at: :desc, created_at: :desc) [STATIC]- `by_author` → where(author: user) [STATIC]... 1 more_Next: `rails_get_schema(table:"articles")` for columns/indexes | `rails_get_controllers(controller:"ArticlesController")` for actions | `rails_analyze_feature(feature:"Article")` for full-stack view_
  1. The model and the tier the answer came from. [STATIC] here, since the sample app is read without booting.
  2. Its columns: from db/schema.rb in this tier, from the database connection when the app boots.
  3. Each association as the source declares it, with its class and its foreign key.
  4. Validations, the one a belongs_to adds on its own included.
  5. Scopes with their bodies, each carrying how sure the reader is of it.
  6. A hint for the next call, so the assistant asks the right tool next.
Article in the sample app, answered by rails-ai-context 5.39.1 at 2c96694 while this page was built, trimmed to five of its sections and the first two lines of each. The whole answer is below.

The problem

The facts are in your app. The assistant writes from memory.

An assistant writing Rails code knows what an average Rails app looks like. Yours has its own columns, associations, filters and test setup, and it starts every session with none of that. You catch the guess, fix it, re-prompt, and something next to it breaks.

You ask it to
Without
With rails-ai-context
Add a subscription_tier column to users
Writes the migration, duplicates a column that exists
Reads the live schema, sees subscription_status, asks before migrating
Call user.posts in a controller
Guesses the name; NoMethodError at runtime
Resolves the real association from the model
Write tests for a new model
Scaffolds with FactoryBot in a fixture suite
Reads the test setup and matches it
Fix a failing create action
Misses the authenticate_user! a parent controller adds
Gets the inherited filters beside the action
Trace where publishable? is used
Reads six files in sequence, still misses a caller
One call: definition, callers by file, tests

What it answers

The assistant asks. The app answers.

Each answer below was recorded while this page was built, by running the gem at 2c96694 against the sample app in this repository: a blog with Devise, an admin namespace, a JSON API, a Sidekiq job and a FactoryBot suite. It was read without booting, which is the tier an app with a missing credential or a broken initializer gets.

It meets the app: a walkthrough in one call

The stack, the gems that matter, the models and how they connect, the routed controllers, the jobs, the mailers, the frontend and how tests are set up. The context files the install writes carry a compact version of this, so the first turn of a session starts here.

# Welcome to Bulletin

## Stack

Bulletin is a Rails 7.2.2 application declaring Ruby 3.4.9p0 on PostgreSQL (3 tables).
Notable gems - auth: devise, pundit; jobs: sidekiq; frontend: turbo-rails, stimulus-rails; database: pg; testing: rspec-rails, factory_bot_rails.

## Data Model

The app has 3 models. The central ones are:

- **Article** (table: `articles`) - belongs_to :author, has_many :comments. 6 validations.
- **Comment** (table: `comments`) - belongs_to :article, belongs_to :user. 4 validations.
- **User** (table: `users`) - has_many :articles, has_many :comments. 7 validations.

## Authentication & Authorization

Authentication via Devise on User (database_authenticatable, registerable, recoverable, rememberable, validatable).
Authorization via Pundit (2 policies).

## Key Flows

- **articles** - index, create, new, edit, show, update, destroy (GET /, GET /articles, POST /articles)
- **admin/articles** - publish, index, show (POST /admin/articles/:id/publish, GET /admin/articles, GET /admin/articles/:id)
- **api/v1/articles** - index, show (GET /api/v1/articles, GET /api/v1/articles/:id)
- **comments** - create (POST /articles/:article_id/comments)
- **rails/health** - show (GET /up)

Total: 15 app routes across 5 routed controllers, 1 dynamic construct not expanded, routes Rails' engines and gems draw at boot not read without booting.

## Background Jobs & Async

1 background job: PublishArticleJob.
1 mailer.

## Frontend

Stimulus: 1 controller for interactive behavior.
Hotwire: 1 Turbo Frames, 1 Turbo Streams.

## Testing

Framework: rspec.
Data setup: FactoryBot (3 factories in 3 files).

Run tests: `bundle exec rspec`

## Getting Started

```bash
git clone <repo-url>
cd sample_app
bundle install
bundle exec rspec  # verify everything works
```

## Rake Tasks

- `articles:publish_ready` - Publish every reviewed article whose author is active (`lib/tasks/articles.rake`)


---
_[STATIC] Static mode (static mode requested with --no-boot). Serving static analysis; runtime-only data is marked [UNAVAILABLE]. Run `bundle exec rails-ai-context --app-path site/sample_app doctor` for details._

---
_Something wrong, missing or failing in rails-ai-context itself, or an improvement? Ask the user whether to file it at https://github.com/crisnahine/rails-ai-context/issues_

$ rails-ai-context --app-path site/sample_app tool onboard --no-boot

It asks about a table

Columns with their types, nullability and defaults, the indexes, and which model sits on the table. With the app booted the answer comes from the database connection; without it, from db/schema.rb, db/structure.sql or the migrations.

## Table: users

**Models:** User
**Primary key:** id

| Column | Type | Null | Default |
|--------|------|------|---------|
| id | bigint | **NO** |  |
| email | string | **NO** | "" |
| encrypted_password | string | **NO** | "" |
| name | string | yes |  |
| role | integer | **NO** | 0 |
| active | boolean | **NO** | true |
| api_token | string | yes |  |
| reset_password_token | string | yes |  |
| reset_password_sent_at | datetime | yes |  |
| remember_created_at | datetime | yes |  |
| created_at | datetime | **NO** |  |
| updated_at | datetime | **NO** |  |

### Indexes
- `index_users_on_api_token` on (api_token) (unique)
- `index_users_on_email` on (email) (unique)
- `index_users_on_reset_password_token` on (reset_password_token) (unique)

_Next: `rails_get_model_details(model:"User")` for associations, validations, scopes._

---
_[STATIC] Static mode (static mode requested with --no-boot). Serving static analysis; runtime-only data is marked [UNAVAILABLE]. Run `bundle exec rails-ai-context --app-path site/sample_app doctor` for details._

---
_Something wrong, missing or failing in rails-ai-context itself, or an improvement? Ask the user whether to file it at https://github.com/crisnahine/rails-ai-context/issues_

$ rails-ai-context --app-path site/sample_app tool schema --table users --no-boot

Before it calls an association

The model is read with the Prism parser: associations with their options, validations including the implicit ones, enums, scopes with their bodies, callbacks in order, the concerns it includes and the methods they bring. A scope with a literal body is [VERIFIED], one built at runtime is [INFERRED], and in this tier every record is capped at [STATIC].

# Article [STATIC]

**Table:** `articles`
**File:** `app/models/article.rb` (32 lines)
**Structure:** class definition(1-1) → includes(2-3) → associations(4-6) → macros(7-8) → validations(9-12) → callbacks(13-15) → scopes(16-18) → instance methods(19-22) → private(23-24) → instance methods(25-32)
**Primary key:** `id`

## Columns
- **id** | bigint | primary key | NOT NULL
- **title** | string | NOT NULL
- **slug** | string | NOT NULL
- **body** | text
- **status** | integer | NOT NULL | default: 0
- **published_at** | datetime
- **comments_count** | integer | NOT NULL | default: 0
- **author_id** | bigint | NOT NULL
- **created_at** | datetime | NOT NULL
- **updated_at** | datetime | NOT NULL

## Associations
- `belongs_to` **author** (class: User) (inverse_of: :articles) (fk: author_id)
- `has_many` **comments** dependent: destroy

_Static tier: only the associations the source declares; a gem that adds associations at boot is not listed._

## Validations
- `presence` on author _(implicit from belongs_to)_
- `presence` on title
- `length` on title (maximum: 120)
- `presence` on slug
- `uniqueness` on slug
- `presence` on body (if: :published?)

## Enums
- `status`: draft(0), in_review(1), published(2) [integer]

## Scopes
- `recent` → order(published_at: :desc, created_at: :desc) [STATIC]
- `by_author` → where(author: user) [STATIC]
- `published` → where(status: :published).where.not(published_at: nil) [STATIC]

## Callbacks
- `before_validation`: :assign_slug (on: :create)
- `after_commit (on: :update)`: :notify_author (if: :saved_change_to_status?)

## Concerns
- **Publishable** - publishable?, publish!

## Key instance methods
- `to_param`

_Next: `rails_get_schema(table:"articles")` for columns/indexes | `rails_get_controllers(controller:"ArticlesController")` for actions | `rails_analyze_feature(feature:"Article")` for full-stack view_

---
_[STATIC] Static mode (static mode requested with --no-boot). Serving static analysis; runtime-only data is marked [UNAVAILABLE]. Run `bundle exec rails-ai-context --app-path site/sample_app doctor` for details._

---
_Something wrong, missing or failing in rails-ai-context itself, or an improvement? Ask the user whether to file it at https://github.com/crisnahine/rails-ai-context/issues_

$ rails-ai-context --app-path site/sample_app tool model_details --model Article --no-boot

Before it writes an action

The actions, every filter that applies with where it was inherited from, the strong parameters, the layout, and schema hints for the models the controller touches. The admin controller below gets authenticate_user! from ApplicationController and require_admin from its own base class.

# Admin::ArticlesController

**Parent:** `Admin::BaseController`
**Layout:** `admin` (declared in Admin::BaseController)

## Actions
- `index`
- `publish`
- `show`

## Filters
- `before` **authenticate_user!** _(from ApplicationController)_
- `before` **require_admin** _(from Admin::BaseController)_
- `before` **set_article** (only: show, publish)

## Schema Hints

### Article [STATIC]
**Table:** `articles` (pk: `id`)
**Columns:** `id` bigint NOT NULL, `title` string NOT NULL, `slug` string NOT NULL, `body` text, `status` integer NOT NULL, `published_at` datetime, `comments_count` integer NOT NULL, `author_id` bigint NOT NULL, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL
**Associations:** `belongs_to` :author, `has_many` :comments
**Validations:** presence(author), presence(title), length(title), presence(slug), uniqueness(slug), presence(body)


_Next: `rails_get_routes(controller:"admin/articles")` for routes
 | `rails_get_model_details(model:"Article")` for model
 | `rails_get_view(controller:"admin/articles")` for views_

---
_[STATIC] Static mode (static mode requested with --no-boot). Serving static analysis; runtime-only data is marked [UNAVAILABLE]. Run `bundle exec rails-ai-context --app-path site/sample_app doctor` for details._

---
_Something wrong, missing or failing in rails-ai-context itself, or an improvement? Ask the user whether to file it at https://github.com/crisnahine/rails-ai-context/issues_

$ rails-ai-context --app-path site/sample_app tool controllers --controller admin/articles --no-boot

Before it writes a test

The framework, where the factories or fixtures live, the traits they define, and a template in the app's own style, so a new test matches the suite instead of the assistant's habits.

# Test Infrastructure

- **Framework:** rspec
- **Factories:** spec/factories (3 files)

## Test Files
- models: 1 file (spec/models)

## Factory Traits
- **spec/factories/articles.rb:** in_review, published
- **spec/factories/users.rb:** editor, admin

## Test Template (follow this pattern for new tests)
```ruby
require "rails_helper"

RSpec.describe ModelName, type: :model do
  describe "validations" do
    it "requires field" do
      record = ModelName.new
      expect(record).not_to be_valid
      expect(record.errors[:field]).to include("can't be blank")
    end
  end

  describe "#method_name" do
    it "does something" do
      record = create(:model_name)
      expect(record.method_name).to eq(expected)
    end
  end
end
```

---
_[STATIC] Static mode (static mode requested with --no-boot). Serving static analysis; runtime-only data is marked [UNAVAILABLE]. Run `bundle exec rails-ai-context --app-path site/sample_app doctor` for details._

---
_Something wrong, missing or failing in rails-ai-context itself, or an improvement? Ask the user whether to file it at https://github.com/crisnahine/rails-ai-context/issues_

$ rails-ai-context --app-path site/sample_app tool test_info --no-boot

It traces a method

search_code with match_type: trace returns the definition and its source, what it calls, its siblings, every caller grouped by file and labelled by kind, and the tests that reference it. One call in place of a file-by-file search.

# Trace: `publishable?`

## Definition
**app/models/concerns/publishable.rb:8** in `module Publishable`
```ruby
  def publishable?
    in_review? && body.present? && author.active?
  end
```

## Calls internally
- `in_review?`
- `body`
- `author`

## Sibling methods (same file)
- `publish!`

## Called from (4 sites)
### app/controllers/admin/articles_controller.rb (Controller) → `POST /admin/articles/:id/publish`
  13: if @article.publishable?
### app/models/concerns/publishable.rb (Model)
  13: raise ArgumentError, "#{self.class.name} #{id} is not ready to publish" unless publishable?
### app/services/article_publisher.rb (Service)
  11: return false unless @article.publishable?
### lib/tasks/articles.rake (Other)
  5: PublishArticleJob.perform_later(article) if article.publishable?

## Tested by (1 reference)
- `spec/models/article_spec.rb` (1 reference)

---
_[STATIC] Static mode (static mode requested with --no-boot). Serving static analysis; runtime-only data is marked [UNAVAILABLE]. Run `bundle exec rails-ai-context --app-path site/sample_app doctor` for details._

---
_Something wrong, missing or failing in rails-ai-context itself, or an improvement? Ask the user whether to file it at https://github.com/crisnahine/rails-ai-context/issues_

$ rails-ai-context --app-path site/sample_app tool search_code --pattern publishable? --match_type trace --no-boot

It reads the routes

Verbs, paths, actions and route helpers, grouped by controller with the filters that apply to each. Read from config/routes.rb here, so the answer says what it cannot count without booting: the routes engines and gems draw, and the devise_for line it did not expand.

# Routes (15 routes, 1 dynamic construct not expanded)

## admin/articles (filters: authenticate_user!, require_admin, set_article (only: show, publish))
- `POST` `/admin/articles/:id/publish` → publish `publish_admin_article_path(@record)` [id]
- `GET` `/admin/articles` → index `admin_articles_path`
- `GET` `/admin/articles/:id` → show `admin_article_path(@record)` [id]
## api/v1/articles (filters: authenticate!)
- `GET` `/api/v1/articles` → index `api_v1_articles_path`
- `GET` `/api/v1/articles/:id` → show `api_v1_article_path(@record)` [id]
## articles (filters: authenticate_user! (skipped on: index, show), set_article (only: show, edit, update, destroy))
- `GET` `/` → index `root_path`
- `GET` `/articles` → index `articles_path`
- `POST` `/articles` → create
- `GET` `/articles/new` → new `new_article_path`
- `GET` `/articles/:id/edit` → edit `edit_article_path(@record)` [id]
- `GET` `/articles/:id` → show `article_path(@record)` [id]
- `PATCH|PUT` `/articles/:id` → update [id]
- `DELETE` `/articles/:id` → destroy [id]
## comments (filters: authenticate_user!, set_article | formats: html, turbo_stream)
- `POST` `/articles/:article_id/comments` → create `article_comments_path(:article_id)` [article_id]
## rails/health
- `GET` `/up` → show `rails_health_check_path`

_Read from source: routes Rails' engines and gems draw at boot (Active Storage, Action Mailbox, conductor) are read only with the app booted, so no count here includes them._

---
_[STATIC] Static mode (static mode requested with --no-boot). Serving static analysis; runtime-only data is marked [UNAVAILABLE]. Run `bundle exec rails-ai-context --app-path site/sample_app doctor` for details._

---
_Something wrong, missing or failing in rails-ai-context itself, or an improvement? Ask the user whether to file it at https://github.com/crisnahine/rails-ai-context/issues_

$ rails-ai-context --app-path site/sample_app tool routes --no-boot

Every answer ends the same way. A tier line names where the facts came from, sections that need a booted app say [UNAVAILABLE] and why, and most answers name the next call in a hint. The last line asks the assistant to offer you an issue when rails-ai-context itself answered wrong.

35 tools

Read-only, and all of them answer from your app

Over MCP each name carries a rails_ prefix, as in rails_get_schema. From the command line the short name works: rails-ai-context tool schema --table users. About half take detail=summary|standard|full.

Search & Trace

  • search_codeSearch + trace: definition, source, callers, test coverage. Also: match_type:"any" for regex search
  • edit_contextCode around a match with class/method context

Understand

  • analyze_featureFull-stack: models + controllers + routes + services + jobs + views + tests
  • contextStart here - schema + model + controller + routes + views in one call
  • onboardNarrative app walkthrough for new developers or AI agents

Schema & Models

  • schemaColumns with [indexed]/[unique]/[encrypted]/[default] hints
  • model_detailsAssociations, validations, scopes, enums, macros, delegations
  • callbacksCallbacks by type in Rails event order, with source
  • concernConcern methods with source + which models include it

Controllers & Routes

  • controllersAction source + inherited filters + render map + private methods
  • routesRoutes with code-ready helpers and controller filters inline

Views & Frontend

  • viewTemplates with ivars, Turbo wiring, Stimulus refs, partial locals
  • stimulusTargets, values, actions + HTML data-attributes + view lookup
  • partial_interfacePartial locals contract: what to pass + usage examples
  • turbo_mapTurbo Stream/Frame wiring + mismatch warnings

Testing & Quality

  • test_infoTests + fixture contents + test template
  • generate_testGenerate test scaffolding matching project patterns (framework, factories, style)
  • validateSyntax + semantic validation (run after each edit)
  • security_scanBrakeman static analysis: SQL injection, XSS, mass assignment
  • performance_checkN+1 risks, missing indexes, Model.all anti-patterns

App Config & Services

  • configApp config (database, auth, assets, cache, queue); section picks another: gems, frontend, api, engines, conventions, autoload, active_support, env, env_config, i18n
  • helper_methodsApp + framework helpers with view cross-references
  • service_patternService objects: interface, dependencies, side effects, callers
  • job_patternJobs: queue, retries, guard clauses, broadcasts, schedules
  • component_catalogViewComponent/Phlex: props, slots, previews, usage
  • mailersMailer classes with delivery actions and delivery method, mailboxes and their routing

Data & Debugging

  • dependency_graphModel association graph as Mermaid diagram
  • migration_advisorGenerate migration code, flag irreversible ops
  • search_docsBundled topic index with weighted keyword search, on-demand GitHub fetch
  • querySafe read-only SQL queries with timeout, row limit, column redaction
  • read_logsReverse file tail with level filtering and sensitive data redaction
  • diagnoseOne-call error diagnosis: context + git changes + logs + fix suggestions
  • review_changesPR/commit review: file context + warnings (missing indexes, removed validations)
  • runtime_infoLive runtime: DB pool, table sizes, cache stats, job queues, pending migrations
  • session_contextTrack what you've already queried, avoid redundant calls

The list, the names and the one-line summaries are read from the tool registry when this page is built, and the categories from the tools reference, which has every parameter. MCP clients can also read five resource templates and nine static resources, such as rails-ai-context://models/{name} and rails://schema.

How it works

Read once, tagged, handed over

40 introspectors read the app, one per kind of fact. 35 tools compose their answers. The install writes the files each AI tool reads, so the assistant knows the tools exist and how to ask.

What a tool call does

  1. The call arrives over MCP from the AI tool, or from rails-ai-context tool NAME in a terminal, or from bin/rails 'ai:tool[NAME]'.
  2. The registry resolves it. Every tool is auto-registered when its class loads, and the CLI accepts schema, get_schema and rails_get_schema alike.
  3. The introspectors read the app. With the app booted: live reflection, the database connection and the routes Rails drew. Without it: config/routes.rb, the schema dump and the source files, with the answer saying so.
  4. Prism parses each file once into a cache keyed by the file's content, and a single walk feeds every listener: associations, validations, scopes, callbacks, strong parameters.
  5. Each record is tagged by how it was read, and the introspection is cached with a fingerprint of the app, so an edit to a model is seen on the next call.
  6. The answer is composed: schema hints for the models it touches, a cut at the response budget, a tier line and, for most tools, a next-call hint.

What the install writes

  1. The MCP config each chosen tool reads: .mcp.json, .cursor/mcp.json, .vscode/mcp.json and .github/mcp.json, opencode.json or .codex/config.toml, merged into one that exists.
  2. One root context file. AGENTS.md carries the project overview and the tools guide when Cursor, OpenCode or Codex is chosen, and CLAUDE.md imports it; otherwise CLAUDE.md carries it. Copilot gets its own copy.
  3. Rules that load where they apply: the schema when a migration is open, the models under app/models, the controllers under app/controllers, in each tool's own format.
  4. Six anti-hallucination rules in the root file: verify before you write, mark every assumption, query even when it feels standard, check the inheritance chain, treat empty output as a signal, re-query after writes.
  5. Its own configuration: an initializer and .rails-ai-context.yml in a Gemfile install, the YAML file alone standalone, and a .gitignore line.
  6. Nothing you wrote. Your lines around its markers stay, and uninstall takes back only what it wrote.
Plate 2The anatomy of a tag

- `active` → where(active: true) [VERIFIED]

[VERIFIED]
read from a literal in the source, so the assistant can rely on it
[INFERRED]
built from an expression at runtime, so it needs a check before it is relied on
[STATIC]
read without booting the app; a record in this tier is capped here
[UNAVAILABLE]
the source is not there: a section that needs a booted app, or data this app does not have, and the answer says which

The line above is a scope as a booted app answers it; in the answers recorded on this page the same kind of line ends in [STATIC]. 32 of the 40 introspectors answer without a booted app: each declares whether it reads files only, needs live reflection, or answers from another source such as the schema dump. What a tier can say is a declaration, so an answer never guesses at what it could not read. The design is in the architecture page and the tiers in the compatibility page.

Quick start

Install in one paste

Open your AI tool at the root of your Rails app and paste this. It installs the gem, sets up the tool you are in, checks the result and tells you the one step left.

Install the rails-ai-context gem in this Rails app so you can read its schema, models, routes and code through its tools instead of guessing.

1. Run `ruby -v` and `bundle exec rails -v`. rails-ai-context needs Ruby 3.1+ and Rails 7.0+. If either is older, stop and tell me.
2. Pick TOOL, the key for the AI tool you are: claude (Claude Code), cursor (Cursor), copilot (GitHub Copilot in VS Code), opencode (OpenCode), codex (Codex CLI). If you are another AI tool, stop and tell me to use the "Another MCP client" prompt instead.
3. Run `bundle add rails-ai-context --group development --version '>= 5.35.0'`. If the Gemfile already lists it, run `bundle update rails-ai-context --conservative` instead. Then run `bundle exec rails-ai-context version`: it must print 5.35.0 or newer, the first version with the flags used below. If it is older, the Gemfile pins it: change its rails-ai-context line to `gem "rails-ai-context", ">= 5.35.0", group: :development` and run `bundle update rails-ai-context --conservative`. If Bundler refuses (a frozen or deployment bundle), run `gem install rails-ai-context -v '>= 5.35.0'` instead and use `rails-ai-context init --tools TOOL --defaults` in step 4.
4. Run `bundle exec rails generate rails_ai_context:install --tools TOOL --defaults`. If it fails because the app can't boot, run `bundle exec rails-ai-context init --tools TOOL --defaults` instead; that one reads the source files when the app won't start.
5. Run `bundle exec rails-ai-context tool onboard` (or `rails-ai-context tool onboard` if you used gem install) and check it describes this app.
6. Tell me which files changed, then the "Next step" lines the install printed for TOOL, word for word. I have to do that step before your rails-ai-context MCP tools appear.

Or type it yourself

bundle add
bundle add rails-ai-context --group development, then bin/rails generate rails_ai_context:install --tools claude --defaults. The generator asks which AI tools you use and what to write; the flags answer for it.
gem install
gem install rails-ai-context, then rails-ai-context init --tools claude --defaults in the app. The Gemfile stays as it is.
claude plugin
gem install rails-ai-context, then claude plugin install rails-ai-context --marketplace crisnahine/rails-ai-context --scope project in the app's folder. It adds the MCP server to Claude Code, gives each session the six rules, writes no context files, and runs the installed gem.
--tools
claude, cursor, copilot, opencode, codex or all. Add --cli for context files only, or --mcp-only for the MCP config only.

Then one step in your AI tool

Every AI tool holds a new project MCP server back until you say yes. The install prints the step for each tool you picked.

ToolThe step
Claude CodeNew session in the app's folder, approve rails-ai-context. Check with /mcp.
CursorRestart, turn it on under MCP in Customize.
GitHub CopilotTrust the workspace, send a chat message. Check with "MCP: List Servers". Copilot CLI: trust the folder, check with copilot mcp list.
OpenCodeRestart in the app's folder. Check with opencode mcp list.
Codex CLITrust the project, start a new session. Check with /mcp.

Then ask something only your app knows, such as "what columns does the users table have?", and watch it call rails_get_schema.

What it needs

  • Ruby 3.1 or newer and Rails 7.0 to 8.1. CI runs the suite on 17 Ruby and Rails pairs on Linux, plus macOS and Windows legs.
  • Optional: brakeman for security_scan, listen for watch, ripgrep for a faster search_code.
  • A booted app for query, runtime_info and doctor. Everything else also answers from the source.

Three ways in

  • MCP over stdio, the default: each AI tool's config starts bundle exec rails-ai-context serve, or rails-ai-context serve standalone.
  • MCP over HTTP, mounted inside the app at /mcp where it inherits your routing and auth, and refused outside development and test unless you turn it on.
  • The command line, with no server at all: rails-ai-context tool NAME in any terminal, or the same tools as rake tasks.
Every other setup has a prompt

No Gemfile change, CLI only, MCP only, a whole team, a folder of apps, nothing committed, MCP over HTTP, Ruby in Docker, another MCP client such as Claude Desktop, Windsurf or Zed. Each prompt gives every install answer as a flag, so it never stalls on a question. They are in the copy-paste prompts, with prompts to change, upgrade, fix and uninstall the setup.

Uninstall
bundle exec rails-ai-context uninstall && bundle remove rails-ai-context   # in the Gemfile
rails-ai-context uninstall                                                 # standalone

It removes the context files, the MCP entries, its config, its .gitignore lines and its pre-commit hook, and keeps what you wrote: your lines in CLAUDE.md, other servers in an MCP config, a hook you edited.

Limits

What it will not do

Read this before deciding.

  • No tool writes to your app or your database. Every tool reads. query runs inside a read-only transaction with a timeout, a row limit and column redaction, and refuses anything that is not a read. The assistant still decides what to do with the facts. The six rules in the root file ask it to verify before writing; nothing enforces that.
  • Without a boot, it says less. 32 of 40 introspectors answer from the source; the rest say [UNAVAILABLE]. An association a gem adds at boot is not listed, and routes that engines draw are not counted.
  • Rails 9 is not supported. The gemspec caps railties below 9.0, and the standalone binary warns when it boots one.
  • On Windows the MCP configs start the server through cmd /d /c, and query on SQLite runs without its time limit, since there is no fork.

Questions

Questions you should ask first

Does it send my code anywhere?

No. The tools run on your machine, in your app's process or in a child of it. Two things go online: search_docs fetches Rails guides from GitHub when called with fetch: true, and the standalone binary asks rubygems.org for the latest version at most once a day, sending nothing about the app. RAILS_AI_CONTEXT_NO_UPDATE_CHECK=1 turns that off.

Can it read my secrets?

Files that hold them are never served: .env, config/master.key, credentials.yml.enc, config/database.yml, keys and certificates, and more, on a list you can extend. Logs are redacted before they leave the process, get_config shows credential key names without values, and query never returns an encrypted column.

What if my app cannot boot?

The tools answer from config/routes.rb, the schema dump and the source files, every answer says so, and sections that need a running app say [UNAVAILABLE] with the reason. --no-boot asks for that tier outright. The answers on this page were all recorded that way.

What does it write into my repository?

The MCP config for each AI tool you picked, one root context file with a copy for Copilot, rules files per tool, its own config and a .gitignore line. A re-run keeps your lines and adds what is missing; uninstall takes back only what it wrote. A team that wants no AI files in the repo can skip the install: the tools work with none of it.

Which AI tools?

Claude Code, Cursor, GitHub Copilot, OpenCode and Codex CLI get context files and MCP config written for them. Any other MCP client, such as Claude Desktop, Windsurf, Zed or a JetBrains IDE (Copilot or AI Assistant), gets an entry from rails-ai-context mcp-entry. A tool with no MCP at all gets the CLI setup, where the context files name the shell commands.

What does it cost?

It is free and MIT licensed. What you pay is tokens, and only when the assistant asks: a tool answer is sized by its detail level and cut at max_tool_response_chars, 200,000 characters by default. The root context file is held to 12,000 bytes in its default compact mode. The line that offers to file an issue costs about 40 tokens an answer and can be turned off.