codebase_index 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 (171) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +29 -0
  3. data/CODE_OF_CONDUCT.md +83 -0
  4. data/CONTRIBUTING.md +65 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +481 -0
  7. data/exe/codebase-console-mcp +22 -0
  8. data/exe/codebase-index-mcp +61 -0
  9. data/exe/codebase-index-mcp-http +64 -0
  10. data/exe/codebase-index-mcp-start +58 -0
  11. data/lib/codebase_index/ast/call_site_extractor.rb +106 -0
  12. data/lib/codebase_index/ast/method_extractor.rb +76 -0
  13. data/lib/codebase_index/ast/node.rb +88 -0
  14. data/lib/codebase_index/ast/parser.rb +653 -0
  15. data/lib/codebase_index/ast.rb +6 -0
  16. data/lib/codebase_index/builder.rb +137 -0
  17. data/lib/codebase_index/chunking/chunk.rb +84 -0
  18. data/lib/codebase_index/chunking/semantic_chunker.rb +290 -0
  19. data/lib/codebase_index/console/adapters/cache_adapter.rb +58 -0
  20. data/lib/codebase_index/console/adapters/good_job_adapter.rb +66 -0
  21. data/lib/codebase_index/console/adapters/sidekiq_adapter.rb +66 -0
  22. data/lib/codebase_index/console/adapters/solid_queue_adapter.rb +66 -0
  23. data/lib/codebase_index/console/audit_logger.rb +75 -0
  24. data/lib/codebase_index/console/bridge.rb +170 -0
  25. data/lib/codebase_index/console/confirmation.rb +90 -0
  26. data/lib/codebase_index/console/connection_manager.rb +173 -0
  27. data/lib/codebase_index/console/console_response_renderer.rb +78 -0
  28. data/lib/codebase_index/console/model_validator.rb +81 -0
  29. data/lib/codebase_index/console/safe_context.rb +82 -0
  30. data/lib/codebase_index/console/server.rb +557 -0
  31. data/lib/codebase_index/console/sql_validator.rb +172 -0
  32. data/lib/codebase_index/console/tools/tier1.rb +118 -0
  33. data/lib/codebase_index/console/tools/tier2.rb +117 -0
  34. data/lib/codebase_index/console/tools/tier3.rb +110 -0
  35. data/lib/codebase_index/console/tools/tier4.rb +79 -0
  36. data/lib/codebase_index/coordination/pipeline_lock.rb +109 -0
  37. data/lib/codebase_index/cost_model/embedding_cost.rb +88 -0
  38. data/lib/codebase_index/cost_model/estimator.rb +128 -0
  39. data/lib/codebase_index/cost_model/provider_pricing.rb +67 -0
  40. data/lib/codebase_index/cost_model/storage_cost.rb +52 -0
  41. data/lib/codebase_index/cost_model.rb +22 -0
  42. data/lib/codebase_index/db/migrations/001_create_units.rb +38 -0
  43. data/lib/codebase_index/db/migrations/002_create_edges.rb +35 -0
  44. data/lib/codebase_index/db/migrations/003_create_embeddings.rb +37 -0
  45. data/lib/codebase_index/db/migrations/004_create_snapshots.rb +45 -0
  46. data/lib/codebase_index/db/migrations/005_create_snapshot_units.rb +40 -0
  47. data/lib/codebase_index/db/migrator.rb +71 -0
  48. data/lib/codebase_index/db/schema_version.rb +73 -0
  49. data/lib/codebase_index/dependency_graph.rb +227 -0
  50. data/lib/codebase_index/embedding/indexer.rb +130 -0
  51. data/lib/codebase_index/embedding/openai.rb +105 -0
  52. data/lib/codebase_index/embedding/provider.rb +135 -0
  53. data/lib/codebase_index/embedding/text_preparer.rb +112 -0
  54. data/lib/codebase_index/evaluation/baseline_runner.rb +115 -0
  55. data/lib/codebase_index/evaluation/evaluator.rb +146 -0
  56. data/lib/codebase_index/evaluation/metrics.rb +79 -0
  57. data/lib/codebase_index/evaluation/query_set.rb +148 -0
  58. data/lib/codebase_index/evaluation/report_generator.rb +90 -0
  59. data/lib/codebase_index/extracted_unit.rb +145 -0
  60. data/lib/codebase_index/extractor.rb +956 -0
  61. data/lib/codebase_index/extractors/action_cable_extractor.rb +228 -0
  62. data/lib/codebase_index/extractors/ast_source_extraction.rb +46 -0
  63. data/lib/codebase_index/extractors/behavioral_profile.rb +309 -0
  64. data/lib/codebase_index/extractors/caching_extractor.rb +261 -0
  65. data/lib/codebase_index/extractors/callback_analyzer.rb +232 -0
  66. data/lib/codebase_index/extractors/concern_extractor.rb +253 -0
  67. data/lib/codebase_index/extractors/configuration_extractor.rb +219 -0
  68. data/lib/codebase_index/extractors/controller_extractor.rb +494 -0
  69. data/lib/codebase_index/extractors/database_view_extractor.rb +278 -0
  70. data/lib/codebase_index/extractors/decorator_extractor.rb +260 -0
  71. data/lib/codebase_index/extractors/engine_extractor.rb +204 -0
  72. data/lib/codebase_index/extractors/event_extractor.rb +211 -0
  73. data/lib/codebase_index/extractors/factory_extractor.rb +289 -0
  74. data/lib/codebase_index/extractors/graphql_extractor.rb +917 -0
  75. data/lib/codebase_index/extractors/i18n_extractor.rb +117 -0
  76. data/lib/codebase_index/extractors/job_extractor.rb +369 -0
  77. data/lib/codebase_index/extractors/lib_extractor.rb +249 -0
  78. data/lib/codebase_index/extractors/mailer_extractor.rb +339 -0
  79. data/lib/codebase_index/extractors/manager_extractor.rb +202 -0
  80. data/lib/codebase_index/extractors/middleware_extractor.rb +133 -0
  81. data/lib/codebase_index/extractors/migration_extractor.rb +469 -0
  82. data/lib/codebase_index/extractors/model_extractor.rb +960 -0
  83. data/lib/codebase_index/extractors/phlex_extractor.rb +252 -0
  84. data/lib/codebase_index/extractors/policy_extractor.rb +214 -0
  85. data/lib/codebase_index/extractors/poro_extractor.rb +246 -0
  86. data/lib/codebase_index/extractors/pundit_extractor.rb +223 -0
  87. data/lib/codebase_index/extractors/rails_source_extractor.rb +473 -0
  88. data/lib/codebase_index/extractors/rake_task_extractor.rb +343 -0
  89. data/lib/codebase_index/extractors/route_extractor.rb +181 -0
  90. data/lib/codebase_index/extractors/scheduled_job_extractor.rb +331 -0
  91. data/lib/codebase_index/extractors/serializer_extractor.rb +334 -0
  92. data/lib/codebase_index/extractors/service_extractor.rb +254 -0
  93. data/lib/codebase_index/extractors/shared_dependency_scanner.rb +91 -0
  94. data/lib/codebase_index/extractors/shared_utility_methods.rb +99 -0
  95. data/lib/codebase_index/extractors/state_machine_extractor.rb +398 -0
  96. data/lib/codebase_index/extractors/test_mapping_extractor.rb +225 -0
  97. data/lib/codebase_index/extractors/validator_extractor.rb +225 -0
  98. data/lib/codebase_index/extractors/view_component_extractor.rb +310 -0
  99. data/lib/codebase_index/extractors/view_template_extractor.rb +261 -0
  100. data/lib/codebase_index/feedback/gap_detector.rb +89 -0
  101. data/lib/codebase_index/feedback/store.rb +119 -0
  102. data/lib/codebase_index/flow_analysis/operation_extractor.rb +209 -0
  103. data/lib/codebase_index/flow_analysis/response_code_mapper.rb +154 -0
  104. data/lib/codebase_index/flow_assembler.rb +290 -0
  105. data/lib/codebase_index/flow_document.rb +191 -0
  106. data/lib/codebase_index/flow_precomputer.rb +102 -0
  107. data/lib/codebase_index/formatting/base.rb +40 -0
  108. data/lib/codebase_index/formatting/claude_adapter.rb +98 -0
  109. data/lib/codebase_index/formatting/generic_adapter.rb +56 -0
  110. data/lib/codebase_index/formatting/gpt_adapter.rb +64 -0
  111. data/lib/codebase_index/formatting/human_adapter.rb +78 -0
  112. data/lib/codebase_index/graph_analyzer.rb +374 -0
  113. data/lib/codebase_index/mcp/index_reader.rb +394 -0
  114. data/lib/codebase_index/mcp/renderers/claude_renderer.rb +81 -0
  115. data/lib/codebase_index/mcp/renderers/json_renderer.rb +17 -0
  116. data/lib/codebase_index/mcp/renderers/markdown_renderer.rb +352 -0
  117. data/lib/codebase_index/mcp/renderers/plain_renderer.rb +240 -0
  118. data/lib/codebase_index/mcp/server.rb +935 -0
  119. data/lib/codebase_index/mcp/tool_response_renderer.rb +62 -0
  120. data/lib/codebase_index/model_name_cache.rb +51 -0
  121. data/lib/codebase_index/notion/client.rb +217 -0
  122. data/lib/codebase_index/notion/exporter.rb +219 -0
  123. data/lib/codebase_index/notion/mapper.rb +39 -0
  124. data/lib/codebase_index/notion/mappers/column_mapper.rb +65 -0
  125. data/lib/codebase_index/notion/mappers/migration_mapper.rb +39 -0
  126. data/lib/codebase_index/notion/mappers/model_mapper.rb +164 -0
  127. data/lib/codebase_index/notion/rate_limiter.rb +68 -0
  128. data/lib/codebase_index/observability/health_check.rb +81 -0
  129. data/lib/codebase_index/observability/instrumentation.rb +34 -0
  130. data/lib/codebase_index/observability/structured_logger.rb +75 -0
  131. data/lib/codebase_index/operator/error_escalator.rb +81 -0
  132. data/lib/codebase_index/operator/pipeline_guard.rb +99 -0
  133. data/lib/codebase_index/operator/status_reporter.rb +80 -0
  134. data/lib/codebase_index/railtie.rb +26 -0
  135. data/lib/codebase_index/resilience/circuit_breaker.rb +99 -0
  136. data/lib/codebase_index/resilience/index_validator.rb +185 -0
  137. data/lib/codebase_index/resilience/retryable_provider.rb +108 -0
  138. data/lib/codebase_index/retrieval/context_assembler.rb +249 -0
  139. data/lib/codebase_index/retrieval/query_classifier.rb +131 -0
  140. data/lib/codebase_index/retrieval/ranker.rb +273 -0
  141. data/lib/codebase_index/retrieval/search_executor.rb +327 -0
  142. data/lib/codebase_index/retriever.rb +160 -0
  143. data/lib/codebase_index/ruby_analyzer/class_analyzer.rb +190 -0
  144. data/lib/codebase_index/ruby_analyzer/dataflow_analyzer.rb +78 -0
  145. data/lib/codebase_index/ruby_analyzer/fqn_builder.rb +18 -0
  146. data/lib/codebase_index/ruby_analyzer/mermaid_renderer.rb +275 -0
  147. data/lib/codebase_index/ruby_analyzer/method_analyzer.rb +143 -0
  148. data/lib/codebase_index/ruby_analyzer/trace_enricher.rb +139 -0
  149. data/lib/codebase_index/ruby_analyzer.rb +87 -0
  150. data/lib/codebase_index/session_tracer/file_store.rb +111 -0
  151. data/lib/codebase_index/session_tracer/middleware.rb +143 -0
  152. data/lib/codebase_index/session_tracer/redis_store.rb +112 -0
  153. data/lib/codebase_index/session_tracer/session_flow_assembler.rb +263 -0
  154. data/lib/codebase_index/session_tracer/session_flow_document.rb +223 -0
  155. data/lib/codebase_index/session_tracer/solid_cache_store.rb +145 -0
  156. data/lib/codebase_index/session_tracer/store.rb +67 -0
  157. data/lib/codebase_index/storage/graph_store.rb +120 -0
  158. data/lib/codebase_index/storage/metadata_store.rb +169 -0
  159. data/lib/codebase_index/storage/pgvector.rb +163 -0
  160. data/lib/codebase_index/storage/qdrant.rb +172 -0
  161. data/lib/codebase_index/storage/vector_store.rb +156 -0
  162. data/lib/codebase_index/temporal/snapshot_store.rb +341 -0
  163. data/lib/codebase_index/version.rb +5 -0
  164. data/lib/codebase_index.rb +223 -0
  165. data/lib/generators/codebase_index/install_generator.rb +32 -0
  166. data/lib/generators/codebase_index/pgvector_generator.rb +37 -0
  167. data/lib/generators/codebase_index/templates/add_pgvector_to_codebase_index.rb.erb +15 -0
  168. data/lib/generators/codebase_index/templates/create_codebase_index_tables.rb.erb +43 -0
  169. data/lib/tasks/codebase_index.rake +583 -0
  170. data/lib/tasks/codebase_index_evaluation.rake +115 -0
  171. metadata +252 -0
@@ -0,0 +1,557 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mcp'
4
+ require_relative 'connection_manager'
5
+ require_relative 'model_validator'
6
+ require_relative 'safe_context'
7
+ require_relative 'tools/tier1'
8
+ require_relative 'tools/tier2'
9
+ require_relative 'tools/tier3'
10
+ require_relative 'tools/tier4'
11
+ require_relative 'sql_validator'
12
+ require_relative 'audit_logger'
13
+ require_relative 'confirmation'
14
+ require_relative 'console_response_renderer'
15
+
16
+ module CodebaseIndex
17
+ module Console
18
+ # Console MCP Server — queries live Rails application state.
19
+ #
20
+ # Communicates with a bridge process running inside the Rails environment
21
+ # via JSON-lines over stdio. Exposes Tier 1-4 tools (read-only, domain, analytics, guarded) through MCP.
22
+ #
23
+ # @example
24
+ # server = CodebaseIndex::Console::Server.build(config: config)
25
+ # transport = MCP::Server::Transports::StdioTransport.new(server)
26
+ # transport.open
27
+ #
28
+ module Server # rubocop:disable Metrics/ModuleLength
29
+ TIER1_TOOLS = %w[count sample find pluck aggregate association_count schema recent status].freeze
30
+ TIER2_TOOLS = %w[diagnose_model data_snapshot validate_record check_setting update_setting
31
+ check_policy validate_with check_eligibility decorate].freeze
32
+ TIER3_TOOLS = %w[slow_endpoints error_rates throughput job_queues job_failures job_find
33
+ job_schedule redis_info cache_stats channel_status].freeze
34
+ TIER4_TOOLS = %w[eval sql query].freeze
35
+
36
+ class << self # rubocop:disable Metrics/ClassLength
37
+ # Build a configured MCP::Server with console tools.
38
+ #
39
+ # @param config [Hash] Configuration hash (from YAML or env)
40
+ # @return [MCP::Server] Configured server ready for transport
41
+ def build(config:)
42
+ connection_config = config['console'] || config
43
+ conn_mgr = ConnectionManager.new(config: connection_config)
44
+ redacted_columns = Array(config['redacted_columns'] || connection_config['redacted_columns'])
45
+ safe_ctx = redacted_columns.any? ? SafeContext.new(connection: nil, redacted_columns: redacted_columns) : nil
46
+
47
+ server = ::MCP::Server.new(
48
+ name: 'codebase-console',
49
+ version: defined?(CodebaseIndex::VERSION) ? CodebaseIndex::VERSION : '0.1.0'
50
+ )
51
+
52
+ renderer = build_console_renderer
53
+
54
+ register_tier1_tools(server, conn_mgr, safe_ctx, renderer: renderer)
55
+ register_tier2_tools(server, conn_mgr, safe_ctx, renderer: renderer)
56
+ register_tier3_tools(server, conn_mgr, safe_ctx, renderer: renderer)
57
+ register_tier4_tools(server, conn_mgr, safe_ctx, renderer: renderer)
58
+ server
59
+ end
60
+
61
+ # Register Tier 1 read-only tools on the server.
62
+ #
63
+ # @param server [MCP::Server] The MCP server instance
64
+ # @param conn_mgr [ConnectionManager] Bridge connection
65
+ # @param safe_ctx [SafeContext, nil] Optional context for column redaction
66
+ # @return [void]
67
+ def register_tier1_tools(server, conn_mgr, safe_ctx = nil, renderer: nil)
68
+ TIER1_TOOLS.each { |tool| send(:"define_#{tool}", server, conn_mgr, safe_ctx, renderer: renderer) }
69
+ end
70
+
71
+ # Register Tier 2 domain-aware tools on the server.
72
+ #
73
+ # @param server [MCP::Server] The MCP server instance
74
+ # @param conn_mgr [ConnectionManager] Bridge connection
75
+ # @param safe_ctx [SafeContext, nil] Optional context for column redaction
76
+ # @return [void]
77
+ def register_tier2_tools(server, conn_mgr, safe_ctx = nil, renderer: nil)
78
+ TIER2_TOOLS.each { |tool| send(:"define_#{tool}", server, conn_mgr, safe_ctx, renderer: renderer) }
79
+ end
80
+
81
+ # Register Tier 3 analytics tools on the server.
82
+ #
83
+ # @param server [MCP::Server] The MCP server instance
84
+ # @param conn_mgr [ConnectionManager] Bridge connection
85
+ # @param safe_ctx [SafeContext, nil] Optional context for column redaction
86
+ # @return [void]
87
+ def register_tier3_tools(server, conn_mgr, safe_ctx = nil, renderer: nil)
88
+ TIER3_TOOLS.each { |tool| send(:"define_#{tool}", server, conn_mgr, safe_ctx, renderer: renderer) }
89
+ end
90
+
91
+ # Register Tier 4 guarded tools on the server.
92
+ #
93
+ # @param server [MCP::Server] The MCP server instance
94
+ # @param conn_mgr [ConnectionManager] Bridge connection
95
+ # @param safe_ctx [SafeContext, nil] Optional context for column redaction
96
+ # @return [void]
97
+ def register_tier4_tools(server, conn_mgr, safe_ctx = nil, renderer: nil)
98
+ TIER4_TOOLS.each { |tool| send(:"define_#{tool}", server, conn_mgr, safe_ctx, renderer: renderer) }
99
+ end
100
+
101
+ private
102
+
103
+ def respond(text)
104
+ ::MCP::Tool::Response.new([{ type: 'text', text: text }])
105
+ end
106
+
107
+ def send_to_bridge(conn_mgr, request, safe_ctx = nil, renderer: nil)
108
+ response = conn_mgr.send_request(request)
109
+ if response['ok']
110
+ result = response['result']
111
+ result = apply_redaction(result, safe_ctx) if safe_ctx
112
+ text = renderer ? renderer.render_default(result) : JSON.pretty_generate(result)
113
+ respond(text)
114
+ else
115
+ ::MCP::Tool::Response.new(
116
+ [{ type: 'text', text: "#{response['error_type']}: #{response['error']}" }],
117
+ is_error: true
118
+ )
119
+ end
120
+ rescue ConnectionError => e
121
+ ::MCP::Tool::Response.new([{ type: 'text', text: "Connection error: #{e.message}" }], is_error: true)
122
+ end
123
+
124
+ # Apply SafeContext column redaction to a result value.
125
+ #
126
+ # Handles Hash (single record) and Array<Hash> (multiple records).
127
+ # Non-Hash values are returned unchanged.
128
+ #
129
+ # @param result [Object] The result from the bridge
130
+ # @param safe_ctx [SafeContext] The context with redacted_columns configured
131
+ # @return [Object] Redacted result
132
+ def apply_redaction(result, safe_ctx)
133
+ case result
134
+ when Array
135
+ result.map { |item| item.is_a?(Hash) ? safe_ctx.redact(item) : item }
136
+ when Hash
137
+ safe_ctx.redact(result)
138
+ else
139
+ result
140
+ end
141
+ end
142
+
143
+ def build_console_renderer
144
+ format = if CodebaseIndex.respond_to?(:configuration)
145
+ CodebaseIndex.configuration&.context_format || :markdown
146
+ else
147
+ :markdown
148
+ end
149
+ format == :json ? JsonConsoleRenderer.new : ConsoleResponseRenderer.new
150
+ end
151
+
152
+ def define_count(server, conn_mgr, safe_ctx = nil, renderer: nil)
153
+ define_console_tool(server, conn_mgr, 'console_count', 'Count records matching scope conditions',
154
+ properties: { model: str_prop('Model name'), scope: obj_prop('Filter conditions') },
155
+ required: ['model'], safe_ctx: safe_ctx, renderer: renderer) do |args|
156
+ Tools::Tier1.console_count(model: args[:model], scope: args[:scope])
157
+ end
158
+ end
159
+
160
+ def define_sample(server, conn_mgr, safe_ctx = nil, renderer: nil)
161
+ define_console_tool(server, conn_mgr, 'console_sample', 'Random sample of records',
162
+ properties: {
163
+ model: str_prop('Model name'), limit: int_prop('Max records (default 5, max 25)'),
164
+ columns: arr_prop('Columns to include'), scope: obj_prop('Filter conditions')
165
+ }, required: ['model'], safe_ctx: safe_ctx, renderer: renderer) do |args|
166
+ Tools::Tier1.console_sample(
167
+ model: args[:model], scope: args[:scope], limit: args[:limit] || 5, columns: args[:columns]
168
+ )
169
+ end
170
+ end
171
+
172
+ def define_find(server, conn_mgr, safe_ctx = nil, renderer: nil)
173
+ define_console_tool(server, conn_mgr, 'console_find',
174
+ 'Find a single record by primary key or unique column',
175
+ properties: {
176
+ model: str_prop('Model name'), id: int_prop('Primary key value'),
177
+ by: obj_prop('Unique column lookup'),
178
+ columns: arr_prop('Columns to include')
179
+ }, required: ['model'], safe_ctx: safe_ctx, renderer: renderer) do |args|
180
+ Tools::Tier1.console_find(model: args[:model], id: args[:id], by: args[:by], columns: args[:columns])
181
+ end
182
+ end
183
+
184
+ def define_pluck(server, conn_mgr, safe_ctx = nil, renderer: nil)
185
+ define_console_tool(server, conn_mgr, 'console_pluck', 'Extract column values from records',
186
+ properties: {
187
+ model: str_prop('Model name'), columns: arr_prop('Column names to pluck'),
188
+ scope: obj_prop('Filter conditions'),
189
+ limit: int_prop('Max records (default 100, max 1000)'),
190
+ distinct: bool_prop('Return unique values only')
191
+ }, required: %w[model columns], safe_ctx: safe_ctx, renderer: renderer) do |args|
192
+ Tools::Tier1.console_pluck(
193
+ model: args[:model], columns: args[:columns], scope: args[:scope],
194
+ limit: args[:limit] || 100, distinct: args[:distinct] || false
195
+ )
196
+ end
197
+ end
198
+
199
+ def define_aggregate(server, conn_mgr, safe_ctx = nil, renderer: nil)
200
+ define_console_tool(server, conn_mgr, 'console_aggregate',
201
+ 'Run aggregate function (sum/avg/min/max) on a column',
202
+ properties: {
203
+ model: str_prop('Model name'),
204
+ function: str_prop('Aggregate function: sum, avg, minimum, maximum'),
205
+ column: str_prop('Column to aggregate'), scope: obj_prop('Filter conditions')
206
+ }, required: %w[model function column], safe_ctx: safe_ctx, renderer: renderer) do |args|
207
+ Tools::Tier1.console_aggregate(
208
+ model: args[:model], function: args[:function], column: args[:column], scope: args[:scope]
209
+ )
210
+ end
211
+ end
212
+
213
+ def define_association_count(server, conn_mgr, safe_ctx = nil, renderer: nil)
214
+ define_console_tool(server, conn_mgr, 'console_association_count',
215
+ 'Count associated records for a specific record',
216
+ properties: {
217
+ model: str_prop('Model name'), id: int_prop('Record primary key'),
218
+ association: str_prop('Association name'),
219
+ scope: obj_prop('Filter on association')
220
+ }, required: %w[model id association], safe_ctx: safe_ctx, renderer: renderer) do |args|
221
+ Tools::Tier1.console_association_count(
222
+ model: args[:model], id: args[:id], association: args[:association], scope: args[:scope]
223
+ )
224
+ end
225
+ end
226
+
227
+ def define_schema(server, conn_mgr, safe_ctx = nil, renderer: nil)
228
+ define_console_tool(server, conn_mgr, 'console_schema', 'Get database schema for a model',
229
+ properties: {
230
+ model: str_prop('Model name'),
231
+ include_indexes: bool_prop('Include index information')
232
+ }, required: ['model'], safe_ctx: safe_ctx, renderer: renderer) do |args|
233
+ Tools::Tier1.console_schema(model: args[:model], include_indexes: args[:include_indexes] || false)
234
+ end
235
+ end
236
+
237
+ def define_recent(server, conn_mgr, safe_ctx = nil, renderer: nil)
238
+ define_console_tool(server, conn_mgr, 'console_recent', 'Recently created/updated records',
239
+ properties: {
240
+ model: str_prop('Model name'),
241
+ order_by: str_prop('Column to sort by (default: created_at)'),
242
+ direction: str_prop('Sort direction: asc or desc (default: desc)'),
243
+ limit: int_prop('Max records (default 10, max 50)'),
244
+ scope: obj_prop('Filter conditions'), columns: arr_prop('Columns to include')
245
+ }, required: ['model'], safe_ctx: safe_ctx, renderer: renderer) do |args|
246
+ Tools::Tier1.console_recent(
247
+ model: args[:model], order_by: args[:order_by] || 'created_at',
248
+ direction: args[:direction] || 'desc', limit: args[:limit] || 10,
249
+ scope: args[:scope], columns: args[:columns]
250
+ )
251
+ end
252
+ end
253
+
254
+ def define_status(server, conn_mgr, safe_ctx = nil, renderer: nil)
255
+ define_console_tool(server, conn_mgr, 'console_status',
256
+ 'System health check - list models and connection status',
257
+ properties: {}, safe_ctx: safe_ctx, renderer: renderer) do |_args|
258
+ Tools::Tier1.console_status
259
+ end
260
+ end
261
+
262
+ # ── Tier 2 tool definitions ──────────────────────────────────────────
263
+
264
+ def define_diagnose_model(server, conn_mgr, safe_ctx = nil, renderer: nil)
265
+ define_console_tool(server, conn_mgr, 'console_diagnose_model',
266
+ 'Diagnose a model: count, recent records, aggregates',
267
+ properties: {
268
+ model: str_prop('Model name'), scope: obj_prop('Filter conditions'),
269
+ sample_size: int_prop('Sample records (default 5, max 25)')
270
+ }, required: ['model'], safe_ctx: safe_ctx, renderer: renderer) do |args|
271
+ Tools::Tier2.console_diagnose_model(
272
+ model: args[:model], scope: args[:scope], sample_size: args[:sample_size] || 5
273
+ )
274
+ end
275
+ end
276
+
277
+ def define_data_snapshot(server, conn_mgr, safe_ctx = nil, renderer: nil)
278
+ define_console_tool(server, conn_mgr, 'console_data_snapshot',
279
+ 'Snapshot a record with associations for debugging',
280
+ properties: {
281
+ model: str_prop('Model name'), id: int_prop('Record primary key'),
282
+ associations: arr_prop('Association names to include'),
283
+ depth: int_prop('Association depth (default 1, max 3)')
284
+ }, required: %w[model id], safe_ctx: safe_ctx, renderer: renderer) do |args|
285
+ Tools::Tier2.console_data_snapshot(
286
+ model: args[:model], id: args[:id],
287
+ associations: args[:associations], depth: args[:depth] || 1
288
+ )
289
+ end
290
+ end
291
+
292
+ def define_validate_record(server, conn_mgr, safe_ctx = nil, renderer: nil)
293
+ define_console_tool(server, conn_mgr, 'console_validate_record',
294
+ 'Run validations on an existing record',
295
+ properties: {
296
+ model: str_prop('Model name'), id: int_prop('Record primary key'),
297
+ attributes: obj_prop('Attributes to set before validating')
298
+ }, required: %w[model id], safe_ctx: safe_ctx, renderer: renderer) do |args|
299
+ Tools::Tier2.console_validate_record(
300
+ model: args[:model], id: args[:id], attributes: args[:attributes]
301
+ )
302
+ end
303
+ end
304
+
305
+ def define_check_setting(server, conn_mgr, safe_ctx = nil, renderer: nil)
306
+ define_console_tool(server, conn_mgr, 'console_check_setting',
307
+ 'Check a configuration setting value',
308
+ properties: {
309
+ key: str_prop('Setting key'), namespace: str_prop('Setting namespace')
310
+ }, required: ['key'], safe_ctx: safe_ctx, renderer: renderer) do |args|
311
+ Tools::Tier2.console_check_setting(key: args[:key], namespace: args[:namespace])
312
+ end
313
+ end
314
+
315
+ def define_update_setting(server, conn_mgr, safe_ctx = nil, renderer: nil)
316
+ define_console_tool(server, conn_mgr, 'console_update_setting',
317
+ 'Update a configuration setting (requires confirmation)',
318
+ properties: {
319
+ key: str_prop('Setting key'), value: str_prop('New value'),
320
+ namespace: str_prop('Setting namespace')
321
+ }, required: %w[key value], safe_ctx: safe_ctx, renderer: renderer) do |args|
322
+ Tools::Tier2.console_update_setting(
323
+ key: args[:key], value: args[:value], namespace: args[:namespace]
324
+ )
325
+ end
326
+ end
327
+
328
+ def define_check_policy(server, conn_mgr, safe_ctx = nil, renderer: nil)
329
+ define_console_tool(server, conn_mgr, 'console_check_policy',
330
+ 'Check authorization policy for a record and user',
331
+ properties: {
332
+ model: str_prop('Model name'), id: int_prop('Record primary key'),
333
+ user_id: int_prop('User to check'), action: str_prop('Policy action')
334
+ }, required: %w[model id user_id action],
335
+ safe_ctx: safe_ctx, renderer: renderer) do |args|
336
+ Tools::Tier2.console_check_policy(
337
+ model: args[:model], id: args[:id], user_id: args[:user_id], action: args[:action]
338
+ )
339
+ end
340
+ end
341
+
342
+ def define_validate_with(server, conn_mgr, safe_ctx = nil, renderer: nil)
343
+ define_console_tool(server, conn_mgr, 'console_validate_with',
344
+ 'Validate attributes against a model without persisting',
345
+ properties: {
346
+ model: str_prop('Model name'), attributes: obj_prop('Attributes to validate'),
347
+ context: str_prop('Validation context')
348
+ }, required: %w[model attributes], safe_ctx: safe_ctx, renderer: renderer) do |args|
349
+ Tools::Tier2.console_validate_with(
350
+ model: args[:model], attributes: args[:attributes], context: args[:context]
351
+ )
352
+ end
353
+ end
354
+
355
+ def define_check_eligibility(server, conn_mgr, safe_ctx = nil, renderer: nil)
356
+ define_console_tool(server, conn_mgr, 'console_check_eligibility',
357
+ 'Check feature eligibility for a record',
358
+ properties: {
359
+ model: str_prop('Model name'), id: int_prop('Record primary key'),
360
+ feature: str_prop('Feature name')
361
+ }, required: %w[model id feature], safe_ctx: safe_ctx, renderer: renderer) do |args|
362
+ Tools::Tier2.console_check_eligibility(
363
+ model: args[:model], id: args[:id], feature: args[:feature]
364
+ )
365
+ end
366
+ end
367
+
368
+ def define_decorate(server, conn_mgr, safe_ctx = nil, renderer: nil)
369
+ define_console_tool(server, conn_mgr, 'console_decorate',
370
+ 'Invoke a decorator on a record and return computed attributes',
371
+ properties: {
372
+ model: str_prop('Model name'), id: int_prop('Record primary key'),
373
+ methods: arr_prop('Decorator methods to call')
374
+ }, required: %w[model id], safe_ctx: safe_ctx, renderer: renderer) do |args|
375
+ Tools::Tier2.console_decorate(model: args[:model], id: args[:id], methods: args[:methods])
376
+ end
377
+ end
378
+
379
+ # ── Tier 3 tool definitions ──────────────────────────────────────────
380
+
381
+ def define_slow_endpoints(server, conn_mgr, safe_ctx = nil, renderer: nil)
382
+ define_console_tool(server, conn_mgr, 'console_slow_endpoints',
383
+ 'List slowest endpoints by response time',
384
+ properties: {
385
+ limit: int_prop('Max endpoints (default 10, max 100)'),
386
+ period: str_prop('Time period (default: 1h)')
387
+ }, safe_ctx: safe_ctx, renderer: renderer) do |args|
388
+ Tools::Tier3.console_slow_endpoints(limit: args[:limit] || 10, period: args[:period] || '1h')
389
+ end
390
+ end
391
+
392
+ def define_error_rates(server, conn_mgr, safe_ctx = nil, renderer: nil)
393
+ define_console_tool(server, conn_mgr, 'console_error_rates',
394
+ 'Get error rates by controller or overall',
395
+ properties: {
396
+ period: str_prop('Time period (default: 1h)'),
397
+ controller: str_prop('Filter by controller')
398
+ }, safe_ctx: safe_ctx, renderer: renderer) do |args|
399
+ Tools::Tier3.console_error_rates(period: args[:period] || '1h', controller: args[:controller])
400
+ end
401
+ end
402
+
403
+ def define_throughput(server, conn_mgr, safe_ctx = nil, renderer: nil)
404
+ define_console_tool(server, conn_mgr, 'console_throughput',
405
+ 'Get request throughput over time',
406
+ properties: {
407
+ period: str_prop('Time period (default: 1h)'),
408
+ interval: str_prop('Aggregation interval (default: 5m)')
409
+ }, safe_ctx: safe_ctx, renderer: renderer) do |args|
410
+ Tools::Tier3.console_throughput(
411
+ period: args[:period] || '1h', interval: args[:interval] || '5m'
412
+ )
413
+ end
414
+ end
415
+
416
+ def define_job_queues(server, conn_mgr, safe_ctx = nil, renderer: nil)
417
+ define_console_tool(server, conn_mgr, 'console_job_queues',
418
+ 'Get job queue statistics',
419
+ properties: {
420
+ queue: str_prop('Filter by queue name')
421
+ }, safe_ctx: safe_ctx, renderer: renderer) do |args|
422
+ Tools::Tier3.console_job_queues(queue: args[:queue])
423
+ end
424
+ end
425
+
426
+ def define_job_failures(server, conn_mgr, safe_ctx = nil, renderer: nil)
427
+ define_console_tool(server, conn_mgr, 'console_job_failures',
428
+ 'List recent job failures',
429
+ properties: {
430
+ limit: int_prop('Max failures (default 10, max 100)'),
431
+ queue: str_prop('Filter by queue name')
432
+ }, safe_ctx: safe_ctx, renderer: renderer) do |args|
433
+ Tools::Tier3.console_job_failures(limit: args[:limit] || 10, queue: args[:queue])
434
+ end
435
+ end
436
+
437
+ def define_job_find(server, conn_mgr, safe_ctx = nil, renderer: nil)
438
+ define_console_tool(server, conn_mgr, 'console_job_find',
439
+ 'Find a job by ID, optionally retry it (requires confirmation)',
440
+ properties: {
441
+ job_id: str_prop('Job identifier'),
442
+ retry: bool_prop('Retry the job (requires confirmation)')
443
+ }, required: ['job_id'], safe_ctx: safe_ctx, renderer: renderer) do |args|
444
+ Tools::Tier3.console_job_find(job_id: args[:job_id], retry_job: args[:retry])
445
+ end
446
+ end
447
+
448
+ def define_job_schedule(server, conn_mgr, safe_ctx = nil, renderer: nil)
449
+ define_console_tool(server, conn_mgr, 'console_job_schedule',
450
+ 'List scheduled/upcoming jobs',
451
+ properties: {
452
+ limit: int_prop('Max jobs (default 20, max 100)')
453
+ }, safe_ctx: safe_ctx, renderer: renderer) do |args|
454
+ Tools::Tier3.console_job_schedule(limit: args[:limit] || 20)
455
+ end
456
+ end
457
+
458
+ def define_redis_info(server, conn_mgr, safe_ctx = nil, renderer: nil)
459
+ define_console_tool(server, conn_mgr, 'console_redis_info',
460
+ 'Get Redis server information',
461
+ properties: {
462
+ section: str_prop('INFO section (e.g., memory, stats)')
463
+ }, safe_ctx: safe_ctx, renderer: renderer) do |args|
464
+ Tools::Tier3.console_redis_info(section: args[:section])
465
+ end
466
+ end
467
+
468
+ def define_cache_stats(server, conn_mgr, safe_ctx = nil, renderer: nil)
469
+ define_console_tool(server, conn_mgr, 'console_cache_stats',
470
+ 'Get cache store statistics',
471
+ properties: {
472
+ namespace: str_prop('Cache namespace filter')
473
+ }, safe_ctx: safe_ctx, renderer: renderer) do |args|
474
+ Tools::Tier3.console_cache_stats(namespace: args[:namespace])
475
+ end
476
+ end
477
+
478
+ def define_channel_status(server, conn_mgr, safe_ctx = nil, renderer: nil)
479
+ define_console_tool(server, conn_mgr, 'console_channel_status',
480
+ 'Get ActionCable channel status',
481
+ properties: {
482
+ channel: str_prop('Filter by channel name')
483
+ }, safe_ctx: safe_ctx, renderer: renderer) do |args|
484
+ Tools::Tier3.console_channel_status(channel: args[:channel])
485
+ end
486
+ end
487
+
488
+ # ── Tier 4 tool definitions ──────────────────────────────────────────
489
+
490
+ def define_eval(server, conn_mgr, safe_ctx = nil, renderer: nil)
491
+ define_console_tool(server, conn_mgr, 'console_eval',
492
+ 'Execute arbitrary Ruby code (requires confirmation)',
493
+ properties: {
494
+ code: str_prop('Ruby code to execute'),
495
+ timeout: int_prop('Timeout in seconds (default 10, max 30)')
496
+ }, required: ['code'], safe_ctx: safe_ctx, renderer: renderer) do |args|
497
+ Tools::Tier4.console_eval(code: args[:code], timeout: args[:timeout] || 10)
498
+ end
499
+ end
500
+
501
+ def define_sql(server, conn_mgr, safe_ctx = nil, renderer: nil)
502
+ validator = SqlValidator.new
503
+ define_console_tool(server, conn_mgr, 'console_sql',
504
+ 'Execute read-only SQL (SELECT/WITH...SELECT only)',
505
+ properties: {
506
+ sql: str_prop('SQL query (SELECT or WITH...SELECT only)'),
507
+ limit: int_prop('Max rows returned (default unlimited, max 10000)')
508
+ }, required: ['sql'], safe_ctx: safe_ctx, renderer: renderer) do |args|
509
+ Tools::Tier4.console_sql(sql: args[:sql], validator: validator, limit: args[:limit])
510
+ end
511
+ end
512
+
513
+ def define_query(server, conn_mgr, safe_ctx = nil, renderer: nil)
514
+ props = {
515
+ model: str_prop('Model name'), select: arr_prop('Columns to select'),
516
+ joins: arr_prop('Associations to join'), group_by: arr_prop('Columns to group by'),
517
+ having: str_prop('HAVING clause'), order: obj_prop('Order specification'),
518
+ scope: obj_prop('Filter conditions'), limit: int_prop('Max rows (max 10000)')
519
+ }
520
+ define_console_tool(server, conn_mgr, 'console_query',
521
+ 'Enhanced query builder with joins and grouping',
522
+ properties: props, required: %w[model select],
523
+ safe_ctx: safe_ctx, renderer: renderer) do |args|
524
+ Tools::Tier4.console_query(
525
+ model: args[:model], select: args[:select], joins: args[:joins],
526
+ group_by: args[:group_by], having: args[:having],
527
+ order: args[:order], scope: args[:scope], limit: args[:limit]
528
+ )
529
+ end
530
+ end
531
+
532
+ # Shared tool definition helper that wires block -> bridge -> response.
533
+ # rubocop:disable Metrics/ParameterLists
534
+ def define_console_tool(server, conn_mgr, name, description, properties:, required: nil,
535
+ safe_ctx: nil, renderer: nil, &tool_block)
536
+ mgr = conn_mgr
537
+ ctx = safe_ctx
538
+ rdr = renderer
539
+ schema = { properties: properties }
540
+ schema[:required] = required if required&.any?
541
+ server.define_tool(name: name, description: description, input_schema: schema) do |server_context:, **args|
542
+ request = tool_block.call(args)
543
+ send_to_bridge(mgr, request.transform_keys(&:to_s), ctx, renderer: rdr)
544
+ end
545
+ end
546
+ # rubocop:enable Metrics/ParameterLists
547
+
548
+ # Schema property helpers for concise tool definitions.
549
+ def str_prop(desc) = { type: 'string', description: desc }
550
+ def int_prop(desc) = { type: 'integer', description: desc }
551
+ def obj_prop(desc) = { type: 'object', description: desc }
552
+ def bool_prop(desc) = { type: 'boolean', description: desc }
553
+ def arr_prop(desc) = { type: 'array', items: { type: 'string' }, description: desc }
554
+ end
555
+ end
556
+ end
557
+ end