railwatch 0.1.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 (78) hide show
  1. checksums.yaml +7 -0
  2. data/AGENTS.md +122 -0
  3. data/CHANGELOG.md +462 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +226 -0
  6. data/app/controllers/railwatch/beacon_controller.rb +254 -0
  7. data/config/routes.rb +5 -0
  8. data/docs/ai-and-mcp.md +227 -0
  9. data/docs/configuration.md +931 -0
  10. data/docs/faq.md +230 -0
  11. data/docs/getting-started.md +279 -0
  12. data/docs/records.md +834 -0
  13. data/docs/replacing-nightwatch.md +216 -0
  14. data/docs/replacing-sentry.md +573 -0
  15. data/docs/security.md +94 -0
  16. data/docs/self-hosting.md +60 -0
  17. data/docs/source-maps.md +60 -0
  18. data/docs/testing.md +175 -0
  19. data/docs/troubleshooting.md +319 -0
  20. data/lib/generators/railwatch/install/install_generator.rb +280 -0
  21. data/lib/generators/railwatch/install/templates/initializer.rb +54 -0
  22. data/lib/generators/railwatch/install/templates/post-deploy +98 -0
  23. data/lib/generators/railwatch/install/templates/railwatch.ts +658 -0
  24. data/lib/railwatch/attachments.rb +83 -0
  25. data/lib/railwatch/backtrace.rb +158 -0
  26. data/lib/railwatch/buffer.rb +122 -0
  27. data/lib/railwatch/clock.rb +25 -0
  28. data/lib/railwatch/configuration.rb +334 -0
  29. data/lib/railwatch/console.rb +48 -0
  30. data/lib/railwatch/context.rb +125 -0
  31. data/lib/railwatch/controller_helpers.rb +21 -0
  32. data/lib/railwatch/current.rb +32 -0
  33. data/lib/railwatch/engine.rb +144 -0
  34. data/lib/railwatch/execution.rb +367 -0
  35. data/lib/railwatch/faraday.rb +73 -0
  36. data/lib/railwatch/health.rb +188 -0
  37. data/lib/railwatch/job_tracing.rb +49 -0
  38. data/lib/railwatch/middleware/request.rb +289 -0
  39. data/lib/railwatch/minitest.rb +43 -0
  40. data/lib/railwatch/patches/inertia.rb +34 -0
  41. data/lib/railwatch/patches/net_http.rb +102 -0
  42. data/lib/railwatch/patches/rake_task.rb +88 -0
  43. data/lib/railwatch/patches/runner_command.rb +120 -0
  44. data/lib/railwatch/patches.rb +43 -0
  45. data/lib/railwatch/profiler.rb +270 -0
  46. data/lib/railwatch/record.rb +119 -0
  47. data/lib/railwatch/redactor.rb +67 -0
  48. data/lib/railwatch/release_detector.rb +97 -0
  49. data/lib/railwatch/reporter.rb +539 -0
  50. data/lib/railwatch/rspec.rb +139 -0
  51. data/lib/railwatch/sampler.rb +17 -0
  52. data/lib/railwatch/secret_safety.rb +62 -0
  53. data/lib/railwatch/sessions.rb +162 -0
  54. data/lib/railwatch/source_maps.rb +59 -0
  55. data/lib/railwatch/spec_helper.rb +147 -0
  56. data/lib/railwatch/sql_normalizer.rb +398 -0
  57. data/lib/railwatch/subscribers/base.rb +54 -0
  58. data/lib/railwatch/subscribers/broadcasts.rb +107 -0
  59. data/lib/railwatch/subscribers/cache.rb +107 -0
  60. data/lib/railwatch/subscribers/deprecations.rb +26 -0
  61. data/lib/railwatch/subscribers/exceptions.rb +304 -0
  62. data/lib/railwatch/subscribers/jobs.rb +282 -0
  63. data/lib/railwatch/subscribers/logs.rb +137 -0
  64. data/lib/railwatch/subscribers/mail.rb +42 -0
  65. data/lib/railwatch/subscribers/notifications.rb +36 -0
  66. data/lib/railwatch/subscribers/process_info.rb +98 -0
  67. data/lib/railwatch/subscribers/queries.rb +183 -0
  68. data/lib/railwatch/subscribers/requests.rb +94 -0
  69. data/lib/railwatch/subscribers/storage.rb +35 -0
  70. data/lib/railwatch/subscribers/users.rb +159 -0
  71. data/lib/railwatch/subscribers/views.rb +54 -0
  72. data/lib/railwatch/subscribers.rb +34 -0
  73. data/lib/railwatch/transport/http.rb +208 -0
  74. data/lib/railwatch/version.rb +5 -0
  75. data/lib/railwatch.rb +550 -0
  76. data/lib/tasks/railwatch_tasks.rake +289 -0
  77. data/llms.txt +38 -0
  78. metadata +157 -0
@@ -0,0 +1,227 @@
1
+ # AI assistants and MCP
2
+
3
+ Railwatch Cloud is an [MCP](https://modelcontextprotocol.io) server. Point
4
+ Claude Code, Claude Desktop, Cursor, VS Code, or Zed at it and the assistant
5
+ sitting in your editor can read the same production data your dashboards
6
+ show: issues and their stack traces, slow routes, the queries and N+1s
7
+ behind them, stored query plans, stack profiles, logs, deploys, and
8
+ crash-free rates per release. It can also write — resolve an issue, set a
9
+ priority, leave a comment — and every write is signed with your user and the
10
+ agent's name in the issue's activity feed.
11
+
12
+ The endpoint is `<your ingest host>/mcp`. For the hosted platform that's
13
+ `https://railwatch.rebulk.com/mcp`; if you self-host, it is your own host (see
14
+ [`self-hosting.md`](self-hosting.md)). The gem knows which one you're on:
15
+
16
+ ```sh
17
+ bin/rails railwatch:mcp
18
+ ```
19
+
20
+ prints every block below with your platform's host already filled in.
21
+
22
+ ## 1. Get a token
23
+
24
+ MCP tokens are **per person**, not per application: sign in to the platform,
25
+ go to **Settings → Profile → "API & MCP token"**, and press *Generate token*.
26
+ The token starts with `lnt_` and is shown once. It can reach every account
27
+ your user belongs to, and nothing else.
28
+
29
+ This is a different token from the `lt_...` ingest token the gem uses. The
30
+ ingest token writes telemetry for one environment; the MCP token reads it
31
+ back as you.
32
+
33
+ That page also renders the blocks below with the real token substituted in,
34
+ each with a copy button — so the fastest path is: generate, copy, paste.
35
+
36
+ ## 2. Connect a client
37
+
38
+ Everything below uses `https://railwatch.rebulk.com/mcp`; substitute your own
39
+ host if you self-host, and `lnt_your_token_here` for the token.
40
+
41
+ ### Claude Code
42
+
43
+ ```sh
44
+ claude mcp add railwatch --transport http https://railwatch.rebulk.com/mcp \
45
+ --header "Authorization: Bearer lnt_your_token_here"
46
+ ```
47
+
48
+ ### Claude Desktop
49
+
50
+ Claude Desktop speaks stdio, so it needs the `mcp-remote` bridge. In
51
+ `claude_desktop_config.json`:
52
+
53
+ ```json
54
+ {
55
+ "mcpServers": {
56
+ "railwatch": {
57
+ "command": "npx",
58
+ "args": [
59
+ "-y",
60
+ "mcp-remote",
61
+ "https://railwatch.rebulk.com/mcp",
62
+ "--header",
63
+ "Authorization: Bearer lnt_your_token_here"
64
+ ]
65
+ }
66
+ }
67
+ }
68
+ ```
69
+
70
+ ### Cursor
71
+
72
+ `.cursor/mcp.json` in the project, or `~/.cursor/mcp.json` for every project:
73
+
74
+ ```json
75
+ {
76
+ "mcpServers": {
77
+ "railwatch": {
78
+ "url": "https://railwatch.rebulk.com/mcp",
79
+ "headers": { "Authorization": "Bearer lnt_your_token_here" }
80
+ }
81
+ }
82
+ }
83
+ ```
84
+
85
+ ### VS Code
86
+
87
+ `.vscode/mcp.json` in the workspace:
88
+
89
+ ```json
90
+ {
91
+ "servers": {
92
+ "railwatch": {
93
+ "type": "http",
94
+ "url": "https://railwatch.rebulk.com/mcp",
95
+ "headers": { "Authorization": "Bearer lnt_your_token_here" }
96
+ }
97
+ }
98
+ }
99
+ ```
100
+
101
+ ### Zed
102
+
103
+ `settings.json`, also through the `mcp-remote` bridge:
104
+
105
+ ```json
106
+ {
107
+ "context_servers": {
108
+ "railwatch": {
109
+ "source": "custom",
110
+ "command": "npx",
111
+ "args": [
112
+ "-y",
113
+ "mcp-remote",
114
+ "https://railwatch.rebulk.com/mcp",
115
+ "--header",
116
+ "Authorization: Bearer lnt_your_token_here"
117
+ ]
118
+ }
119
+ }
120
+ }
121
+ ```
122
+
123
+ ### Check it without a client
124
+
125
+ ```sh
126
+ curl -sS https://railwatch.rebulk.com/mcp \
127
+ -H "Authorization: Bearer lnt_your_token_here" \
128
+ -H "Content-Type: application/json" \
129
+ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
130
+ ```
131
+
132
+ A JSON body listing tools means the token works. A `401` means it doesn't.
133
+
134
+ `GET https://railwatch.rebulk.com/mcp` and
135
+ `GET https://railwatch.rebulk.com/.well-known/mcp.json` describe the server —
136
+ name, version, protocol version, transport, tool and prompt names, and how
137
+ to authenticate — with no token at all, for clients that probe a host before
138
+ they're configured.
139
+
140
+ ## 3. Tools
141
+
142
+ Every tool returns JSON as the text body of a single content block. **All
143
+ durations are milliseconds.** Every `window` argument takes `1h`, `6h`,
144
+ `24h`, `7d`, or `30d`, and defaults to `24h`.
145
+
146
+ | Tool | Arguments | Returns |
147
+ |---|---|---|
148
+ | `list_applications` | — | applications, their account, and their environments. Start here — everything else takes the `application_slug` and `environment` this returns. |
149
+ | `list_issues` | `application_slug?`, `environment?`, `status?` (`open`/`resolved`/`ignored`, default `open`), `limit?` | issues with key, title, kind, status, priority, occurrence and affected-user counts, culprit, first/last seen. |
150
+ | `get_issue` | `key` | one issue plus the sample occurrence: exception class, message, stack frames, and the execution it happened inside. |
151
+ | `update_issue` | `key`, `status?`, `priority?`, `assignee_email?`, `agent?` | the updated issue. Writes an activity entry attributed to your user and `agent`. |
152
+ | `add_comment` | `key`, `body`, `agent?` | the created comment, attributed the same way. |
153
+ | `list_slow_routes` | `application_slug`, `environment`, `window?` | the 20 slowest routes by p95, each with `group_hash`, count, errors, `p95_ms`. |
154
+ | `get_route` | `application_slug`, `environment`, `route` or `group_hash`, `window?` | one route's count/errors, p50/p95/p99/max, its slowest individual requests, the slowest queries those requests ran, and each N+1 with a concrete Active Record fix. |
155
+ | `search_requests` | `application_slug`, `environment`, `q?`, `window?`, `limit?` | individual requests. `q` is the dashboard's filter grammar: `method:GET`, `route:/checkout`, `status:500` or `status:5xx`, `deploy:…`, `tenant:…`, `user:…`, `min_ms:250`; bare words match the route name. |
156
+ | `get_execution` | `application_slug`, `environment`, `execution_id` | one execution and its full child timeline — every query, cache read, log line, outgoing request, view render, and exception, each offset in ms from the start. |
157
+ | `explain_query` | `application_slug`, `environment`, `group_hash`, `window?` | the stored query plan for a query group, with the SQL and the sample's duration. `explain` is null unless the app sets `RAILWATCH_CAPTURE_QUERY_EXPLAIN`. `sql` is the normalized shape unless the app also sets `RAILWATCH_CAPTURE_SQL_VALUES`. |
158
+ | `get_profile` | `application_slug`, `environment`, `profile_id?`, `execution_id?`, `limit?` | the hottest frames of a stack profile — self and total samples, each with a percentage. |
159
+ | `search_logs` | `application_slug`, `environment`, `q`, `level?`, `limit?` | matching log lines, each with the `execution_id` to expand with `get_execution`. |
160
+ | `list_tenants` | `application_slug`, `environment`, `window?`, `q?` | your app's own tenants (whatever it passes to `Railwatch.context(tenant:)`) with request, error, job, exception, and user counts. |
161
+ | `recent_deploys` | `application_slug`, `environment` | the 20 most recent deploys with ref, name, time, and link. |
162
+ | `release_health` | `application_slug`, `environment`, `window?` | crash-free session rate, crash-free user rate, and adoption per release. |
163
+ | `list_alerts` | `application_slug?`, `event?`, `status?`, `limit?` | fired alerts: which rule, which issue, which integration, and whether delivery succeeded. |
164
+
165
+ ## 4. Prompts
166
+
167
+ Three canned workflows. A prompt is a plan, not an answer: it tells the
168
+ assistant which tools to call in which order and what to do with each
169
+ result, so its first turn is spent working rather than asking you which tool
170
+ exists.
171
+
172
+ | Prompt | Arguments | What it does |
173
+ |---|---|---|
174
+ | `triage_issue` | `key` | Reads the issue and its sample, pulls the execution timeline around the failure, searches the logs for the same failure elsewhere, lines `first_seen_at` up against recent deploys, then reports what breaks, for whom, how often, and the smallest fix. It will comment and set a priority; it is told not to resolve. |
175
+ | `slow_route` | `application_slug`, `environment`, `route`, `window?` | Pulls the route summary, its slow queries and N+1s, the stored plan for each query group, and a stack profile if one exists, then reports the indexes and `includes` to add, ordered by expected saving, quoting measured milliseconds. |
176
+ | `daily_summary` | `application_slug`, `environment`, `window?` | New issues, spiking issues, deploys, release-health movement, the worst routes, and what already alerted — leading with the one thing worth acting on. |
177
+
178
+ In Claude Code these appear as slash commands once the server is connected.
179
+
180
+ ## 5. Resources
181
+
182
+ The server also publishes documents an assistant can read without a tool
183
+ call:
184
+
185
+ | URI | Contents |
186
+ |---|---|
187
+ | `railwatch://applications` | Every application and environment the token can see. |
188
+ | `railwatch://applications/<slug>/environments/<name>/summary` | Request and job volume, p95, errors, open issue count, last-seen time, and deploys, over the last 24 hours, with the previous 24 hours alongside for comparison. |
189
+ | `railwatch://docs/<name>` | Railwatch's own documentation — `readme`, `getting-started`, `configuration`, `records`, `testing`, `replacing-sentry`, `troubleshooting`, `faq`, and the rest. An assistant that doesn't know an option can look it up instead of guessing. |
190
+
191
+ ## 6. What an assistant can and can't do
192
+
193
+ - **Scope.** A token reaches exactly the accounts its user belongs to.
194
+ Revoke it by regenerating: Settings → Profile → *Regenerate token*
195
+ invalidates the old one immediately.
196
+ - **Writes.** Only three things write: `update_issue`, `add_comment`, and
197
+ the activity entries they create. There is no tool that deletes anything,
198
+ changes billing, or touches your Rails app.
199
+ - **Attribution.** Every write records your user *and* the `agent` name the
200
+ client sent, so "who resolved this" has an honest answer in the UI.
201
+ - **Payloads.** Tools return what the dashboards show, which is what the gem
202
+ shipped. If you don't want request parameters or job arguments leaving
203
+ your app, they never arrive here in the first place — see the redaction
204
+ and opt-in capture settings in
205
+ [`configuration.md`](configuration.md).
206
+
207
+ ## 7. Agents working on your app
208
+
209
+ Separately from the MCP server, the gem ships two files for coding agents
210
+ working *in a Rails app that uses Railwatch*:
211
+
212
+ - [`../llms.txt`](../llms.txt) — the [llmstxt.org](https://llmstxt.org)
213
+ index: one paragraph on what Railwatch is, then every document with a
214
+ one-line description.
215
+ - [`../AGENTS.md`](../AGENTS.md) — how to install it, the facade methods,
216
+ the spec matchers, `railwatch:doctor`, and this MCP hookup, in under 120
217
+ lines.
218
+
219
+ Copy either into your own app's repo to give its agent the same context.
220
+
221
+ ## See also
222
+
223
+ - [`getting-started.md`](getting-started.md) — install, token, first request.
224
+ - [`self-hosting.md`](self-hosting.md) — pointing the gem, and this endpoint,
225
+ at your own platform.
226
+ - [`troubleshooting.md`](troubleshooting.md) — when something isn't
227
+ reporting.