Rails Informant
Self-hosted error monitoring for Rails, built for AI agents
Why Rails Informant? ◆ Quick Start ◆ Configuration ◆ MCP Server ◆ Architecture ◆ Data and Privacy ◆ Security
Captures exceptions, stores them in your app's database with rich context (backtraces, breadcrumbs, request data), sends notifications, and exposes error data via a bundled MCP server -- so AI agents can query, triage, and fix production errors directly.
No dashboard. The agent is the interface.
Why Rails Informant?
-
Agent-native -- 12 MCP tools let AI agents list, inspect, resolve, and fix errors without a browser. The
/informantClaude Code skill provides a complete triage-to-fix workflow. - Self-hosted -- Errors stay in your database. No external service, no data leaving your infrastructure (unless you configure Slack or webhook notifications).
-
Zero-config capture -- Errors captured automatically via
Rails.errorsubscriber and Rack middleware. Breadcrumbs fromActiveSupport::Notificationsprovide structured debugging context. - Lightweight -- Two database tables, no Redis, no background workers beyond ActiveJob. Runtime dependencies: Rails 8.1+ only.
Quick Start
Add to your Gemfile:
gem "rails-informant"Install:
bundle install
bin/rails generate rails_informant:install
bin/rails db:migrateSet an authentication token:
bin/rails credentials:editrails_informant:
api_token: your-secret-token # generate with: openssl rand -hex 32Install Claude Code integration:
bin/rails generate rails_informant:skillErrors are captured automatically in non-local environments. To capture errors manually:
RailsInformant.capture(exception, context: { order_id: 42 })Configuration
# config/initializers/rails_informant.rb
RailsInformant.configure do |config|
config.capture_errors = !Rails.env.local?
config.api_token = Rails.application.credentials.dig(:rails_informant, :api_token)
config.slack_webhook_url = Rails.application.credentials.dig(:rails_informant, :slack_webhook_url)
config.retention_days = 30
endEvery option can be set via an environment variable. The initializer takes precedence over env vars. These configure the Rails app. For MCP server env vars (agent side), see MCP Server > Setup.
| Option | Env var | Default | Description |
|---|---|---|---|
api_token |
INFORMANT_API_TOKEN |
nil |
Authentication token for MCP server access |
capture_errors |
INFORMANT_CAPTURE_ERRORS |
true |
Enable/disable error capture (set to "false" to disable) |
ignored_exceptions |
INFORMANT_IGNORED_EXCEPTIONS |
[] |
Exception classes to skip (comma-separated in env var) |
retention_days |
INFORMANT_RETENTION_DAYS |
nil |
Auto-purge resolved errors after N days |
slack_webhook_url |
INFORMANT_SLACK_WEBHOOK_URL |
nil |
Slack incoming webhook URL |
capture_user_email |
(none) | false |
Capture email from detected user (PII -- opt-in) |
webhook_url |
INFORMANT_WEBHOOK_URL |
nil |
Generic webhook URL for notifications |
Connecting the tokens: The
api_tokenin your Rails credentials andINFORMANT_PRODUCTION_TOKENmust be the same value. The first authenticates incoming requests to your app; the second tells the MCP server what token to send.
Secrets hygiene:
.envrccontains secrets and should be in.gitignore..mcp.jsonis safe to commit -- it only contains the command name, no tokens.
Error Capture
Errors are captured automatically via:
-
Rails.errorsubscriber -- background jobs, mailer errors,Rails.error.handleblocks - Rack middleware -- unhandled request exceptions and rescued framework exceptions
Fingerprinting
Errors are grouped by SHA256(class_name:first_app_backtrace_frame). Line numbers are normalized so the same error at different lines groups together.
Ignored Exceptions
Common framework exceptions (404s, CSRF, etc.) are ignored by default. Add more:
config.ignored_exceptions = ["MyApp::BoringError", /Stripe::/]Breadcrumbs
Structured events from ActiveSupport::Notifications are captured automatically as breadcrumbs -- SQL query names, cache hits, template renders, HTTP calls, job executions. Stored per-occurrence for rich debugging context without raw log lines.
MCP Server
The bundled informant-mcp executable connects Claude Code to your error data via Model Context Protocol (MCP).
Setup
The rails_informant:skill generator creates .mcp.json automatically. Set INFORMANT_PRODUCTION_URL and INFORMANT_PRODUCTION_TOKEN as environment variables (e.g., via .envrc + direnv). The MCP server inherits env vars from your shell.
For multi-environment setups, create ~/.config/informant-mcp.yml:
environments:
production:
url: https://myapp.com
token: ${INFORMANT_PRODUCTION_TOKEN}
staging:
url: https://staging.myapp.com
token: ${INFORMANT_STAGING_TOKEN}Tools
| Tool | Description |
|---|---|
list_environments |
List configured environments |
list_errors |
List error groups with filtering and pagination |
get_error |
Full error detail with recent occurrences |
resolve_error |
Mark as resolved |
ignore_error |
Mark as ignored |
reopen_error |
Reopen a resolved/ignored error |
mark_fix_pending |
Mark with fix SHA for auto-resolve on deploy |
mark_duplicate |
Mark as duplicate of another group |
delete_error |
Delete group and occurrences |
annotate_error |
Add investigation notes |
get_informant_status |
Summary with counts and top errors |
list_occurrences |
List occurrences with filtering |
Local Development
The MCP server enforces HTTPS by default. When pointing at a local HTTP URL (e.g., http://localhost:3000), pass --allow-insecure:
{
"mcpServers": {
"informant": {
"command": "informant-mcp",
"args": ["--allow-insecure"]
}
}
}This is only needed for local development/testing. Production setups over HTTPS don't need it.
Claude Code Skill
Use /informant in Claude Code to triage and fix errors interactively. The skill:
- Checks error status with
get_informant_status - Lists unresolved errors
- Investigates with full occurrence data
- Implements fixes with test-first workflow
- Marks
fix_pendingfor auto-resolution on deploy
Architecture
Development Machine Remote Servers
+-----------------------+ +-----------------------+
| Claude Code | | Production |
| | | | /informant |
| | stdio | +-----------------------+
| v | HTTPS+Token
| MCP Server | -----------> +-----------------------+
| (exe/informant-mcp) | | Staging |
| | | /informant |
+-----------------------+ +-----------------------+
Inside the Rails app:
+-------------------------------------------------+
| Rails.error subscriber (primary capture) |
| Rack Middleware (safety net) |
| - ErrorCapture (before ShowExceptions) |
| - RescuedExceptionInterceptor (after Debug) |
| | |
| v |
| Fingerprint + Upsert (atomic counter) |
| | |
| v |
| Occurrence.create (with breadcrumbs, context) |
| | |
| v |
| NotifyJob.perform_later (async dispatch) |
| - Slack (Block Kit, Net::HTTP) |
| - Webhook (PII stripped by default) |
+-------------------------------------------------+
Error Group Lifecycle
unresolved --> fix_pending --> resolved (auto, on deploy)
unresolved --> resolved (manual)
unresolved --> ignored
unresolved --> duplicate
resolved --> unresolved [REGRESSION]
fix_pending --> unresolved (reopen)
ignored --> unresolved (reopen)
duplicate --> unresolved (reopen)
Deploy Detection
On boot, the engine checks if fix_pending errors have been deployed by comparing the current git SHA against original_sha. Deployed fixes are automatically transitioned to resolved.
Git SHA is resolved from environment variables (GIT_SHA, REVISION, KAMAL_VERSION) or .git/HEAD.
Rake Tasks
bin/rails informant:stats # Show error monitoring statistics
bin/rails informant:purge # Purge resolved errors older than retention_daysData and Privacy
Each occurrence stores the following PII:
-
User email -- only captured when
config.capture_user_email = trueand the user model responds to#email -
IP address -- from
request.remote_ip -
Custom user context -- anything set via
RailsInformant::Current.user_context
For GDPR compliance, only include identifiers needed for debugging (e.g., user ID) rather than personal data. You can override automatic user detection by setting user context explicitly:
# In a before_action or around_action
RailsInformant::Current.user_context = { id: current_user.id }All stored context passes through ActiveSupport::ParameterFilter, so adding keys to filter_parameters suppresses them:
# config/application.rb
config.filter_parameters += [:email]This replaces email values with [FILTERED] in occurrence data. IP addresses can be suppressed the same way by adding :ip.
Security
- MCP server requires token authentication (
secure_compare) - All stored context is filtered through
ActiveSupport::ParameterFilter - MCP server enforces HTTPS by default
- Security headers:
Cache-Control: no-store,X-Content-Type-Options: nosniff - Error capture never breaks the host application
- Webhook payloads strip PII by default
-
Rate limiting -- the engine does not include built-in rate limiting. Add rate limiting on the
/informant/prefix in production, for example with Rack::Attack:
# config/initializers/rack_attack.rb
Rack::Attack.throttle("informant", limit: 60, period: 1.minute) do |req|
req.ip if req.path.start_with?("/informant/")
endLicense
This project is licensed under the MIT License -- see the LICENSE file for details.