Coding Agents (MCP)
Give Claude Code, Codex, Cursor or any MCP client read-only access to your Rails Pulse data, so an agent can find what is slow and why.
Overview
Rails Pulse ships with an MCP server so that a coding agent can look at the same data the dashboard shows: the slowest endpoints since the last deploy, the SQL inside them, the jobs that failed, and the exceptions that are firing. Ask the agent why checkout got slow and it can go and look, instead of guessing from the code.
Everything the agent can call is read-only. The MCP server talks to the JSON API over HTTP with its own token, which cannot record a deployment, change an exception’s status, or touch any other part of your app.
Setup
1. Set an API token in the app
# config/initializers/rails_pulse.rb
RailsPulse.configure do |config|
config.api_token = ENV["RAILS_PULSE_API_TOKEN"]
# or: Rails.application.credentials.dig(:rails_pulse, :api_token)
end
With no api_token, the JSON API refuses every request.
2. Add the mcp gem
The MCP server needs the mcp gem, which Rails Pulse does not pull into production. A development group is enough:
# Gemfile
group :development do
gem "mcp", "~> 1.0"
end
Without it, rails-pulse mcp exits with a message saying what to add.
3. Point the CLI at your app
rails-pulse configure
This prompts for the app’s URL, the API token and the mount path (default /rails_pulse), makes a test request, and saves them to ~/.rails-pulse, readable only by you. Nothing is written to the project directory. The MCP server and the CLI share these credentials.
You can use environment variables instead, which take precedence over the file:
| Variable | Purpose |
|---|---|
RAILS_PULSE_URL | The app’s base URL, such as https://yourapp.com |
RAILS_PULSE_TOKEN | The value of config.api_token |
RAILS_PULSE_MOUNT_PATH | Where the engine is mounted, if not /rails_pulse |
RAILS_PULSE_CONFIG | Read a config file other than ~/.rails-pulse |
4. Register the server with your agent
The server runs over stdio. For Claude Code:
claude mcp add rails-pulse -- bundle exec rails-pulse mcp
For other clients, add a server entry that runs bundle exec rails-pulse mcp from your app’s directory. For example, in Cursor’s .cursor/mcp.json:
{
"mcpServers": {
"rails-pulse": {
"command": "bundle",
"args": ["exec", "rails-pulse", "mcp"]
}
}
}
Tools
The server exposes eleven tools, all marked read-only:
| Tool | What it answers |
|---|---|
rails_pulse_coverage | Which app and environment this is, what has been recorded and how recently, retention, and whether any requests were dropped |
rails_pulse_routes | The tracked endpoints, with request volume, average latency and error counts |
rails_pulse_slow_requests | The slowest endpoints for a period, ranked by average response time |
rails_pulse_endpoint | A detailed profile of one endpoint: latency distribution, error rate and status codes |
rails_pulse_queries | The most expensive SQL, with N+1 detection and the file and line each query came from |
rails_pulse_errors | 4xx and 5xx responses grouped by endpoint |
rails_pulse_exceptions | Exception groups with status, occurrence count, location and latest message |
rails_pulse_exception | One exception group’s recent occurrences, with backtraces, request, filtered params and deploy SHA |
rails_pulse_jobs | Background job failure rates, slow jobs and recent failures |
rails_pulse_deployments | Recent deployments with revision, start and finish time, metadata, and whether each one made things worse |
rails_pulse_insights | What is past its thresholds in a summary period, and whether the thresholds themselves fit |
Time windows
Most tools take a period of last_hour, last_24_hours or last_7_days. They also take since and until as ISO 8601 timestamps, which pin the window instead of measuring back from now. This is what makes a before-and-after comparison around a deploy repeatable. Explicit bounds win over period, a timestamp with no zone is read as UTC, and every tool reports the window it actually measured.
rails_pulse_insights is the exception: it reads one whole summary period, chosen by period (hour, day, week, month) and at, because a P95 cannot be combined across periods.
Exact lookups
An identifier from an alert or a log is a tool argument too, so the agent can jump straight to one record: hashed_sql on rails_pulse_queries, fingerprint on rails_pulse_exceptions (which then ignores status and the window), path and controller_action on rails_pulse_routes, revision on rails_pulse_deployments, and path_prefix on rails_pulse_errors, which groups requests no route matched by path.
From a slow endpoint to its SQL
rails_pulse_endpoint and rails_pulse_slow_requests return a route_id. Pass it to rails_pulse_queries as route and you get only the SQL issued while serving that endpoint, each query with the file and line it came from. That is how an agent gets from “this endpoint is slow” to “this is the query, and here is the code that runs it”.
No data is not the same as no problems
rails_pulse_coverage reports the oldest and newest data held for requests, job runs and exceptions, whether summaries are up to date, and whether the background writer dropped any requests. If a kind of data is not being tracked (for example config.track_jobs is off), it says so instead of reporting zero. Agents are told to call it first, and before reporting that nothing went wrong, so that “no failures recorded” is not read as “no failures”.
Teaching your agent to use it
Two commands write instructions that tell an agent when and how to use Rails Pulse:
rails-pulse install claude # Claude Code skill: ~/.claude/skills/rails-pulse/SKILL.md
rails-pulse install agents # writes ./AGENTS.md
rails-pulse install agents --append # adds a Rails Pulse section to an existing AGENTS.md
--append wraps its section in markers, so running it again replaces that section rather than adding a second copy, and leaves the rest of your AGENTS.md alone. rails-pulse install --list shows every integration and where it writes.
Example prompts
- “Which endpoints got slower since yesterday’s deploy?”
- “Why is
OrdersController#indexslow? Show me the queries behind it.” - “Are there any N+1 queries on the busiest routes?”
- “What’s failing in background jobs this week?”
- “We fixed the checkout query at 14:00. Compare the hour before and the hour after.”
Security
- The MCP server never connects to your database. It reads from the JSON API, so it sees only what the API returns.
api_tokengrants read access only. It is refused by the deployments endpoint, so a token given to an agent cannot record a release. See Deployment Tracking.- Exception params and messages are filtered the same way they are in the dashboard.
- Point the agent at the environment you mean to investigate.
rails_pulse_coveragereports the application name and environment that answered, so numbers from staging are not mistaken for production.