rails-informant 0.0.5 → 0.0.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9cb09528a587e40da2aa7b66b56483f97953dcde9fd01e172009e859236b1b83
4
- data.tar.gz: ad121656a0ae00abd63c49ffc26f493a0cd5d90412f68ef657ec63dbb2eddce6
3
+ metadata.gz: b66bd1a6283f2a349a57994f529a0b57b71053c979c874268283d97477928873
4
+ data.tar.gz: d17e62d7ba21318f94205d9365d293ce1110fe58e9c56b4c82cc0c334e4f65bf
5
5
  SHA512:
6
- metadata.gz: 9b449febb753c029ac0905a9a9af076039e3b008be77bfe2f444fbabadfb1ca9c4b66e19165c618d326fa78f70bda1cd8e284c0a052425a69cdea13e8aa5b9a8
7
- data.tar.gz: dd18b3b3e42d3551c08dc23bfe68e0f497e7b373713985e7fea8f569b5c352d263491e7ea98967d596cb6748530a702d03289936e5cc50a1a5fe2d22aaba7a66
6
+ metadata.gz: cf7f02ec548bbd755dc411b3f08a9a74807d45e333ccd3aedaf99b02be909ec4b4a046146ca913943dc41b88dc3e78cafcdf140163cdb1de2537c5d3f5a5b170
7
+ data.tar.gz: dd582dce1ccee4a3e6eaa3678c118416f9dff50afeafc00ad0a9a65a59e50760188541817f859d374f8aec5d139b5febb3024c54340b2b5fd98e36aa47fcfcf1
data/README.md CHANGED
@@ -13,10 +13,11 @@
13
13
  <p>
14
14
  <a href="#why-rails-informant">Why Rails Informant?</a>
15
15
  &#9670; <a href="#quick-start">Quick Start</a>
16
+ &#9670; <a href="#agent-setup">Agent Setup</a>
16
17
  &#9670; <a href="#configuration">Configuration</a>
17
18
  &#9670; <a href="#mcp-server">MCP Server</a>
18
19
  &#9670; <a href="#architecture">Architecture</a>
19
- &#9670; <a href="#data--privacy">Data & Privacy</a>
20
+ &#9670; <a href="#data-and-privacy">Data and Privacy</a>
20
21
  &#9670; <a href="#security">Security</a>
21
22
  </p>
22
23
  </div>
@@ -65,6 +66,33 @@ Errors are captured automatically in non-local environments. To capture errors m
65
66
  RailsInformant.capture(exception, context: { order_id: 42 })
66
67
  ```
67
68
 
69
+ ## Agent Setup
70
+
71
+ End-to-end path for AI agents (Claude Code, Devin) to start triaging errors:
72
+
73
+ 1. **Install the gem** -- follow [Quick Start](#quick-start) above. The install generator creates `.mcp.json` automatically.
74
+ 2. **Set an authentication token** in your Rails credentials or env var:
75
+ ```ruby
76
+ # config/initializers/rails_informant.rb
77
+ config.api_token = Rails.application.credentials.dig(:rails_informant, :api_token)
78
+ ```
79
+ 3. **Set env vars** for the MCP server (e.g., via `.envrc` + [direnv](https://direnv.net)):
80
+ ```sh
81
+ # .envrc
82
+ export INFORMANT_PRODUCTION_URL=https://myapp.com
83
+ export INFORMANT_PRODUCTION_TOKEN=your-api-token
84
+ ```
85
+ The MCP server process inherits environment variables from your shell, so `INFORMANT_*` vars set via `.envrc` (or any other method) are picked up automatically -- no need to inline them in `.mcp.json`.
86
+ 4. **Install the Claude Code skill** (optional but recommended):
87
+ ```sh
88
+ bin/rails generate rails_informant:skill
89
+ ```
90
+ 5. **Use `/informant`** in Claude Code to triage and fix errors, or let Devin handle them autonomously with the [Devin integration](#devin-ai).
91
+
92
+ > **Connecting the tokens:** The `api_token` in your Rails credentials and `INFORMANT_PRODUCTION_TOKEN` must be the **same value**. The first authenticates incoming requests to your app; the second tells the MCP server what token to send.
93
+
94
+ > **Secrets hygiene:** `.envrc` contains secrets and should be in `.gitignore`. `.mcp.json` is safe to commit -- it only contains the command name, no tokens.
95
+
68
96
  ## Configuration
69
97
 
70
98
  ```ruby
@@ -77,11 +105,11 @@ RailsInformant.configure do |config|
77
105
  end
78
106
  ```
79
107
 
80
- Every option can be set via an environment variable. The initializer takes precedence over env vars.
108
+ Every 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 [Agent Setup](#agent-setup).
81
109
 
82
110
  | Option | Env var | Default | Description |
83
111
  |--------|---------|---------|-------------|
84
- | `api_token` | `INFORMANT_API_TOKEN` | `nil` | Bearer token for API authentication (required for MCP) |
112
+ | `api_token` | `INFORMANT_API_TOKEN` | `nil` | Authentication token for MCP server access |
85
113
  | `capture_errors` | `INFORMANT_CAPTURE_ERRORS` | `true` | Enable/disable error capture (set to `"false"` to disable) |
86
114
  | `devin_api_key` | `INFORMANT_DEVIN_API_KEY` | `nil` | Devin AI API key for autonomous error fixing |
87
115
  | `devin_playbook_id` | `INFORMANT_DEVIN_PLAYBOOK_ID` | `nil` | Devin playbook ID for error triage workflow |
@@ -114,52 +142,27 @@ config.ignored_exceptions = ["MyApp::BoringError", /Stripe::/]
114
142
 
115
143
  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.
116
144
 
117
- ## API
118
-
119
- Token-authenticated JSON API mounted at `/informant/api/v1/`.
120
-
121
- ```text
122
- GET /informant/api/v1/errors # List error groups (paginated, filterable)
123
- GET /informant/api/v1/errors/:id # Show with recent occurrences
124
- PATCH /informant/api/v1/errors/:id # Update status or notes
125
- DELETE /informant/api/v1/errors/:id # Delete group and occurrences
126
- PATCH /informant/api/v1/errors/:id/fix_pending # Mark fix pending
127
- PATCH /informant/api/v1/errors/:id/duplicate # Mark as duplicate
128
- GET /informant/api/v1/occurrences # List occurrences
129
- GET /informant/api/v1/status # Error monitoring summary
130
- ```
131
-
132
- Authenticate with `Authorization: Bearer <token>`.
133
-
134
145
  ## MCP Server
135
146
 
136
147
  The bundled `informant-mcp` executable connects Claude Code to your error data via [Model Context Protocol (MCP)](https://modelcontextprotocol.io).
137
148
 
138
- The MCP server requires the `mcp` gem, which is not a runtime dependency. Add it to your Gemfile:
139
-
140
- ```ruby
141
- gem "mcp", ">= 0.7", "< 2"
142
- ```
143
-
144
149
  ### Setup
145
150
 
146
- Add to your Claude Code MCP config:
151
+ The install generator creates `.mcp.json` for you. To set it up manually:
147
152
 
148
153
  ```json
149
154
  {
150
155
  "mcpServers": {
151
156
  "informant": {
152
- "command": "informant-mcp",
153
- "env": {
154
- "INFORMANT_PRODUCTION_URL": "https://myapp.com",
155
- "INFORMANT_PRODUCTION_TOKEN": "your-api-token"
156
- }
157
+ "command": "informant-mcp"
157
158
  }
158
159
  }
159
160
  }
160
161
  ```
161
162
 
162
- Or create `~/.config/informant-mcp.yml` for multi-environment setups:
163
+ 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 -- no need to put secrets in `.mcp.json`.
164
+
165
+ For multi-environment setups, create `~/.config/informant-mcp.yml`:
163
166
 
164
167
  ```yaml
165
168
  environments:
@@ -188,6 +191,23 @@ environments:
188
191
  | `get_informant_status` | Summary with counts and top errors |
189
192
  | `list_occurrences` | List occurrences with filtering |
190
193
 
194
+ ### Local Development
195
+
196
+ The MCP server enforces HTTPS by default. When pointing at a local HTTP URL (e.g., `http://localhost:3000`), pass `--allow-insecure`:
197
+
198
+ ```json
199
+ {
200
+ "mcpServers": {
201
+ "informant": {
202
+ "command": "informant-mcp",
203
+ "args": ["--allow-insecure"]
204
+ }
205
+ }
206
+ }
207
+ ```
208
+
209
+ This is only needed for local development/testing. Production setups over HTTPS don't need it.
210
+
191
211
  ## Claude Code Skill
192
212
 
193
213
  Use `/informant` in Claude Code to triage and fix errors interactively. The skill:
@@ -233,12 +253,12 @@ The notification prompt includes: error class, error message (truncated), severi
233
253
  Development Machine Remote Servers
234
254
  +-----------------------+ +-----------------------+
235
255
  | Claude Code | | Production |
236
- | | | | /informant/api/v1 |
256
+ | | | | /informant |
237
257
  | | stdio | +-----------------------+
238
258
  | v | HTTPS+Token
239
259
  | MCP Server | -----------> +-----------------------+
240
260
  | (exe/informant-mcp) | | Staging |
241
- | | | /informant/api/v1 |
261
+ | | | /informant |
242
262
  +-----------------------+ +-----------------------+
243
263
 
244
264
  Inside the Rails app:
@@ -288,7 +308,7 @@ bin/rails informant:stats # Show error monitoring statistics
288
308
  bin/rails informant:purge # Purge resolved errors older than retention_days
289
309
  ```
290
310
 
291
- ## Data & Privacy
311
+ ## Data and Privacy
292
312
 
293
313
  Each occurrence stores the following PII:
294
314
 
@@ -314,18 +334,18 @@ This replaces email values with `[FILTERED]` in occurrence data. IP addresses ca
314
334
 
315
335
  ## Security
316
336
 
317
- - API requires bearer token authentication (`secure_compare`)
337
+ - MCP server requires token authentication (`secure_compare`)
318
338
  - All stored context is filtered through `ActiveSupport::ParameterFilter`
319
339
  - MCP server enforces HTTPS by default
320
340
  - Security headers: `Cache-Control: no-store`, `X-Content-Type-Options: nosniff`
321
341
  - Error capture never breaks the host application
322
342
  - Webhook payloads strip PII by default
323
- - **Rate limiting** -- the API does not include built-in rate limiting. Add rate limiting on the `/informant/api/` prefix in production, for example with [Rack::Attack](https://github.com/rack/rack-attack):
343
+ - **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](https://github.com/rack/rack-attack):
324
344
 
325
345
  ```ruby
326
346
  # config/initializers/rack_attack.rb
327
- Rack::Attack.throttle("informant/api", limit: 60, period: 1.minute) do |req|
328
- req.ip if req.path.start_with?("/informant/api/")
347
+ Rack::Attack.throttle("informant", limit: 60, period: 1.minute) do |req|
348
+ req.ip if req.path.start_with?("/informant/")
329
349
  end
330
350
  ```
331
351
 
data/exe/informant-mcp CHANGED
@@ -3,11 +3,7 @@
3
3
 
4
4
  $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
5
5
 
6
- begin
7
- require "mcp"
8
- rescue LoadError
9
- abort 'The "mcp" gem is required to run the Informant MCP server. Add gem "mcp", ">= 0.7", "< 2" to your Gemfile and run bundle install.'
10
- end
6
+ require "mcp"
11
7
 
12
8
  require "optparse"
13
9
  require "rails_informant/mcp"
@@ -1,3 +1,4 @@
1
+ require "json"
1
2
  require "rails/generators"
2
3
  require "rails/generators/active_record"
3
4
 
@@ -20,5 +21,53 @@ module RailsInformant
20
21
  def mount_engine
21
22
  route "mount RailsInformant::Engine => '/informant'"
22
23
  end
24
+
25
+ def create_or_update_mcp_json
26
+ mcp_path = File.join(destination_root, ".mcp.json")
27
+ informant_entry = { "command" => "informant-mcp" }
28
+
29
+ if File.exist?(mcp_path)
30
+ existing = JSON.parse(File.read(mcp_path))
31
+ existing["mcpServers"] ||= {}
32
+ existing["mcpServers"]["informant"] = informant_entry
33
+ create_file ".mcp.json", JSON.pretty_generate(existing) + "\n", force: true
34
+ else
35
+ create_file ".mcp.json", JSON.pretty_generate(
36
+ "mcpServers" => { "informant" => informant_entry }
37
+ ) + "\n"
38
+ end
39
+ end
40
+
41
+ def print_next_steps
42
+ say ""
43
+ say "Rails Informant installed!", :green
44
+ say ""
45
+ say "Next steps:", :yellow
46
+ say " 1. Run migrations:"
47
+ say " bin/rails db:migrate"
48
+ say ""
49
+ say " 2. Set a token (required for MCP server access):"
50
+ say " bin/rails credentials:edit"
51
+ say " # Add: rails_informant:"
52
+ say " # api_token: #{SecureRandom.hex 32}"
53
+ say ""
54
+ say " 3. Configure env vars for the MCP server (e.g., via .envrc + direnv):"
55
+ say " export INFORMANT_PRODUCTION_URL=https://your-app.com"
56
+ say " export INFORMANT_PRODUCTION_TOKEN=your-api-token"
57
+ say ""
58
+ say " The token must match the api_token in your Rails credentials."
59
+ say " Add .envrc to .gitignore — it contains secrets."
60
+ say ""
61
+ say " 4. Optional — install the Claude Code skill:"
62
+ say " bin/rails generate rails_informant:skill"
63
+ say ""
64
+ end
65
+
66
+ private
67
+
68
+ def json_column_type
69
+ adapter = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).first&.adapter.to_s
70
+ adapter.match?(/postgres/i) ? "jsonb" : "json"
71
+ end
23
72
  end
24
73
  end
@@ -9,7 +9,7 @@ allowed-tools:
9
9
  - Glob
10
10
  - Edit
11
11
  - Write
12
- - Bash(bin/test *)
12
+ - Bash(bin/rails test *)
13
13
  - Bash(bundle exec *)
14
14
  ---
15
15
 
@@ -38,13 +38,13 @@ class CreateInformantTables < ActiveRecord::Migration[8.1]
38
38
  create_table :informant_occurrences do |t|
39
39
  t.references :error_group, null: false,
40
40
  foreign_key: { to_table: :informant_error_groups }
41
- t.jsonb :backtrace
42
- t.jsonb :exception_chain
43
- t.jsonb :request_context
44
- t.jsonb :user_context
45
- t.jsonb :custom_context
46
- t.jsonb :environment_context
47
- t.jsonb :breadcrumbs
41
+ t.<%= json_column_type %> :backtrace
42
+ t.<%= json_column_type %> :exception_chain
43
+ t.<%= json_column_type %> :request_context
44
+ t.<%= json_column_type %> :user_context
45
+ t.<%= json_column_type %> :custom_context
46
+ t.<%= json_column_type %> :environment_context
47
+ t.<%= json_column_type %> :breadcrumbs
48
48
  t.string :git_sha
49
49
  t.timestamps
50
50
 
@@ -15,7 +15,7 @@ RailsInformant.configure do |config|
15
15
  # Or override what is captured by setting user context explicitly:
16
16
  # RailsInformant::Current.user_context = { id: current_user.id }
17
17
 
18
- # API token for the JSON API (required for MCP server access)
18
+ # Authentication token (required for MCP server access)
19
19
  config.api_token = Rails.application.credentials.dig(:rails_informant, :api_token)
20
20
 
21
21
  # Slack webhook URL for error notifications
@@ -68,19 +68,25 @@ module RailsInformant
68
68
 
69
69
  token = RailsInformant.api_token
70
70
 
71
- if token.nil?
72
- raise <<~MSG.squish
71
+ message = if token.nil?
72
+ <<~MSG.squish
73
73
  RailsInformant: api_token must be configured when capture_errors is enabled.
74
74
  Set it in your initializer: config.api_token = "your-secret-token"
75
75
  MSG
76
- end
77
-
78
- if token.length < MINIMUM_TOKEN_LENGTH
79
- raise <<~MSG.squish
76
+ elsif token.length < MINIMUM_TOKEN_LENGTH
77
+ <<~MSG.squish
80
78
  RailsInformant: api_token must be at least #{MINIMUM_TOKEN_LENGTH} characters.
81
79
  Use SecureRandom.hex(32) or Rails credentials to generate a secure token.
82
80
  MSG
83
81
  end
82
+
83
+ return unless message
84
+
85
+ if RailsInformant.server_mode?
86
+ raise message
87
+ else
88
+ Rails.logger&.warn message
89
+ end
84
90
  end
85
91
  end
86
92
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-informant
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel López Prat
@@ -65,6 +65,26 @@ dependencies:
65
65
  - - ">="
66
66
  - !ruby/object:Gem::Version
67
67
  version: '8.1'
68
+ - !ruby/object:Gem::Dependency
69
+ name: mcp
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0.7'
75
+ - - "<"
76
+ - !ruby/object:Gem::Version
77
+ version: '2'
78
+ type: :runtime
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0.7'
85
+ - - "<"
86
+ - !ruby/object:Gem::Version
87
+ version: '2'
68
88
  - !ruby/object:Gem::Dependency
69
89
  name: railties
70
90
  requirement: !ruby/object:Gem::Requirement