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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +29 -0
- data/CODE_OF_CONDUCT.md +83 -0
- data/CONTRIBUTING.md +65 -0
- data/LICENSE.txt +21 -0
- data/README.md +481 -0
- data/exe/codebase-console-mcp +22 -0
- data/exe/codebase-index-mcp +61 -0
- data/exe/codebase-index-mcp-http +64 -0
- data/exe/codebase-index-mcp-start +58 -0
- data/lib/codebase_index/ast/call_site_extractor.rb +106 -0
- data/lib/codebase_index/ast/method_extractor.rb +76 -0
- data/lib/codebase_index/ast/node.rb +88 -0
- data/lib/codebase_index/ast/parser.rb +653 -0
- data/lib/codebase_index/ast.rb +6 -0
- data/lib/codebase_index/builder.rb +137 -0
- data/lib/codebase_index/chunking/chunk.rb +84 -0
- data/lib/codebase_index/chunking/semantic_chunker.rb +290 -0
- data/lib/codebase_index/console/adapters/cache_adapter.rb +58 -0
- data/lib/codebase_index/console/adapters/good_job_adapter.rb +66 -0
- data/lib/codebase_index/console/adapters/sidekiq_adapter.rb +66 -0
- data/lib/codebase_index/console/adapters/solid_queue_adapter.rb +66 -0
- data/lib/codebase_index/console/audit_logger.rb +75 -0
- data/lib/codebase_index/console/bridge.rb +170 -0
- data/lib/codebase_index/console/confirmation.rb +90 -0
- data/lib/codebase_index/console/connection_manager.rb +173 -0
- data/lib/codebase_index/console/console_response_renderer.rb +78 -0
- data/lib/codebase_index/console/model_validator.rb +81 -0
- data/lib/codebase_index/console/safe_context.rb +82 -0
- data/lib/codebase_index/console/server.rb +557 -0
- data/lib/codebase_index/console/sql_validator.rb +172 -0
- data/lib/codebase_index/console/tools/tier1.rb +118 -0
- data/lib/codebase_index/console/tools/tier2.rb +117 -0
- data/lib/codebase_index/console/tools/tier3.rb +110 -0
- data/lib/codebase_index/console/tools/tier4.rb +79 -0
- data/lib/codebase_index/coordination/pipeline_lock.rb +109 -0
- data/lib/codebase_index/cost_model/embedding_cost.rb +88 -0
- data/lib/codebase_index/cost_model/estimator.rb +128 -0
- data/lib/codebase_index/cost_model/provider_pricing.rb +67 -0
- data/lib/codebase_index/cost_model/storage_cost.rb +52 -0
- data/lib/codebase_index/cost_model.rb +22 -0
- data/lib/codebase_index/db/migrations/001_create_units.rb +38 -0
- data/lib/codebase_index/db/migrations/002_create_edges.rb +35 -0
- data/lib/codebase_index/db/migrations/003_create_embeddings.rb +37 -0
- data/lib/codebase_index/db/migrations/004_create_snapshots.rb +45 -0
- data/lib/codebase_index/db/migrations/005_create_snapshot_units.rb +40 -0
- data/lib/codebase_index/db/migrator.rb +71 -0
- data/lib/codebase_index/db/schema_version.rb +73 -0
- data/lib/codebase_index/dependency_graph.rb +227 -0
- data/lib/codebase_index/embedding/indexer.rb +130 -0
- data/lib/codebase_index/embedding/openai.rb +105 -0
- data/lib/codebase_index/embedding/provider.rb +135 -0
- data/lib/codebase_index/embedding/text_preparer.rb +112 -0
- data/lib/codebase_index/evaluation/baseline_runner.rb +115 -0
- data/lib/codebase_index/evaluation/evaluator.rb +146 -0
- data/lib/codebase_index/evaluation/metrics.rb +79 -0
- data/lib/codebase_index/evaluation/query_set.rb +148 -0
- data/lib/codebase_index/evaluation/report_generator.rb +90 -0
- data/lib/codebase_index/extracted_unit.rb +145 -0
- data/lib/codebase_index/extractor.rb +956 -0
- data/lib/codebase_index/extractors/action_cable_extractor.rb +228 -0
- data/lib/codebase_index/extractors/ast_source_extraction.rb +46 -0
- data/lib/codebase_index/extractors/behavioral_profile.rb +309 -0
- data/lib/codebase_index/extractors/caching_extractor.rb +261 -0
- data/lib/codebase_index/extractors/callback_analyzer.rb +232 -0
- data/lib/codebase_index/extractors/concern_extractor.rb +253 -0
- data/lib/codebase_index/extractors/configuration_extractor.rb +219 -0
- data/lib/codebase_index/extractors/controller_extractor.rb +494 -0
- data/lib/codebase_index/extractors/database_view_extractor.rb +278 -0
- data/lib/codebase_index/extractors/decorator_extractor.rb +260 -0
- data/lib/codebase_index/extractors/engine_extractor.rb +204 -0
- data/lib/codebase_index/extractors/event_extractor.rb +211 -0
- data/lib/codebase_index/extractors/factory_extractor.rb +289 -0
- data/lib/codebase_index/extractors/graphql_extractor.rb +917 -0
- data/lib/codebase_index/extractors/i18n_extractor.rb +117 -0
- data/lib/codebase_index/extractors/job_extractor.rb +369 -0
- data/lib/codebase_index/extractors/lib_extractor.rb +249 -0
- data/lib/codebase_index/extractors/mailer_extractor.rb +339 -0
- data/lib/codebase_index/extractors/manager_extractor.rb +202 -0
- data/lib/codebase_index/extractors/middleware_extractor.rb +133 -0
- data/lib/codebase_index/extractors/migration_extractor.rb +469 -0
- data/lib/codebase_index/extractors/model_extractor.rb +960 -0
- data/lib/codebase_index/extractors/phlex_extractor.rb +252 -0
- data/lib/codebase_index/extractors/policy_extractor.rb +214 -0
- data/lib/codebase_index/extractors/poro_extractor.rb +246 -0
- data/lib/codebase_index/extractors/pundit_extractor.rb +223 -0
- data/lib/codebase_index/extractors/rails_source_extractor.rb +473 -0
- data/lib/codebase_index/extractors/rake_task_extractor.rb +343 -0
- data/lib/codebase_index/extractors/route_extractor.rb +181 -0
- data/lib/codebase_index/extractors/scheduled_job_extractor.rb +331 -0
- data/lib/codebase_index/extractors/serializer_extractor.rb +334 -0
- data/lib/codebase_index/extractors/service_extractor.rb +254 -0
- data/lib/codebase_index/extractors/shared_dependency_scanner.rb +91 -0
- data/lib/codebase_index/extractors/shared_utility_methods.rb +99 -0
- data/lib/codebase_index/extractors/state_machine_extractor.rb +398 -0
- data/lib/codebase_index/extractors/test_mapping_extractor.rb +225 -0
- data/lib/codebase_index/extractors/validator_extractor.rb +225 -0
- data/lib/codebase_index/extractors/view_component_extractor.rb +310 -0
- data/lib/codebase_index/extractors/view_template_extractor.rb +261 -0
- data/lib/codebase_index/feedback/gap_detector.rb +89 -0
- data/lib/codebase_index/feedback/store.rb +119 -0
- data/lib/codebase_index/flow_analysis/operation_extractor.rb +209 -0
- data/lib/codebase_index/flow_analysis/response_code_mapper.rb +154 -0
- data/lib/codebase_index/flow_assembler.rb +290 -0
- data/lib/codebase_index/flow_document.rb +191 -0
- data/lib/codebase_index/flow_precomputer.rb +102 -0
- data/lib/codebase_index/formatting/base.rb +40 -0
- data/lib/codebase_index/formatting/claude_adapter.rb +98 -0
- data/lib/codebase_index/formatting/generic_adapter.rb +56 -0
- data/lib/codebase_index/formatting/gpt_adapter.rb +64 -0
- data/lib/codebase_index/formatting/human_adapter.rb +78 -0
- data/lib/codebase_index/graph_analyzer.rb +374 -0
- data/lib/codebase_index/mcp/index_reader.rb +394 -0
- data/lib/codebase_index/mcp/renderers/claude_renderer.rb +81 -0
- data/lib/codebase_index/mcp/renderers/json_renderer.rb +17 -0
- data/lib/codebase_index/mcp/renderers/markdown_renderer.rb +352 -0
- data/lib/codebase_index/mcp/renderers/plain_renderer.rb +240 -0
- data/lib/codebase_index/mcp/server.rb +935 -0
- data/lib/codebase_index/mcp/tool_response_renderer.rb +62 -0
- data/lib/codebase_index/model_name_cache.rb +51 -0
- data/lib/codebase_index/notion/client.rb +217 -0
- data/lib/codebase_index/notion/exporter.rb +219 -0
- data/lib/codebase_index/notion/mapper.rb +39 -0
- data/lib/codebase_index/notion/mappers/column_mapper.rb +65 -0
- data/lib/codebase_index/notion/mappers/migration_mapper.rb +39 -0
- data/lib/codebase_index/notion/mappers/model_mapper.rb +164 -0
- data/lib/codebase_index/notion/rate_limiter.rb +68 -0
- data/lib/codebase_index/observability/health_check.rb +81 -0
- data/lib/codebase_index/observability/instrumentation.rb +34 -0
- data/lib/codebase_index/observability/structured_logger.rb +75 -0
- data/lib/codebase_index/operator/error_escalator.rb +81 -0
- data/lib/codebase_index/operator/pipeline_guard.rb +99 -0
- data/lib/codebase_index/operator/status_reporter.rb +80 -0
- data/lib/codebase_index/railtie.rb +26 -0
- data/lib/codebase_index/resilience/circuit_breaker.rb +99 -0
- data/lib/codebase_index/resilience/index_validator.rb +185 -0
- data/lib/codebase_index/resilience/retryable_provider.rb +108 -0
- data/lib/codebase_index/retrieval/context_assembler.rb +249 -0
- data/lib/codebase_index/retrieval/query_classifier.rb +131 -0
- data/lib/codebase_index/retrieval/ranker.rb +273 -0
- data/lib/codebase_index/retrieval/search_executor.rb +327 -0
- data/lib/codebase_index/retriever.rb +160 -0
- data/lib/codebase_index/ruby_analyzer/class_analyzer.rb +190 -0
- data/lib/codebase_index/ruby_analyzer/dataflow_analyzer.rb +78 -0
- data/lib/codebase_index/ruby_analyzer/fqn_builder.rb +18 -0
- data/lib/codebase_index/ruby_analyzer/mermaid_renderer.rb +275 -0
- data/lib/codebase_index/ruby_analyzer/method_analyzer.rb +143 -0
- data/lib/codebase_index/ruby_analyzer/trace_enricher.rb +139 -0
- data/lib/codebase_index/ruby_analyzer.rb +87 -0
- data/lib/codebase_index/session_tracer/file_store.rb +111 -0
- data/lib/codebase_index/session_tracer/middleware.rb +143 -0
- data/lib/codebase_index/session_tracer/redis_store.rb +112 -0
- data/lib/codebase_index/session_tracer/session_flow_assembler.rb +263 -0
- data/lib/codebase_index/session_tracer/session_flow_document.rb +223 -0
- data/lib/codebase_index/session_tracer/solid_cache_store.rb +145 -0
- data/lib/codebase_index/session_tracer/store.rb +67 -0
- data/lib/codebase_index/storage/graph_store.rb +120 -0
- data/lib/codebase_index/storage/metadata_store.rb +169 -0
- data/lib/codebase_index/storage/pgvector.rb +163 -0
- data/lib/codebase_index/storage/qdrant.rb +172 -0
- data/lib/codebase_index/storage/vector_store.rb +156 -0
- data/lib/codebase_index/temporal/snapshot_store.rb +341 -0
- data/lib/codebase_index/version.rb +5 -0
- data/lib/codebase_index.rb +223 -0
- data/lib/generators/codebase_index/install_generator.rb +32 -0
- data/lib/generators/codebase_index/pgvector_generator.rb +37 -0
- data/lib/generators/codebase_index/templates/add_pgvector_to_codebase_index.rb.erb +15 -0
- data/lib/generators/codebase_index/templates/create_codebase_index_tables.rb.erb +43 -0
- data/lib/tasks/codebase_index.rake +583 -0
- data/lib/tasks/codebase_index_evaluation.rake +115 -0
- 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
|