45 MCP tools that give AI agents live access to your Rails schema, models, routes & conventions.
Read-only by design. Defense in depth. Every tool is non-destructive.
[!CAUTION] This gem is designed for development environments. The query tool is disabled in production by default. Sensitive files are blocked. All 45 tools are read-only.
rails_search_docs with fetch: trueThe rails_query tool uses a 4-layer security model:
flowchart LR
Q[SQL Query] --> L1{Layer 1\nRegex Validation}
L1 -->|"Blocked:\nINSERT, DROP,\nUNION SELECT..."| R1[Rejected]
L1 -->|SELECT only| L2{Layer 2\nDatabase Read-Only}
L2 -->|"SET TRANSACTION\nREAD ONLY\n+ timeout"| L3{Layer 3\nRow Limit}
L3 -->|"Cap: 1000 rows\nDefault: 100"| L4{Layer 4\nSensitive Columns}
L4 -->|"names password_digest,\napi_key, ..."| R1
L4 -->|"no sensitive column"| OK[Safe Result]
style R1 fill:#e74c3c,stroke:#c0392b,color:#fff
style OK fill:#27ae60,stroke:#1e8449,color:#fff
style L1 fill:#e67e22,stroke:#d35400,color:#fff
style L2 fill:#f39c12,stroke:#e67e22,color:#fff
style L3 fill:#3498db,stroke:#2980b9,color:#fff
style L4 fill:#9b59b6,stroke:#8e44ad,color:#fff
Before any query reaches the database:
/* */), line (--), MySQL (# at line start)After validation, the query runs inside a transaction:
| Database | Mechanism |
|---|---|
| PostgreSQL | SET TRANSACTION READ ONLY + SET LOCAL statement_timeout |
| MySQL | SET TRANSACTION READ ONLY + MAX_EXECUTION_TIME hint |
| SQLite | PRAGMA query_only = ON + progress handler for timeout |
All queries execute inside a transaction, then rollback (even if they could write, they can’t).
config.query_row_limit (hard cap: 1000)LIMIT clause appended to queryA query that names a sensitive column is rejected before execution, not redacted after it:
Default redacted patterns: password_digest, encrypted_password, password_hash, reset_password_token, confirmation_token, unlock_token, otp_secret, session_data, secret_key, api_key, api_secret, access_token, refresh_token, jti
Matching is by name, case-insensitive and word-bounded, against both the defaults above and config.query_redacted_columns. SELECT password_digest AS pd FROM users is blocked outright: post-execution redaction reads the column names the database returns, which the caller controls through aliases and expressions, so it cannot be relied on.
If one of your own columns merely looks sensitive (an oauth_applications.secret, say), exempt it by name:
config.query_allowed_columns = %w[secret]
[!WARNING] Disabled in production by default. Only enable with
config.allow_query_in_production = trueif you understand the implications.
The rails_search_code and file-reading tools block access to sensitive files:
.env .env.*
config/master.key
config/credentials.yml.enc config/credentials/*.yml.enc
config/database.yml config/secrets.yml
config/cable.yml config/storage.yml
config/mongoid.yml config/redis.yml
*.pem *.key *.p12 *.pfx *.jks *.keystore
**/id_rsa **/id_ed25519 **/id_ecdsa **/id_dsa
.ssh/* .aws/credentials .aws/config .netrc .pgpass .my.cnf
Search also excludes generated AI context files to prevent circular references:
CLAUDE.md, .claude/, .mcp.json
.cursor/, .cursorrules
.github/copilot-instructions.md, .github/instructions/, .vscode/mcp.json
AGENTS.md, opencode.json
.codex/
.ai-context.json
config.sensitive_patterns = %w[.env* *.key *.pem credentials.yml.enc]
All file-reading operations validate paths against Rails.root:
real_path = File.realpath(requested_path)
raise unless real_path.start_with?(Rails.root.to_s)
The VFS (rails-ai-context://views/{path}) applies the same protection for view template reads.
Search tools use array-based command execution (never shell strings):
# Safe: array form
Open3.capture2("rg", "--no-heading", pattern, "--", directory)
# Pattern injection prevented by -- separator
File type parameters accept only alphanumeric characters.
On Ruby 3.2 and newer, user-supplied regex patterns have a 1-second timeout:
Regexp.new(pattern, timeout: 1)
Complex patterns that would cause catastrophic backtracking raise RegexpError instead of hanging.
[!WARNING]
Regexp.timeoutdoes not exist on Ruby 3.1, which this gem still supports. There the timeout is skipped, and a pattern crafted to backtrack catastrophically can hang the process serving the tool. If you exposerails_search_codeto input you do not control, run it on Ruby 3.2 or newer.
SafeFile.read provides drop-in safety for all file reads:
config.max_file_size, default: 5 MB)nil on any failure (no exceptions leak)Everything that leaves your app through this gem - log lines, query rows, environment values, config source slices - passes through one redaction module before anything else can touch it. Redacting and shortening are a single operation there, so a long credential cannot be cut apart in a way that hides it from the pattern that would have caught it.
What gets redacted:
redis://user:pass@host)Redacted values carry one of two markers, and only these two:
| Marker | Means |
|---|---|
[FILTERED] |
A value was removed because it is or may be a secret |
[EMAIL] |
An email address was removed |
Marker presence and this vocabulary are part of the output contract (see below) - you can pattern-match on them.
Contract, safe to depend on:
[FILTERED] / [EMAIL] marker vocabularyIncidental, expected to change between releases:
Pin your assertions to the first list. When something in the second list changes in a way that could break a pinned assertion, the CHANGELOG says so.
The rails_migration_advisor tool validates input:
When using HTTP transport (Rack middleware or McpController):
127.0.0.1 (localhost only - not exposed to network)auto_mount is false by default - must be explicitly enabledauto_mount is true in production, and report it as enabled elsewhereThe McpController uses thread-safe transport initialization with mutex synchronization.
rails_get_env returns credential keys, never valuesconfig/credentials/*.yml.enc is in the sensitive patterns listEmail crisjosephnahine@gmail.com. Response within 48 hours.
Supported versions: 4.0.x and later (4.2.1+ includes security hardening). See the repo root SECURITY.md for the full policy.