rails-ai-bridge 4.0.0 → 4.2.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.
- checksums.yaml +4 -4
- data/.mutant.yml +26 -0
- data/.reek.yml +4 -0
- data/.rubocop.yml +9 -0
- data/AGENTS.md +5 -5
- data/CHANGELOG.md +42 -1
- data/CLAUDE.md +5 -5
- data/CONTRIBUTING.md +2 -2
- data/GEMINI.md +1 -1
- data/README.md +14 -9
- data/Rakefile +2 -0
- data/SECURITY.md +4 -4
- data/docs/02-port-registry-resolution.md +152 -0
- data/docs/devin-setup.md +2 -2
- data/docs/mcp-security.md +53 -0
- data/docs/port-registry-resolution.md +1 -1
- data/docs/registry-resolution.md +5 -4
- data/docs/skill-registry-guide.md +77 -9
- data/lib/rails_ai_bridge/config/registry.rb +52 -4
- data/lib/rails_ai_bridge/context_provider.rb +1 -1
- data/lib/rails_ai_bridge/doctor/checkers/registry_checker.rb +135 -0
- data/lib/rails_ai_bridge/doctor.rb +2 -1
- data/lib/rails_ai_bridge/engine.rb +1 -0
- data/lib/rails_ai_bridge/instrumentation.rb +30 -1
- data/lib/rails_ai_bridge/introspectors/action_mailbox_introspector.rb +6 -0
- data/lib/rails_ai_bridge/introspectors/asset_pipeline_introspector.rb +7 -0
- data/lib/rails_ai_bridge/introspectors/database_stats_introspector.rb +8 -0
- data/lib/rails_ai_bridge/introspectors/devops_introspector.rb +7 -0
- data/lib/rails_ai_bridge/introspectors/i18n_introspector.rb +7 -0
- data/lib/rails_ai_bridge/introspectors/rake_task_introspector.rb +7 -0
- data/lib/rails_ai_bridge/introspectors/test_introspector.rb +8 -0
- data/lib/rails_ai_bridge/registry/context_provider_definition.rb +42 -0
- data/lib/rails_ai_bridge/registry/context_tool_spec.rb +53 -0
- data/lib/rails_ai_bridge/registry/pack_resolver.rb +101 -6
- data/lib/rails_ai_bridge/registry/rake_presenter.rb +32 -0
- data/lib/rails_ai_bridge/registry/registry_manifest.rb +137 -2
- data/lib/rails_ai_bridge/registry/skill_source_resolver.rb +69 -7
- data/lib/rails_ai_bridge/registry.rb +63 -1
- data/lib/rails_ai_bridge/resources.rb +87 -3
- data/lib/rails_ai_bridge/serializers/README.md +0 -1
- data/lib/rails_ai_bridge/serializers/json_serializer.rb +4 -0
- data/lib/rails_ai_bridge/serializers/providers/base_provider_serializer.rb +1 -1
- data/lib/rails_ai_bridge/server.rb +4 -1
- data/lib/rails_ai_bridge/tasks/rails_ai_bridge.rake +35 -4
- data/lib/rails_ai_bridge/tools/list_context_providers.rb +132 -0
- data/lib/rails_ai_bridge/tools/list_registry.rb +3 -3
- data/lib/rails_ai_bridge/tools/search_code.rb +31 -0
- data/lib/rails_ai_bridge/tools/usage_formatter.rb +72 -0
- data/lib/rails_ai_bridge/tools/use_agent.rb +54 -0
- data/lib/rails_ai_bridge/tools/use_skill.rb +57 -0
- data/lib/rails_ai_bridge/version.rb +1 -1
- data/lib/tasks/perf.rake +27 -0
- metadata +11 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 24c9dd6461240b9d6864b35c171c465635f9fdacc3c76195915f43b995a27f18
|
|
4
|
+
data.tar.gz: e97756c625478c854f78f661393d26043ba36b07b8941bed7a2a2c09b3a62a4b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f453227c44348be19356eecb728dac22328abcb20a99cf0430f441c68557346d93576527716764cae4f68f404fcab524dd44c4454b27a4f9078955632be8937f
|
|
7
|
+
data.tar.gz: 10e975098f252c37f37108e04d0e370b03f469829f9e2520334b180e83a8844a8276b9f4ed60ae650e0f9bd47dd7361b42885dcbdf87880e37d56c04152720fa
|
data/.mutant.yml
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Mutant configuration for rails-ai-bridge critical paths.
|
|
3
|
+
#
|
|
4
|
+
# Run with: BUNDLE_GEMFILE=Gemfile-mutation bundle exec mutant run [subject expressions]
|
|
5
|
+
# Example: BUNDLE_GEMFILE=Gemfile-mutation bundle exec mutant run RailsAiBridge::Registry::FrontmatterParser
|
|
6
|
+
#
|
|
7
|
+
# The open-source usage mode is used because rails-ai-bridge is an
|
|
8
|
+
# open-source project. A commercial license is required for private
|
|
9
|
+
# repositories or advanced features (full operator set, isolation).
|
|
10
|
+
#
|
|
11
|
+
# Target: >80% mutation coverage on registry/tools.
|
|
12
|
+
#
|
|
13
|
+
# Subject expressions are passed on the CLI to target critical paths:
|
|
14
|
+
# RailsAiBridge::Registry — registry resolution
|
|
15
|
+
# RailsAiBridge::Tools — MCP tools
|
|
16
|
+
# RailsAiBridge::Serializers — output serializers
|
|
17
|
+
usage: opensource
|
|
18
|
+
integration: rspec
|
|
19
|
+
includes:
|
|
20
|
+
- lib
|
|
21
|
+
- spec
|
|
22
|
+
requires:
|
|
23
|
+
- spec_helper
|
|
24
|
+
fail_fast: false
|
|
25
|
+
mutation:
|
|
26
|
+
timeout: 5.0
|
data/.reek.yml
CHANGED
|
@@ -87,6 +87,7 @@ detectors:
|
|
|
87
87
|
- RailsAiBridge::Serializers::RegenerationFooter
|
|
88
88
|
- RailsAiBridge::Introspectors::SemanticIntrospector
|
|
89
89
|
- RailsAiBridge::RubydexAdapter::IncrementalIndexer
|
|
90
|
+
- RailsAiBridge::Registry::RegistryManifest
|
|
90
91
|
DuplicateMethodCall:
|
|
91
92
|
exclude:
|
|
92
93
|
- RailsAiBridge::Config::Introspection#excluded_table?
|
|
@@ -416,6 +417,7 @@ detectors:
|
|
|
416
417
|
- RailsAiBridge::Tools::BaseTool
|
|
417
418
|
- RailsAiBridge::Watcher::BridgeRegenerator
|
|
418
419
|
- RailsAiBridge::Fingerprinter::CachedSnapshot
|
|
420
|
+
- RailsAiBridge::Registry::RegistryManifest
|
|
419
421
|
NestedIterators:
|
|
420
422
|
exclude:
|
|
421
423
|
- RailsAiBridge::Fingerprinter#snapshot
|
|
@@ -495,6 +497,7 @@ detectors:
|
|
|
495
497
|
- RailsAiBridge::Introspectors::TestIntrospector
|
|
496
498
|
- RailsAiBridge::Introspectors::ViewIntrospector
|
|
497
499
|
- RailsAiBridge::RubydexAdapter
|
|
500
|
+
- RailsAiBridge::Registry::RegistryManifest
|
|
498
501
|
- RailsAiBridge::Tools::GetStimulus::ResponseFormatter
|
|
499
502
|
- RailsAiBridge::Tools::SearchSemantic
|
|
500
503
|
TooManyInstanceVariables:
|
|
@@ -502,6 +505,7 @@ detectors:
|
|
|
502
505
|
- RailsAiBridge::Config::Introspection
|
|
503
506
|
- RailsAiBridge::Config::Mcp
|
|
504
507
|
- RailsAiBridge::Config::Output
|
|
508
|
+
- RailsAiBridge::Config::Registry
|
|
505
509
|
- RailsAiBridge::Config::Rubydex
|
|
506
510
|
- RailsAiBridge::Config::Server
|
|
507
511
|
- RailsAiBridge::Configuration
|
data/.rubocop.yml
CHANGED
|
@@ -9,6 +9,7 @@ AllCops:
|
|
|
9
9
|
- "vendor/**/*"
|
|
10
10
|
- "spec/internal/**/*"
|
|
11
11
|
- "gemfiles/**/*"
|
|
12
|
+
- ".mutant.yml"
|
|
12
13
|
Naming/FileName:
|
|
13
14
|
Exclude:
|
|
14
15
|
- "lib/rails-ai-bridge.rb"
|
|
@@ -39,6 +40,8 @@ Layout/LineLength:
|
|
|
39
40
|
Max: 180
|
|
40
41
|
Metrics/ModuleLength:
|
|
41
42
|
Max: 240
|
|
43
|
+
Exclude:
|
|
44
|
+
- "lib/rails_ai_bridge/resources.rb"
|
|
42
45
|
Metrics/MethodLength:
|
|
43
46
|
Max: 70
|
|
44
47
|
RSpec/NestedGroups:
|
|
@@ -57,6 +60,12 @@ RSpec/MultipleDescribes:
|
|
|
57
60
|
Enabled: false
|
|
58
61
|
RSpec/MessageSpies:
|
|
59
62
|
Enabled: false
|
|
63
|
+
RSpec/Output:
|
|
64
|
+
Exclude:
|
|
65
|
+
- "spec/support/perf_baseline.rb"
|
|
66
|
+
Rails/RakeEnvironment:
|
|
67
|
+
Exclude:
|
|
68
|
+
- "lib/tasks/*.rake"
|
|
60
69
|
Metrics/PerceivedComplexity:
|
|
61
70
|
Max: 35
|
|
62
71
|
Exclude:
|
data/AGENTS.md
CHANGED
|
@@ -8,9 +8,9 @@ structure to AI assistants via the Model Context Protocol (MCP).
|
|
|
8
8
|
- `lib/rails_ai_bridge.rb` — Main entry point, public API (Zeitwerk autoloaded)
|
|
9
9
|
- `lib/rails_ai_bridge/configuration.rb` — User-facing config with presets (:standard, :full)
|
|
10
10
|
- `lib/rails_ai_bridge/introspector.rb` — Orchestrates sub-introspectors
|
|
11
|
-
- `lib/rails_ai_bridge/introspectors/` — Built-in introspector classes; `:standard` preset runs **9**, `:full` runs **
|
|
12
|
-
- `lib/rails_ai_bridge/tools/` —
|
|
13
|
-
- `lib/rails_ai_bridge/serializers/` — Output formatters (
|
|
11
|
+
- `lib/rails_ai_bridge/introspectors/` — Built-in introspector classes; `:standard` preset runs **9**, `:full` runs **27** (see `Configuration::PRESETS`). Registry: `Introspector::BUILTIN_INTROSPECTORS` (includes opt-in symbols such as `database_stats`, `non_ar_models` not listed in those presets).
|
|
12
|
+
- `lib/rails_ai_bridge/tools/` — 17 built-in MCP tools using the official mcp SDK (hosts can add more via `additional_tools`)
|
|
13
|
+
- `lib/rails_ai_bridge/serializers/` — Output formatters (claude, claude_rules, codex, cursor_rules, devin, devin_rules, copilot, copilot_instructions, gemini, rules, markdown, JSON)
|
|
14
14
|
- `lib/rails_ai_bridge/resources.rb` — MCP resources (static data AI clients read directly)
|
|
15
15
|
- `lib/rails_ai_bridge/server.rb` — MCP server configuration (stdio + HTTP transports)
|
|
16
16
|
- `lib/rails_ai_bridge/middleware.rb` — Rack middleware for auto-mounting MCP HTTP endpoint
|
|
@@ -30,10 +30,10 @@ structure to AI assistants via the Model Context Protocol (MCP).
|
|
|
30
30
|
6. **Diff-aware** — context regeneration skips unchanged files
|
|
31
31
|
7. **Per-assistant serializers** — each AI tool gets tailored output format
|
|
32
32
|
8. **Zeitwerk autoloading** — files loaded on-demand, not all upfront
|
|
33
|
-
9. **Introspector presets** — `:standard` (9 core) default, `:full` (
|
|
33
|
+
9. **Introspector presets** — `:standard` (9 core) default, `:full` (27 introspectors; optional extras such as `database_stats`, `non_ar_models`) for power users
|
|
34
34
|
10. **MCP auto-discovery** — `.mcp.json` generated by install generator
|
|
35
35
|
11. **Compact by default** — context files ≤150 lines, MCP tools use `detail` parameter (summary/standard/full)
|
|
36
|
-
12. **Per-tool split rules** — `.
|
|
36
|
+
12. **Per-tool split rules** — `.codex/`, `.cursor/rules/`, `.devin/rules/`, `.github/instructions/`
|
|
37
37
|
|
|
38
38
|
## Testing
|
|
39
39
|
|
data/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,48 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [4.2.0] - 2026-08-13
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`rails_list_context_providers` MCP tool and dynamic resources** (#147) — new tool reads the registry manifest's `context_providers` section and returns formatted markdown with provider name, type, endpoint, optional flag, and tool specs. Context providers are also registered as dynamic MCP resources (URI template `rails://context-providers/{name}`), bringing the tool count from 16 to 17.
|
|
13
|
+
- **Registry health checker for Doctor** (#145) — new `Doctor::Checkers::RegistryChecker` validates manifest existence, JSON parsing, `RegistryManifest.validate!`, resolver construction, and lockfile presence (when configured). Returns pass/warn/fail with actionable fix hints. Doctor check count: 17.
|
|
14
|
+
- **Request-level resolver memoization** (#159) — `Registry.with_request_resolver` wraps each tool invocation in a thread-local request scope so `build_resolver` is called once per request instead of once per tool. Nil results are never memoized; thread-local storage is always cleaned up (even on error).
|
|
15
|
+
- **Dependabot configuration** (#153) — `.github/dependabot.yml` with grouped updates for bundler, github-actions, and gomod ecosystems, plus automatic PR labeling.
|
|
16
|
+
- **Rails 8.1 in release workflow matrix** (#164) — `.github/workflows/release.yml` now includes Rails 8.1 alongside 7.1, 7.2, and 8.0.
|
|
17
|
+
- **Skunk score threshold CI job** (#157) — advisory CI job runs `skunk lib/` after tests generate coverage data, fails if the SkunkScore average exceeds 30. Threshold to be ratcheted down over time.
|
|
18
|
+
- **Performance regression baseline** (#160) — `spec/support/perf_baseline.json` with three metrics (introspection, context generation, MCP tool response). `rake perf:compare` fails on >20% regression. CI uploads the baseline as a 30-day artifact.
|
|
19
|
+
- **Mutation testing for critical paths** (#163) — `mutant-rspec` via separate `Gemfile-mutation` (eval'd from the main Gemfile) to avoid breaking Ruby 3.2 CI resolution. Advisory CI job targets `Registry::Resolver`, `FrontmatterParser`, `RegistryManifest`, `PackResolver`, `SkillSourceResolver`, and key tools/serializers. Initial coverage: 94.39% on `FrontmatterParser`.
|
|
20
|
+
- **YARD documentation for 9 uncovered files** (#156) — `@param`/`@return` tags added to `search_code.rb`, all 6 introspectors, `json_serializer.rb`, and `usage_formatter.rb`. `yard stats` now 100% for `lib/`.
|
|
21
|
+
- **Dedicated `UsageFormatter` spec** (#151) — comprehensive spec coverage for `Tools::UsageFormatter` formatting logic.
|
|
22
|
+
- **Registry server specs and integration tests** (#149, #150) — `server_spec.rb` extended with registry tool assertions; new `mcp/registry_integration_spec.rb` covers `rails_list_registry`, `rails_resolve_skill`, and `rails_use_skill` end-to-end through the MCP server.
|
|
23
|
+
- **Documentation parity guard spec** — `spec/lib/rails_ai_bridge/doc_parity_spec.rb` fails when README/AGENTS/CLAUDE tool or introspector counts drift from the actual constants.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **Standardized registry manifest and lockfile paths** (#155) — defaults changed from `config/rails_ai_bridge_registry.json` / `config/rails_ai_bridge_registry.lock` to `config/rails_ai_bridge/registry.json` / `config/rails_ai_bridge/registry.lock`. Backward-compat fallback: when the new path doesn't exist but the legacy path does, the getter returns the legacy path. Custom user paths are never overridden.
|
|
28
|
+
- **Documentation humanization and Windsurf purge** (#144, #148) — README, AGENTS.md, CLAUDE.md, CONTRIBUTING.md, and SECURITY.md updated: tool count 16→17, introspector count 26→27, serializer list humanized (alphabetical with Claude/Codex/Gemini), bogus `.Codex/rules/` path fixed to `.codex/`, Windsurf support claims removed (no serializer will be implemented), Mermaid diagrams added to docs.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- **`Instrumentation::InstrumentedTool` server_context parameter translation** — tools without caching received `server_context:` but expected `_server_context:`, causing `ArgumentError` when caching was disabled. `InstrumentedTool` now detects the tool's parameter name and translates accordingly.
|
|
33
|
+
- **`ContextProvider` cache key fork safety** (#162) — replaced `object_id`-based cache key with a stable digest, preventing stale cache hits after `Process.fork`.
|
|
34
|
+
|
|
35
|
+
## [4.1.0] - 2026-08-11
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- **JWT authentication docs for the HTTP MCP transport** (#129) — `docs/mcp-security.md` now documents the `mcp_jwt_decoder` pattern with a worked `JWT.decode` example, the decoder return-value contract, and a token rotation strategy (short expiry, signing-key overlap windows, revocation guidance).
|
|
40
|
+
- **`rails_use_skill` / `rails_use_agent` MCP tools** (#133) — resolve a skill or agent via `Registry::Resolver` and return it framed for immediate in-context application: an intent header, the deprecation notice when the name was redirected, the full content, and a follow-through footer. Use these to *act on* a skill; `rails_resolve_skill` remains the read-only inspection tool (with `pack=` pinning).
|
|
41
|
+
- **JSON output for the registry rake tasks** (#128) — `rails "ai:skills:list[json]"` (or `FORMAT=json rails ai:skills:list`) prints a stable `{"packs": [...], "skills": [...]}` catalog document for CI and custom tooling. `RakePresenter` gains `skills_json`, `packs_json`, and `catalog_json`; the default table output is unchanged.
|
|
42
|
+
- **Transitive `depends_on` loading for skill packs** (#126) — opt in with `config.registry.auto_load_dependencies = true` (default `false`): `PackResolver` expands declared pack dependencies transitively (fixed-point iteration capped at 10 levels) and warns about circular dependency chains while still loading every pack in the cycle. Dependencies missing from the manifest are left to the existing warning, which now points at the new flag.
|
|
43
|
+
- **Structured logging for registry git operations** (#131) — `SkillSourceResolver` accepts an optional `logger:` (defaults to `Rails.logger`, or a stderr logger outside Rails). Clone, pull, and checkout operations log `key=value` lines: DEBUG before the operation, INFO with `duration_ms` on success, ERROR with the failure message before raising.
|
|
44
|
+
- **Context provider definitions in the registry manifest** (#134) — new `Registry::ContextProviderDefinition` and `Registry::ContextToolSpec` value objects (ported from the Rust runtime) parse an optional `context_providers` section of the registry manifest: provider `type`/`endpoint`, optional flag, and tool lists supporting both simple names and `{ name, field, arguments }` mappings. `RegistryManifest#context_providers` defaults to `{}` — parsing is preparatory and nothing consumes these definitions yet.
|
|
45
|
+
- **Manifest schema validation** (#123) — new `RegistryManifest.validate!` raises a descriptive `RegistryManifest::ValidationError` for the first invalid field (missing/empty pack `source`; wrong types for `version`, `default_stack`, `ref`, `tile`, `depends_on`, `always_loaded`, `priority`; non-object `packs` entries). New `rails ai:registry:validate` rake task validates the configured manifest for CI/pre-commit use and exits non-zero on failure.
|
|
46
|
+
|
|
47
|
+
### Changed (breaking)
|
|
48
|
+
|
|
49
|
+
- **Full SHA-256 digests for skill-pack cache keys** (#122) — `SkillSourceResolver.compute_cache_key` now appends the full 64-character SHA-256 hex digest instead of a 16-character truncation, consistent with the gem's fingerprinting. Cache directories created under the old key format are orphaned; clear `~/.rails-ai-bridge/cache` to reclaim disk space.
|
|
9
50
|
|
|
10
51
|
## [4.0.0] - 2026-08-09
|
|
11
52
|
|
data/CLAUDE.md
CHANGED
|
@@ -9,14 +9,14 @@ structure to AI assistants via the Model Context Protocol (MCP).
|
|
|
9
9
|
- `lib/rails_ai_bridge/configuration.rb` — User-facing config with presets (:standard, :full)
|
|
10
10
|
- `lib/rails_ai_bridge/introspector.rb` — Orchestrates sub-introspectors
|
|
11
11
|
- `lib/rails_ai_bridge/introspectors/` — Built-in introspector classes; `:standard` preset
|
|
12
|
-
runs **9**, `:full` runs **
|
|
12
|
+
runs **9**, `:full` runs **27** (see `Configuration::PRESETS`). Registry:
|
|
13
13
|
`Introspector::BUILTIN_INTROSPECTORS` (includes opt-in symbols such as
|
|
14
14
|
`database_stats`, `non_ar_models` not listed in those presets).
|
|
15
|
-
- `lib/rails_ai_bridge/tools/` —
|
|
15
|
+
- `lib/rails_ai_bridge/tools/` — 17 built-in MCP tools using the official mcp SDK
|
|
16
16
|
(hosts can add more via `additional_tools`)
|
|
17
17
|
- `lib/rails_ai_bridge/serializers/` — Output formatters (claude, claude_rules,
|
|
18
|
-
cursor_rules, devin, devin_rules, copilot, copilot_instructions,
|
|
19
|
-
markdown, JSON)
|
|
18
|
+
codex, cursor_rules, devin, devin_rules, copilot, copilot_instructions,
|
|
19
|
+
gemini, rules, markdown, JSON)
|
|
20
20
|
- `lib/rails_ai_bridge/resources.rb` — MCP resources (static data AI clients read directly)
|
|
21
21
|
- `lib/rails_ai_bridge/server.rb` — MCP server configuration (stdio + HTTP transports)
|
|
22
22
|
- `lib/rails_ai_bridge/middleware.rb` — Rack middleware for auto-mounting MCP HTTP endpoint
|
|
@@ -36,7 +36,7 @@ structure to AI assistants via the Model Context Protocol (MCP).
|
|
|
36
36
|
6. **Diff-aware** — context regeneration skips unchanged files
|
|
37
37
|
7. **Per-assistant serializers** — each AI tool gets tailored output format
|
|
38
38
|
8. **Zeitwerk autoloading** — files loaded on-demand, not all upfront
|
|
39
|
-
9. **Introspector presets** — `:standard` (9 core) default, `:full` (
|
|
39
|
+
9. **Introspector presets** — `:standard` (9 core) default, `:full` (27 introspectors; optional extras such as `database_stats`, `non_ar_models`) for power users
|
|
40
40
|
10. **MCP auto-discovery** — `.mcp.json` generated by install generator
|
|
41
41
|
11. **Compact by default** — context files ≤150 lines, MCP tools use `detail` parameter (summary/standard/full)
|
|
42
42
|
12. **Per-tool split rules** — `.claude/rules/`, `.cursor/rules/`, `.devin/rules/`, `.github/instructions/`
|
data/CONTRIBUTING.md
CHANGED
|
@@ -19,13 +19,13 @@ The test suite uses [Combustion](https://github.com/pat/combustion) to boot a mi
|
|
|
19
19
|
```
|
|
20
20
|
lib/rails_ai_bridge/
|
|
21
21
|
├── introspectors/ # Built-in introspectors (schema, models, non_ar_models, routes, …)
|
|
22
|
-
├── tools/ #
|
|
22
|
+
├── tools/ # 17 built-in MCP tools (detail levels, pagination, extensible)
|
|
23
23
|
├── rubydex_adapter.rb # Rubydex API wrapper (singleton + query interface + stats)
|
|
24
24
|
├── rubydex_adapter/ # Extracted collaborators (one concern each)
|
|
25
25
|
│ ├── serializer.rb # Hash serialization (declaration_to_hash, definition_to_hash, …)
|
|
26
26
|
│ ├── indexer.rb # Graph building + source file scanning
|
|
27
27
|
│ └── method_counter.rb # Flat method counting pipeline (no nested conditionals)
|
|
28
|
-
├── serializers/ # Per-assistant formatters (claude, cursor,
|
|
28
|
+
├── serializers/ # Per-assistant formatters (claude, codex, cursor, devin, copilot, gemini, JSON)
|
|
29
29
|
├── server.rb # MCP server setup (stdio + HTTP)
|
|
30
30
|
├── engine.rb # Rails Engine for auto-integration
|
|
31
31
|
└── configuration.rb # User-facing config (presets, context_mode, limits)
|
data/GEMINI.md
CHANGED
|
@@ -9,7 +9,7 @@ structure to AI assistants via the Model Context Protocol (MCP).
|
|
|
9
9
|
- `lib/rails_ai_bridge/configuration.rb` — User-facing config with presets (:standard, :full)
|
|
10
10
|
- `lib/rails_ai_bridge/introspector.rb` — Orchestrates sub-introspectors
|
|
11
11
|
- `lib/rails_ai_bridge/introspectors/` — Built-in introspector classes; `:standard` preset runs **9**, `:full` runs **26** (see `Configuration::PRESETS`). Registry: `Introspector::BUILTIN_INTROSPECTORS` (includes opt-in symbols such as `database_stats`, `non_ar_models` not listed in those presets).
|
|
12
|
-
- `lib/rails_ai_bridge/tools/` —
|
|
12
|
+
- `lib/rails_ai_bridge/tools/` — 16 built-in MCP tools using the official mcp SDK (hosts can add more via `additional_tools`)
|
|
13
13
|
- `lib/rails_ai_bridge/serializers/` — Output formatters (claude, claude_rules, cursor_rules, windsurf, windsurf_rules, copilot, copilot_instructions, rules, markdown, JSON, gemini)
|
|
14
14
|
- `lib/rails_ai_bridge/resources.rb` — MCP resources (static data AI clients read directly)
|
|
15
15
|
- `lib/rails_ai_bridge/server.rb` — MCP server configuration (stdio + HTTP transports)
|
data/README.md
CHANGED
|
@@ -83,7 +83,7 @@ This creates two complementary layers:
|
|
|
83
83
|
| Static files | Give the assistant passive project orientation at session start | Fewer cold starts and fewer generic assumptions |
|
|
84
84
|
| MCP tools | Return exact live details when requested | Less context bloat and fewer schema/route hallucinations |
|
|
85
85
|
|
|
86
|
-
Compact files are ordered for usefulness: primary domain models, busy endpoints, recently migrated tables, and optional hot-table
|
|
86
|
+
Compact files are ordered for usefulness: primary domain models, busy endpoints, recently migrated tables, and optional hot-table hints appear before less important details.
|
|
87
87
|
|
|
88
88
|
## Safety model
|
|
89
89
|
|
|
@@ -159,7 +159,7 @@ Optional: `gem install rails-ai-bridge` installs the gem into your Ruby environm
|
|
|
159
159
|
| Zero config | Yes — Railtie + install generator | No — per-project `projects.yml` | No |
|
|
160
160
|
| Token optimization | Yes — compact files + `detail:"summary"` workflow | Varies | No |
|
|
161
161
|
| Codex-oriented repo files | Yes — `AGENTS.md`, `.codex/README.md` | No | DIY |
|
|
162
|
-
| Live MCP tools | Yes —
|
|
162
|
+
| Live MCP tools | Yes — 17 read-only `rails_*` tools (extensible) | Yes | No |
|
|
163
163
|
| Auto-introspection | Yes — up to **27** domains (`:full`) | No — server points at projects you configure | DIY |
|
|
164
164
|
|
|
165
165
|
*Comparison reflects typical documented setups; verify against each project before treating any row as absolute.*
|
|
@@ -251,7 +251,7 @@ This keeps context focused and avoids unnecessary token usage while still allowi
|
|
|
251
251
|
|
|
252
252
|
## MCP Tools
|
|
253
253
|
|
|
254
|
-
The gem exposes **
|
|
254
|
+
The gem exposes **17 built-in tools** via MCP that AI clients call on-demand (hosts can append more via `config.additional_tools`):
|
|
255
255
|
|
|
256
256
|
| Tool | What it returns |
|
|
257
257
|
|------|----------------|
|
|
@@ -267,7 +267,11 @@ The gem exposes **13 built-in tools** via MCP that AI clients call on-demand (ho
|
|
|
267
267
|
| `rails_get_view` | View layouts, templates, partials; optional per-file detail under the configured `app/views` path |
|
|
268
268
|
| `rails_search_semantic` | Semantic code search using rubydex — find declarations by name with types, locations, and relationships |
|
|
269
269
|
| `rails_get_stimulus` | Stimulus controllers: targets, values, actions, outlets (requires `:stimulus` introspector) |
|
|
270
|
-
| `rails_list_registry` | Skill pack catalog — list skills, agents, or active packs; requires `config/
|
|
270
|
+
| `rails_list_registry` | Skill pack catalog — list skills, agents, or active packs; requires `config/rails_ai_bridge/registry.json` |
|
|
271
|
+
| `rails_resolve_skill` | Full content of a named skill or agent from the registry (priority ordering + deprecation redirects); optional `pack=` pin and `type=agent` |
|
|
272
|
+
| `rails_use_skill` | Loads a skill framed as an application directive (apply it step by step to the current task) |
|
|
273
|
+
| `rails_use_agent` | Loads an agent/workflow framed as an activation directive (follow it end to end) |
|
|
274
|
+
| `rails_list_context_providers` | Context providers declared in the registry manifest — external services (e.g. MCP servers) the bridge can query for project context; shows type, endpoint, optional flag, and tool specs |
|
|
271
275
|
|
|
272
276
|
All tools are **read-only** — they never modify your application or database.
|
|
273
277
|
|
|
@@ -304,7 +308,7 @@ A safety net (`max_tool_response_chars`, default 120K) truncates oversized respo
|
|
|
304
308
|
|
|
305
309
|
Early project-level trials suggest the biggest improvement is not always dramatic token reduction by itself. In several runs, `rails-ai-bridge` led to faster, more focused responses even when total token usage only dropped modestly.
|
|
306
310
|
|
|
307
|
-
This
|
|
311
|
+
This makes sense: compact assistant-specific files and the summary-first MCP workflow help the model find relevant code faster and start with better context.
|
|
308
312
|
|
|
309
313
|
Observed benefits so far:
|
|
310
314
|
- Less exploratory reading before the assistant reaches the relevant files
|
|
@@ -540,13 +544,14 @@ end
|
|
|
540
544
|
| `parallel_introspection` | `false` | Run introspectors concurrently (requires `concurrent-ruby`, which is already a Rails dependency) |
|
|
541
545
|
| `parallel_pool_size` | `4` | Max threads in the parallel pool; capped at the number of active introspectors so no idle threads are created |
|
|
542
546
|
| `parallel_timeout_seconds` | `10` | Per-introspector future timeout (seconds); timed-out introspectors return `{ error: "timed out after Ns" }` without blocking the others |
|
|
543
|
-
| `registry.registry_manifest_path` | `"config/
|
|
547
|
+
| `registry.registry_manifest_path` | `"config/rails_ai_bridge/registry.json"` | Path to the registry manifest JSON file for skill pack resolution |
|
|
544
548
|
| `registry.skill_cache_dir` | `"~/.rails-ai-bridge/cache"` | Directory for caching git repositories containing skill packs |
|
|
545
549
|
| `registry.skill_packs` | `nil` | Explicit pack names to load, or `nil` for auto-detection based on framework |
|
|
546
550
|
| `registry.local_registry_paths` | `[]` | Local directory paths (must contain `directory.json`) loaded at priority 0 |
|
|
547
551
|
| `registry.resolver_ttl` | `1800` | Seconds to cache the wired resolver in memory; `0` disables caching |
|
|
548
552
|
| `registry.git_pull_ttl` | `86400` | Seconds between `git pull` refreshes per cached pack (24 h default). Set to `0` to pull on every resolver rebuild |
|
|
549
553
|
| `registry.git_timeout` | `30` | Seconds before a git operation (clone, pull, checkout) is forcibly interrupted |
|
|
554
|
+
| `registry.auto_load_dependencies` | `false` | Load declared `depends_on` packs transitively; circular chains warn but still load |
|
|
550
555
|
|
|
551
556
|
Other HTTP MCP knobs live only on the nested object, for example `RailsAiBridge.configuration.mcp.authorize`, `mcp.mode`, `mcp.security_profile`, and `mcp.require_auth_in_production` — see [docs/GUIDE.md](docs/GUIDE.md) and [docs/mcp-security.md](docs/mcp-security.md).
|
|
552
557
|
</details>
|
|
@@ -682,10 +687,10 @@ To customize it in your initializer (`config/initializers/rails_ai_bridge.rb`):
|
|
|
682
687
|
rails-ai-bridge can load **skill packs** — shared collections of agent instructions — from versioned git repositories and surface them through `rails_list_registry` and rake tasks.
|
|
683
688
|
|
|
684
689
|
```ruby
|
|
685
|
-
config.registry.registry_manifest_path = "config/
|
|
690
|
+
config.registry.registry_manifest_path = "config/rails_ai_bridge/registry.json"
|
|
686
691
|
```
|
|
687
692
|
|
|
688
|
-
Quick example manifest (`config/
|
|
693
|
+
Quick example manifest (`config/rails_ai_bridge/registry.json`):
|
|
689
694
|
|
|
690
695
|
```json
|
|
691
696
|
{
|
|
@@ -831,7 +836,7 @@ Bug reports and pull requests: [github.com/igmarin/rails-ai-bridge/issues](https
|
|
|
831
836
|
|
|
832
837
|
## Acknowledgments & Origins
|
|
833
838
|
|
|
834
|
-
This gem ships as **rails-ai-bridge** (Ruby **`RailsAiBridge`**, version **
|
|
839
|
+
This gem ships as **rails-ai-bridge** (Ruby **`RailsAiBridge`**, version **4.2.0**). Earlier iterations of the same codebase were distributed as `rails-ai-context`.
|
|
835
840
|
|
|
836
841
|
RailsMCP evolved from
|
|
837
842
|
[crisnahine/rails-ai-context](https://github.com/crisnahine/rails-ai-context),
|
data/Rakefile
CHANGED
data/SECURITY.md
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
| Version | Supported |
|
|
6
6
|
|---------|--------------------|
|
|
7
|
-
|
|
|
8
|
-
| 3.
|
|
9
|
-
| 3.0.x–3.
|
|
10
|
-
| 2.x | :x: (end of life — upgrade to
|
|
7
|
+
| 4.x | :white_check_mark: |
|
|
8
|
+
| 3.6.x | :white_check_mark: (security fixes best-effort) |
|
|
9
|
+
| 3.0.x–3.5.x | :warning: (best-effort) |
|
|
10
|
+
| 2.x | :x: (end of life — upgrade to 4.x) |
|
|
11
11
|
| 1.x | :x: |
|
|
12
12
|
| < 1.0 | :x: |
|
|
13
13
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Plan 2: Port Registry Resolution from Rust Runtime to rails-ai-bridge
|
|
2
|
+
|
|
3
|
+
**Status:** Complete ✅ — implemented via PRs #40–#45
|
|
4
|
+
**Priority:** After Plan 1
|
|
5
|
+
**Estimated effort:** 4 weeks
|
|
6
|
+
**Depends on:** Plan 1 (agent-mcp-runtime archived)
|
|
7
|
+
|
|
8
|
+
> **Completion note (2026-08-10):** All success criteria shipped. See
|
|
9
|
+
> [`port-registry-resolution.md`](port-registry-resolution.md) for as-built decisions and deltas, and
|
|
10
|
+
> [`registry-resolution.md`](registry-resolution.md) for user-facing documentation. Naming deltas from
|
|
11
|
+
> this plan: rake tasks shipped as `ai:skills:list` / `ai:skills:resolve[pack,name]` (planned:
|
|
12
|
+
> `ai:list_skills` / `ai:resolve_skill`); configuration lives under `config.registry.*`
|
|
13
|
+
> (`Config::Registry`); the MCP surface is the unified `rails_list_registry` tool plus
|
|
14
|
+
> `rails_resolve_skill`; git operations use `Open3` (stdlib) — no `git` gem dependency.
|
|
15
|
+
|
|
16
|
+
## Objective
|
|
17
|
+
|
|
18
|
+
Port the registry resolution logic from `agent-mcp-runtime` (Rust) into `rails-ai-bridge` (Ruby). This enables the bridge to resolve skill packs from git repositories, handle priority-based loading, and support deprecation redirects — all necessary for the future skill compiler feature.
|
|
19
|
+
|
|
20
|
+
## Components to Port
|
|
21
|
+
|
|
22
|
+
### 1. New module: `lib/rails_ai_bridge/registry/`
|
|
23
|
+
|
|
24
|
+
#### `lib/rails_ai_bridge/registry/manifest.rb`
|
|
25
|
+
- Port `RegistryManifest` struct from Rust (version, packs, default_stack, context_providers)
|
|
26
|
+
- Port `PackDefinition` struct (source, tile, always_loaded, depends_on)
|
|
27
|
+
- Port `ContextProviderDefinition` and `ContextToolSpec` (for future context provider integration)
|
|
28
|
+
- Use JSON parsing with `json` gem (already in rails-ai-bridge dependencies)
|
|
29
|
+
|
|
30
|
+
#### `lib/rails_ai_bridge/registry/tile_manifest.rb`
|
|
31
|
+
- Port `TileManifest` struct (name, version, summary, depends_on, skills, agents, deprecated_skills)
|
|
32
|
+
- Port `SkillEntry` and `AgentEntry` structs
|
|
33
|
+
- Port `DeprecatedEntry` struct (moved_to, message, removed_in)
|
|
34
|
+
|
|
35
|
+
#### `lib/rails_ai_bridge/registry/git_source_resolver.rb`
|
|
36
|
+
- Port `SkillSourceResolver` from Rust
|
|
37
|
+
- Implement git clone/pull using `git` gem or shell commands
|
|
38
|
+
- Cache directory: `~/.rails-ai-bridge/cache/` (instead of `~/.agent-mcp-runtime/cache/`)
|
|
39
|
+
- Support local path resolution (sibling directories for development)
|
|
40
|
+
- Implement `GitRunner` trait pattern with a default implementation and test mock
|
|
41
|
+
|
|
42
|
+
#### `lib/rails_ai_bridge/registry/pack_detector.rb`
|
|
43
|
+
- Port `PackDetector` from Rust
|
|
44
|
+
- Parse Gemfile to detect Rails vs Hanami
|
|
45
|
+
- Return array of detected frameworks
|
|
46
|
+
|
|
47
|
+
#### `lib/rails_ai_bridge/registry/pack_resolver.rb`
|
|
48
|
+
- Port `PackResolverService` logic
|
|
49
|
+
- Implement priority-based pack loading:
|
|
50
|
+
- Priority 0: Local registries (--registry flag)
|
|
51
|
+
- Priority 10: Framework packs (rails, hanami)
|
|
52
|
+
- Priority 20: Core pack (always_loaded)
|
|
53
|
+
- Priority 30: Default stack (planning)
|
|
54
|
+
- Handle explicit pack overrides
|
|
55
|
+
- Load tile.json manifests from resolved sources
|
|
56
|
+
- Return `RegistryResolver` instance
|
|
57
|
+
|
|
58
|
+
#### `lib/rails_ai_bridge/registry/resolver.rb`
|
|
59
|
+
- Port `RegistryResolver` from Rust
|
|
60
|
+
- Implement `LoadedPack` struct (name, tile, base_path, priority)
|
|
61
|
+
- Implement `ResolvedSkill` struct (name, pack, path, content)
|
|
62
|
+
- Implement `SkillSummary` struct (name, pack, description)
|
|
63
|
+
- Implement `resolve_skill(name)` with deprecation redirect handling
|
|
64
|
+
- Implement `resolve_agent(name)`
|
|
65
|
+
- Implement `list_skills()` with deduplication by priority
|
|
66
|
+
- Implement `validate_dependencies()` to warn on missing dependencies
|
|
67
|
+
- Sort packs by priority (ascending = higher priority)
|
|
68
|
+
|
|
69
|
+
#### `lib/rails_ai_bridge/registry.rb`
|
|
70
|
+
- Main registry module that requires all submodules
|
|
71
|
+
- Public API entry point
|
|
72
|
+
|
|
73
|
+
### 2. Integration with rails-ai-bridge
|
|
74
|
+
|
|
75
|
+
**Add to `lib/rails_ai_bridge.rb`:**
|
|
76
|
+
- Require the new registry module
|
|
77
|
+
- Add convenience method `RailsAiBridge.resolve_skill(pack_name, skill_name)`
|
|
78
|
+
- Add convenience method `RailsAiBridge.list_skills()`
|
|
79
|
+
|
|
80
|
+
**Create new Rake task: `lib/rails_ai_bridge/tasks/registry.rake`**
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
namespace :rails_ai_bridge do
|
|
84
|
+
desc "List all available skills from configured packs"
|
|
85
|
+
task list_skills: :environment do
|
|
86
|
+
# Use registry resolver to list skills
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
desc "Resolve a specific skill"
|
|
90
|
+
task :resolve_skill, [:pack, :skill] => :environment do |t, args|
|
|
91
|
+
# Resolve and print skill content
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 3. Configuration
|
|
97
|
+
|
|
98
|
+
**Add to `lib/rails_ai_bridge/configuration.rb`:**
|
|
99
|
+
- `registry_manifest_path` — Path to registry.json (default: `config/rails_ai_bridge/registry.json`)
|
|
100
|
+
- `skill_cache_dir` — Path to git cache directory (default: `~/.rails-ai-bridge/cache`)
|
|
101
|
+
- `skill_packs` — Array of explicit pack names to load (optional)
|
|
102
|
+
- `local_registry_paths` — Array of local registry directories (optional)
|
|
103
|
+
|
|
104
|
+
### 4. Tests
|
|
105
|
+
|
|
106
|
+
**Create spec directory: `spec/rails_ai_bridge/registry/`**
|
|
107
|
+
- `manifest_spec.rb`
|
|
108
|
+
- `tile_manifest_spec.rb`
|
|
109
|
+
- `git_source_resolver_spec.rb` (with mock git runner)
|
|
110
|
+
- `pack_detector_spec.rb`
|
|
111
|
+
- `pack_resolver_spec.rb`
|
|
112
|
+
- `resolver_spec.rb`
|
|
113
|
+
|
|
114
|
+
Use the Rust tests in `agent-mcp-runtime/src/registry/pack_resolver.rs` and `resolver.rs` as reference for test cases.
|
|
115
|
+
|
|
116
|
+
### 5. Documentation
|
|
117
|
+
|
|
118
|
+
**Create `docs/registry-resolution.md`** inside rails-ai-bridge:
|
|
119
|
+
- Explain the registry resolution system
|
|
120
|
+
- Document priority rules
|
|
121
|
+
- Provide example registry.json configuration
|
|
122
|
+
- Document configuration options
|
|
123
|
+
|
|
124
|
+
### 6. Dependencies
|
|
125
|
+
|
|
126
|
+
Add to `rails-ai-bridge.gemspec` if not already present:
|
|
127
|
+
- `git` gem (for git operations) — check if already present
|
|
128
|
+
- Ensure `json` gem is available (standard library in Ruby 3+)
|
|
129
|
+
|
|
130
|
+
## Implementation Order
|
|
131
|
+
|
|
132
|
+
1. **Week 1:** Create manifest and tile_manifest structs with tests; create git_source_resolver with mock for tests
|
|
133
|
+
2. **Week 2:** Create pack_detector and pack_resolver with tests
|
|
134
|
+
3. **Week 3:** Create resolver with tests; integrate with rails-ai-bridge main module
|
|
135
|
+
4. **Week 4:** Add Rake tasks, configuration, and documentation
|
|
136
|
+
|
|
137
|
+
## Notes
|
|
138
|
+
|
|
139
|
+
- The existing `ruby-skill-bench` has a `PackResolver` class that does similar work. Review it for patterns but don't directly copy — the bridge version needs git resolution and priority handling that the bench version lacks.
|
|
140
|
+
- The Rust runtime uses async/await. Ruby doesn't have this — use synchronous git operations.
|
|
141
|
+
- Cache directory should be `~/.rails-ai-bridge/cache/` to avoid conflicts with the archived runtime.
|
|
142
|
+
- Framework detection: just check Gemfile for `'rails'` or `'hanami'` gems.
|
|
143
|
+
- Deprecation handling is critical for the skill migration from `rails-agent-skills` to `ruby-core-skills` already in progress.
|
|
144
|
+
|
|
145
|
+
## Success Criteria
|
|
146
|
+
|
|
147
|
+
- [x] All 6 registry modules created with passing specs (323 registry/config/tool examples green, 2026-08-10)
|
|
148
|
+
- [x] `rails ai:skills:list` Rake task works (shipped name; planned as `ai:list_skills`)
|
|
149
|
+
- [x] `rails "ai:skills:resolve[pack,name]"` Rake task works (shipped name; planned as `ai:resolve_skill[pack,name]`)
|
|
150
|
+
- [x] Documentation written (`docs/registry-resolution.md`)
|
|
151
|
+
- [x] Priority-based resolution handles core/rails/hanami/planning correctly
|
|
152
|
+
- [x] Deprecation redirects work (e.g., old skill name → new location)
|
data/docs/devin-setup.md
CHANGED
|
@@ -226,7 +226,7 @@ rails-ai-bridge supports a skill registry — a collection of reusable AI skills
|
|
|
226
226
|
|
|
227
227
|
The `rails_list_registry` tool returns all available skills. The `rails_resolve_skill` tool retrieves the full content of a named skill so Devin can apply it.
|
|
228
228
|
|
|
229
|
-
Skills are configured via `config/
|
|
229
|
+
Skills are configured via `config/rails_ai_bridge/registry.json` in your Rails application. A minimal registry file looks like:
|
|
230
230
|
|
|
231
231
|
```json
|
|
232
232
|
{
|
|
@@ -261,4 +261,4 @@ Devin will call `rails_list_registry`, review the options, and call `rails_resol
|
|
|
261
261
|
| `.devin/rules/rails-mcp-tools.md` | `rails ai:bridge:devin` | Yes |
|
|
262
262
|
| `AGENTS.md` | `rails ai:bridge:devin` | Yes |
|
|
263
263
|
| `.devin/mcp.json` | Manual | Yes |
|
|
264
|
-
| `config/
|
|
264
|
+
| `config/rails_ai_bridge/registry.json` | Manual | Yes |
|
data/docs/mcp-security.md
CHANGED
|
@@ -12,6 +12,59 @@ By default, HTTP MCP allows anonymous access when no auth strategy is configured
|
|
|
12
12
|
|
|
13
13
|
In non-production environments, the standalone HTTP MCP server prints a one-time stderr warning when it starts without authentication to make the default behavior visible.
|
|
14
14
|
|
|
15
|
+
## JWT authentication with short-lived tokens
|
|
16
|
+
|
|
17
|
+
`http_mcp_token` is a static bearer token: rotating it means redistributing a secret to every client. For deployments that need rotation or expiry, configure `config.mcp_jwt_decoder` instead — the highest-priority auth strategy. The gem carries **no JWT dependency**: you supply a lambda that decodes (and verifies) the token.
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
# config/initializers/rails_ai_bridge.rb
|
|
21
|
+
require "jwt" # the host app's own jwt gem
|
|
22
|
+
|
|
23
|
+
RailsAiBridge.configure do |config|
|
|
24
|
+
config.mcp.require_http_auth = true
|
|
25
|
+
|
|
26
|
+
config.mcp_jwt_decoder = ->(token) do
|
|
27
|
+
payload, _header = JWT.decode(
|
|
28
|
+
token,
|
|
29
|
+
Rails.application.credentials.jwt_mcp_secret!,
|
|
30
|
+
true, # verify signature
|
|
31
|
+
algorithm: "HS256",
|
|
32
|
+
verify_expiration: true # reject expired tokens (default when verifying)
|
|
33
|
+
)
|
|
34
|
+
payload # truthy payload => authenticated
|
|
35
|
+
rescue JWT::DecodeError, JWT::ExpiredSignature
|
|
36
|
+
nil # nil => 401 unauthorized
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Decoder contract:
|
|
42
|
+
|
|
43
|
+
- Return a **truthy payload** (Hash recommended) — request is authenticated; the payload is exposed to authorization hooks.
|
|
44
|
+
- Return **`nil`** or **`false`** — request is rejected with `401` (`:unauthorized`).
|
|
45
|
+
- **Raise** — treated as `:decode_error`, also `401`; the exception never propagates.
|
|
46
|
+
|
|
47
|
+
### Token rotation strategy
|
|
48
|
+
|
|
49
|
+
- **Short expiry, frequent re-issue.** Issue MCP tokens with a 5–15 minute `exp` from your existing login/session flow. A leaked token expires on its own; there is nothing to rotate per incident.
|
|
50
|
+
- **Signing-key rotation.** When rotating the HMAC secret (or moving to RS256/JWKS), accept both keys during the overlap window:
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
config.mcp_jwt_decoder = ->(token) do
|
|
54
|
+
keys = [Rails.application.credentials.jwt_mcp_secret!,
|
|
55
|
+
Rails.application.credentials.jwt_mcp_secret_previous!]
|
|
56
|
+
keys.lazy.filter_map do |key|
|
|
57
|
+
JWT.decode(token, key, true, algorithm: "HS256").first
|
|
58
|
+
rescue JWT::DecodeError, JWT::ExpiredSignature
|
|
59
|
+
nil
|
|
60
|
+
end.first
|
|
61
|
+
end
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Retire the previous key once all clients have re-issued tokens.
|
|
65
|
+
- **Immediate revocation.** Stateless JWTs cannot be revoked; if you need it, combine short `exp` with your own denylist check inside the decoder (return `nil` for revoked subjects).
|
|
66
|
+
- **Client capability note.** Some AI hosts cannot refresh tokens and work best with a long-lived static token or `mcp_token_resolver` (which can consult a secret manager per request). Pick the strategy per client: JWT for short-lived machine-to-machine auth, `mcp_token_resolver` when the credential lives in a vault, `http_mcp_token` only for local development.
|
|
67
|
+
|
|
15
68
|
## Rate limiting and proxies
|
|
16
69
|
|
|
17
70
|
Built-in rate limiting keys off the Rack request IP. Behind reverse proxies, configure Rails `trusted_proxies` so `request.ip` reflects the real client; otherwise limits may apply to the wrong address or be bypassed.
|
|
@@ -119,7 +119,7 @@ necessary for the future skill compiler feature.
|
|
|
119
119
|
|
|
120
120
|
**Files:**
|
|
121
121
|
- `lib/rails_ai_bridge/config/registry.rb` — `Config::Registry` sub-object
|
|
122
|
-
- `registry_manifest_path` (default: `config/
|
|
122
|
+
- `registry_manifest_path` (default: `config/rails_ai_bridge/registry.json`)
|
|
123
123
|
- `skill_cache_dir` (default: `~/.rails-ai-bridge/cache`)
|
|
124
124
|
- `skill_packs` (default: `nil` — triggers auto-detection)
|
|
125
125
|
- `local_registry_paths` (default: `[]`)
|
data/docs/registry-resolution.md
CHANGED
|
@@ -16,7 +16,7 @@ When a pack is loaded, its skills and agents appear in `rails_list_registry`, an
|
|
|
16
16
|
|
|
17
17
|
## Quick start
|
|
18
18
|
|
|
19
|
-
**1. Create the registry manifest** at `config/
|
|
19
|
+
**1. Create the registry manifest** at `config/rails_ai_bridge/registry.json`:
|
|
20
20
|
|
|
21
21
|
```json
|
|
22
22
|
{
|
|
@@ -39,7 +39,7 @@ When a pack is loaded, its skills and agents appear in `rails_list_registry`, an
|
|
|
39
39
|
|
|
40
40
|
```ruby
|
|
41
41
|
RailsAiBridge.configure do |config|
|
|
42
|
-
config.registry.registry_manifest_path = "config/
|
|
42
|
+
config.registry.registry_manifest_path = "config/rails_ai_bridge/registry.json"
|
|
43
43
|
end
|
|
44
44
|
```
|
|
45
45
|
|
|
@@ -102,7 +102,7 @@ All options live under `config.registry.*`:
|
|
|
102
102
|
|
|
103
103
|
| Option | Default | Description |
|
|
104
104
|
|--------|---------|-------------|
|
|
105
|
-
| `registry_manifest_path` | `"config/
|
|
105
|
+
| `registry_manifest_path` | `"config/rails_ai_bridge/registry.json"` | Path to the registry manifest JSON |
|
|
106
106
|
| `skill_cache_dir` | `~/.rails-ai-bridge/cache` | Directory for caching cloned git repositories |
|
|
107
107
|
| `skill_packs` | `nil` | Explicit list of pack names to load, or `nil` for auto-detection |
|
|
108
108
|
| `local_registry_paths` | `[]` | Local directory paths containing a `directory.json` (loaded at priority 0) |
|
|
@@ -200,9 +200,10 @@ Once a registry manifest is configured:
|
|
|
200
200
|
|
|
201
201
|
| Task | Description |
|
|
202
202
|
|------|-------------|
|
|
203
|
-
| `rails ai:skills:list` | Print skill catalog to stdout |
|
|
203
|
+
| `rails ai:skills:list` | Print skill catalog to stdout; `[json]` argument (or `FORMAT=json`) prints a `{"packs": [...], "skills": [...]}` document |
|
|
204
204
|
| `rails "ai:skills:resolve[pack,skill_name]"` | Resolve and print a skill's full content |
|
|
205
205
|
| `rails ai:skills:clear_cache` | Remove all cached pack git repositories and invalidate the resolver cache |
|
|
206
|
+
| `rails ai:registry:validate` | Validate the registry manifest schema; exits non-zero on the first invalid field |
|
|
206
207
|
|
|
207
208
|
Examples:
|
|
208
209
|
|