rails-mcp-server 1.2.3 → 1.4.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/README.md +168 -166
- data/docs/AGENT.md +345 -0
- data/exe/rails-mcp-config +1411 -0
- data/exe/rails-mcp-server +23 -10
- data/exe/rails-mcp-setup-claude +1 -1
- data/lib/rails-mcp-server/analyzers/analyze_controller_views.rb +253 -0
- data/lib/rails-mcp-server/analyzers/analyze_environment_config.rb +79 -0
- data/lib/rails-mcp-server/analyzers/analyze_models.rb +251 -0
- data/lib/rails-mcp-server/analyzers/base_analyzer.rb +42 -0
- data/lib/rails-mcp-server/analyzers/get_file.rb +40 -0
- data/lib/rails-mcp-server/analyzers/get_routes.rb +212 -0
- data/lib/rails-mcp-server/analyzers/get_schema.rb +216 -0
- data/lib/rails-mcp-server/analyzers/list_files.rb +43 -0
- data/lib/rails-mcp-server/analyzers/load_guide.rb +84 -0
- data/lib/rails-mcp-server/analyzers/project_info.rb +136 -0
- data/lib/rails-mcp-server/tools/base_tool.rb +2 -0
- data/lib/rails-mcp-server/tools/execute_ruby.rb +409 -0
- data/lib/rails-mcp-server/tools/execute_tool.rb +115 -0
- data/lib/rails-mcp-server/tools/search_tools.rb +186 -0
- data/lib/rails-mcp-server/tools/switch_project.rb +16 -1
- data/lib/rails-mcp-server/version.rb +1 -1
- data/lib/rails_mcp_server.rb +19 -53
- metadata +65 -18
- data/lib/rails-mcp-server/extensions/resource_templating.rb +0 -182
- data/lib/rails-mcp-server/extensions/server_templating.rb +0 -333
- data/lib/rails-mcp-server/tools/analyze_controller_views.rb +0 -239
- data/lib/rails-mcp-server/tools/analyze_environment_config.rb +0 -427
- data/lib/rails-mcp-server/tools/analyze_models.rb +0 -116
- data/lib/rails-mcp-server/tools/get_file.rb +0 -55
- data/lib/rails-mcp-server/tools/get_routes.rb +0 -24
- data/lib/rails-mcp-server/tools/get_schema.rb +0 -141
- data/lib/rails-mcp-server/tools/list_files.rb +0 -54
- data/lib/rails-mcp-server/tools/load_guide.rb +0 -370
- data/lib/rails-mcp-server/tools/project_info.rb +0 -86
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
module RailsMcpServer
|
|
2
|
+
class SearchTools < BaseTool
|
|
3
|
+
tool_name "search_tools"
|
|
4
|
+
|
|
5
|
+
description <<~DESC
|
|
6
|
+
Search available Rails MCP tools by keyword or category. Use this to discover tools
|
|
7
|
+
before invoking them with execute_tool.
|
|
8
|
+
|
|
9
|
+
Workflow:
|
|
10
|
+
1. Call search_tools to find relevant tools
|
|
11
|
+
2. Call execute_tool(tool_name: "tool_name", params: {...}) to run them
|
|
12
|
+
|
|
13
|
+
Categories: models, database, routing, controllers, files, project, guides
|
|
14
|
+
DESC
|
|
15
|
+
|
|
16
|
+
arguments do
|
|
17
|
+
optional(:query).filled(:string).description(
|
|
18
|
+
"Search term to find matching tools (e.g., 'model', 'routes', 'schema', 'controller')"
|
|
19
|
+
)
|
|
20
|
+
optional(:category).filled(:string).description(
|
|
21
|
+
"Filter by category: models, database, routing, controllers, files, project, guides"
|
|
22
|
+
)
|
|
23
|
+
optional(:detail_level).filled(:string).description(
|
|
24
|
+
"Level of detail: 'names' (tool names only), 'summary' (names + one-line description), 'full' (complete definition with parameters). Default: 'summary'"
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
TOOL_CATALOG = {
|
|
29
|
+
"project_info" => {
|
|
30
|
+
category: "project",
|
|
31
|
+
keywords: %w[project info structure directory tree rails version],
|
|
32
|
+
summary: "Get Rails version, directory structure, and project overview",
|
|
33
|
+
parameters: [
|
|
34
|
+
{name: "max_depth", type: "integer", required: false, description: "Directory tree depth (default: 2, max: 5)"},
|
|
35
|
+
{name: "include_files", type: "boolean", required: false, description: "Include files in tree (default: true)"},
|
|
36
|
+
{name: "detail_level", type: "string", required: false, description: "'minimal', 'summary', or 'full' (default: 'full')"}
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
"list_files" => {
|
|
40
|
+
category: "files",
|
|
41
|
+
keywords: %w[files list directory glob find search],
|
|
42
|
+
summary: "List files matching a pattern in a directory",
|
|
43
|
+
parameters: [
|
|
44
|
+
{name: "directory", type: "string", required: false, description: "Directory path relative to project root"},
|
|
45
|
+
{name: "pattern", type: "string", required: false, description: "Glob pattern (default: '*.rb')"}
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"get_file" => {
|
|
49
|
+
category: "files",
|
|
50
|
+
keywords: %w[file read content source code view],
|
|
51
|
+
summary: "Read the contents of a specific file",
|
|
52
|
+
parameters: [
|
|
53
|
+
{name: "path", type: "string", required: true, description: "File path relative to project root"}
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
"get_routes" => {
|
|
57
|
+
category: "routing",
|
|
58
|
+
keywords: %w[routes endpoints urls api paths http verbs controller actions introspection],
|
|
59
|
+
summary: "List HTTP routes via Rails introspection with filtering by controller, verb, or path",
|
|
60
|
+
parameters: [
|
|
61
|
+
{name: "controller", type: "string", required: false, description: "Filter by controller name"},
|
|
62
|
+
{name: "verb", type: "string", required: false, description: "Filter by HTTP verb (GET, POST, PUT, PATCH, DELETE)"},
|
|
63
|
+
{name: "path_contains", type: "string", required: false, description: "Filter routes containing path segment"},
|
|
64
|
+
{name: "named_only", type: "boolean", required: false, description: "Only return named routes (default: false)"},
|
|
65
|
+
{name: "detail_level", type: "string", required: false, description: "'names', 'summary', or 'full' (default: 'full')"}
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
"analyze_models" => {
|
|
69
|
+
category: "models",
|
|
70
|
+
keywords: %w[model active_record orm association schema database belongs_to has_many validations callbacks scopes prism introspection],
|
|
71
|
+
summary: "Analyze ActiveRecord models using Rails introspection and/or Prism static analysis",
|
|
72
|
+
parameters: [
|
|
73
|
+
{name: "model_name", type: "string", required: false, description: "Single model name (CamelCase)"},
|
|
74
|
+
{name: "model_names", type: "array", required: false, description: "Array of model names for batch analysis"},
|
|
75
|
+
{name: "detail_level", type: "string", required: false, description: "'names', 'associations', or 'full' (default: 'full')"},
|
|
76
|
+
{name: "analysis_type", type: "string", required: false, description: "'introspection' (runtime), 'static' (Prism AST), or 'full' (both). Default: 'introspection'"}
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
"get_schema" => {
|
|
80
|
+
category: "database",
|
|
81
|
+
keywords: %w[schema database table column migration sql foreign_key index],
|
|
82
|
+
summary: "Get database schema - all tables or specific table details",
|
|
83
|
+
parameters: [
|
|
84
|
+
{name: "table_name", type: "string", required: false, description: "Specific table (snake_case, plural)"},
|
|
85
|
+
{name: "table_names", type: "array", required: false, description: "Array of table names for batch retrieval"},
|
|
86
|
+
{name: "detail_level", type: "string", required: false, description: "'tables', 'summary', or 'full' (default: 'full')"}
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
"analyze_controller_views" => {
|
|
90
|
+
category: "controllers",
|
|
91
|
+
keywords: %w[controller view action template partial stimulus erb html callbacks filters before_action strong_params prism introspection],
|
|
92
|
+
summary: "Analyze controllers using Rails introspection and/or Prism static analysis (callbacks, filters, strong params, renders)",
|
|
93
|
+
parameters: [
|
|
94
|
+
{name: "controller_name", type: "string", required: false, description: "Specific controller to analyze"},
|
|
95
|
+
{name: "detail_level", type: "string", required: false, description: "'names', 'summary', or 'full' (default: 'full')"},
|
|
96
|
+
{name: "analysis_type", type: "string", required: false, description: "'introspection' (runtime), 'static' (Prism AST), or 'full' (both). Default: 'introspection'"}
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
"analyze_environment_config" => {
|
|
100
|
+
category: "project",
|
|
101
|
+
keywords: %w[environment config configuration development production test settings],
|
|
102
|
+
summary: "Compare environment configurations and find inconsistencies",
|
|
103
|
+
parameters: []
|
|
104
|
+
},
|
|
105
|
+
"load_guide" => {
|
|
106
|
+
category: "guides",
|
|
107
|
+
keywords: %w[guide documentation rails turbo stimulus kamal docs help],
|
|
108
|
+
summary: "Load Rails, Turbo, Stimulus, or Kamal documentation guides",
|
|
109
|
+
parameters: [
|
|
110
|
+
{name: "guides", type: "string", required: true, description: "Library: 'rails', 'turbo', 'stimulus', 'kamal', 'custom'"},
|
|
111
|
+
{name: "guide", type: "string", required: false, description: "Specific guide name to load"}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
}.freeze
|
|
115
|
+
|
|
116
|
+
CATEGORIES = %w[models database routing controllers files project guides].freeze
|
|
117
|
+
|
|
118
|
+
def call(query: nil, category: nil, detail_level: "summary")
|
|
119
|
+
detail_level = "summary" unless %w[names summary full].include?(detail_level)
|
|
120
|
+
|
|
121
|
+
# Filter tools
|
|
122
|
+
filtered_tools = TOOL_CATALOG.dup
|
|
123
|
+
|
|
124
|
+
if category && CATEGORIES.include?(category.downcase)
|
|
125
|
+
filtered_tools = filtered_tools.select { |_, info| info[:category] == category.downcase }
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
if query && !query.strip.empty?
|
|
129
|
+
query_terms = query.downcase.split(/\s+/)
|
|
130
|
+
filtered_tools = filtered_tools.select do |name, info|
|
|
131
|
+
searchable = [name, info[:category], info[:summary], *info[:keywords]].join(" ").downcase
|
|
132
|
+
query_terms.all? { |term| searchable.include?(term) }
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
if filtered_tools.empty?
|
|
137
|
+
return "No tools found matching your criteria. Available categories: #{CATEGORIES.join(", ")}"
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
format_results(filtered_tools, detail_level)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
private
|
|
144
|
+
|
|
145
|
+
def format_results(tools, detail_level)
|
|
146
|
+
case detail_level
|
|
147
|
+
when "names"
|
|
148
|
+
output = ["Available tools (invoke via execute_tool):\n"]
|
|
149
|
+
output << tools.keys.sort.join("\n")
|
|
150
|
+
output.join
|
|
151
|
+
|
|
152
|
+
when "summary"
|
|
153
|
+
output = ["Available tools (invoke via execute_tool):\n"]
|
|
154
|
+
tools.sort_by { |name, _| name }.each do |name, info|
|
|
155
|
+
output << " #{name} [#{info[:category]}]"
|
|
156
|
+
output << " #{info[:summary]}"
|
|
157
|
+
output << ""
|
|
158
|
+
end
|
|
159
|
+
output << "Example: execute_tool(tool_name: \"analyze_models\", params: { model_name: \"User\", analysis_type: \"full\" })"
|
|
160
|
+
output.join("\n")
|
|
161
|
+
|
|
162
|
+
when "full"
|
|
163
|
+
output = ["Available tools (invoke via execute_tool):\n"]
|
|
164
|
+
tools.sort_by { |name, _| name }.each do |name, info|
|
|
165
|
+
output << " #{name}"
|
|
166
|
+
output << " Category: #{info[:category]}"
|
|
167
|
+
output << " #{info[:summary]}"
|
|
168
|
+
output << " Keywords: #{info[:keywords].join(", ")}"
|
|
169
|
+
|
|
170
|
+
if info[:parameters].any?
|
|
171
|
+
output << " Parameters:"
|
|
172
|
+
info[:parameters].each do |param|
|
|
173
|
+
req = param[:required] ? "required" : "optional"
|
|
174
|
+
output << " - #{param[:name]} (#{param[:type]}, #{req}): #{param[:description]}"
|
|
175
|
+
end
|
|
176
|
+
else
|
|
177
|
+
output << " Parameters: none"
|
|
178
|
+
end
|
|
179
|
+
output << ""
|
|
180
|
+
end
|
|
181
|
+
output << "Usage: execute_tool(tool_name: \"<name>\", params: { ... })"
|
|
182
|
+
output.join("\n")
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
@@ -8,13 +8,28 @@ module RailsMcpServer
|
|
|
8
8
|
required(:project_name).filled(:string).description("Name of the project as defined in the projects.yml file (case-sensitive)")
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
QUICK_START = <<~GUIDE
|
|
12
|
+
|
|
13
|
+
Quick Start:
|
|
14
|
+
• Get project overview: execute_tool("project_info")
|
|
15
|
+
• Read a file: execute_ruby("puts read_file('config/routes.rb')")
|
|
16
|
+
• Find files: execute_ruby("puts Dir.glob('app/models/*.rb').join('\\n')")
|
|
17
|
+
• Analyze models: execute_tool("analyze_models", { model_name: "User" })
|
|
18
|
+
• Get routes: execute_tool("get_routes")
|
|
19
|
+
• Get schema: execute_tool("get_schema", { table_name: "users" })
|
|
20
|
+
• Search available tools: search_tools()
|
|
21
|
+
|
|
22
|
+
Helpers in execute_ruby: read_file(path), file_exists?(path), list_files(pattern), project_root
|
|
23
|
+
Note: Always use `puts` in execute_ruby to see output.
|
|
24
|
+
GUIDE
|
|
25
|
+
|
|
11
26
|
def call(project_name:)
|
|
12
27
|
if projects.key?(project_name)
|
|
13
28
|
self.current_project = project_name
|
|
14
29
|
self.active_project_path = File.expand_path(projects[project_name])
|
|
15
30
|
log(:info, "Switched to project: #{project_name} at path: #{active_project_path}")
|
|
16
31
|
|
|
17
|
-
"Switched to project: #{project_name} at path: #{active_project_path}"
|
|
32
|
+
"Switched to project: #{project_name} at path: #{active_project_path}\n#{QUICK_START}"
|
|
18
33
|
else
|
|
19
34
|
log(:warn, "Project not found: #{project_name}")
|
|
20
35
|
|
data/lib/rails_mcp_server.rb
CHANGED
|
@@ -4,29 +4,35 @@ require "forwardable"
|
|
|
4
4
|
require "open3"
|
|
5
5
|
require_relative "rails-mcp-server/version"
|
|
6
6
|
require_relative "rails-mcp-server/config"
|
|
7
|
-
require_relative "rails-mcp-server/extensions/resource_templating"
|
|
8
|
-
require_relative "rails-mcp-server/extensions/server_templating"
|
|
9
7
|
require_relative "rails-mcp-server/utilities/run_process"
|
|
8
|
+
|
|
9
|
+
# MCP Tools (registered with FastMCP)
|
|
10
10
|
require_relative "rails-mcp-server/tools/base_tool"
|
|
11
|
-
require_relative "rails-mcp-server/tools/project_info"
|
|
12
|
-
require_relative "rails-mcp-server/tools/list_files"
|
|
13
|
-
require_relative "rails-mcp-server/tools/get_file"
|
|
14
|
-
require_relative "rails-mcp-server/tools/get_routes"
|
|
15
|
-
require_relative "rails-mcp-server/tools/analyze_models"
|
|
16
|
-
require_relative "rails-mcp-server/tools/get_schema"
|
|
17
|
-
require_relative "rails-mcp-server/tools/analyze_controller_views"
|
|
18
|
-
require_relative "rails-mcp-server/tools/analyze_environment_config"
|
|
19
11
|
require_relative "rails-mcp-server/tools/switch_project"
|
|
20
|
-
require_relative "rails-mcp-server/tools/
|
|
12
|
+
require_relative "rails-mcp-server/tools/search_tools"
|
|
13
|
+
require_relative "rails-mcp-server/tools/execute_tool"
|
|
14
|
+
require_relative "rails-mcp-server/tools/execute_ruby"
|
|
15
|
+
|
|
16
|
+
# Analyzers (internal, invoked via execute_tool)
|
|
17
|
+
require_relative "rails-mcp-server/analyzers/base_analyzer"
|
|
18
|
+
require_relative "rails-mcp-server/analyzers/project_info"
|
|
19
|
+
require_relative "rails-mcp-server/analyzers/list_files"
|
|
20
|
+
require_relative "rails-mcp-server/analyzers/get_file"
|
|
21
|
+
require_relative "rails-mcp-server/analyzers/get_routes"
|
|
22
|
+
require_relative "rails-mcp-server/analyzers/get_schema"
|
|
23
|
+
require_relative "rails-mcp-server/analyzers/analyze_models"
|
|
24
|
+
require_relative "rails-mcp-server/analyzers/analyze_controller_views"
|
|
25
|
+
require_relative "rails-mcp-server/analyzers/analyze_environment_config"
|
|
26
|
+
require_relative "rails-mcp-server/analyzers/load_guide"
|
|
27
|
+
|
|
28
|
+
# Resources
|
|
21
29
|
require_relative "rails-mcp-server/resources/base_resource"
|
|
22
|
-
|
|
23
30
|
require_relative "rails-mcp-server/resources/guide_content_formatter"
|
|
24
31
|
require_relative "rails-mcp-server/resources/guide_error_handler"
|
|
25
32
|
require_relative "rails-mcp-server/resources/guide_file_finder"
|
|
26
33
|
require_relative "rails-mcp-server/resources/guide_loader_template"
|
|
27
34
|
require_relative "rails-mcp-server/resources/guide_manifest_operations"
|
|
28
35
|
require_relative "rails-mcp-server/resources/guide_framework_contract"
|
|
29
|
-
|
|
30
36
|
require_relative "rails-mcp-server/resources/rails_guides_resource"
|
|
31
37
|
require_relative "rails-mcp-server/resources/rails_guides_resources"
|
|
32
38
|
require_relative "rails-mcp-server/resources/stimulus_guides_resource"
|
|
@@ -59,45 +65,5 @@ module RailsMcpServer
|
|
|
59
65
|
|
|
60
66
|
@config.logger.add(log_level, message)
|
|
61
67
|
end
|
|
62
|
-
|
|
63
|
-
# NOTE: This needs to be removed once FastMcp provides official support for URI templating
|
|
64
|
-
# Setup method to initialize FastMcp::Resource extensions
|
|
65
|
-
# Call this after the gem is loaded to enable URI templating
|
|
66
|
-
def setup_resource_extensions!
|
|
67
|
-
Extensions::ResourceExtensionSetup.setup!
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Check if resource extensions are loaded
|
|
71
|
-
def resource_extensions_loaded?
|
|
72
|
-
Extensions::ResourceExtensionSetup.setup_complete?
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# Setup method to initialize FastMcp::Server extensions
|
|
76
|
-
# This is called automatically by resource extension setup
|
|
77
|
-
def setup_server_extensions!
|
|
78
|
-
Extensions::ServerExtensionSetup.setup!
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
# Check if server extensions are loaded
|
|
82
|
-
def server_extensions_loaded?
|
|
83
|
-
Extensions::ServerExtensionSetup.setup_complete?
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# Setup all extensions at once
|
|
87
|
-
def setup_extensions!
|
|
88
|
-
setup_resource_extensions!
|
|
89
|
-
# Server extensions are setup automatically by resource extensions
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# Check if all extensions are loaded
|
|
93
|
-
def extensions_loaded?
|
|
94
|
-
resource_extensions_loaded? && server_extensions_loaded?
|
|
95
|
-
end
|
|
96
68
|
end
|
|
97
|
-
|
|
98
|
-
class Error < StandardError; end
|
|
99
|
-
|
|
100
|
-
# Auto-setup extensions when the module is loaded
|
|
101
|
-
# This ensures extensions are available immediately
|
|
102
|
-
setup_resource_extensions!
|
|
103
69
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails-mcp-server
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mario Alberto Chávez Cárdenas
|
|
@@ -29,42 +29,42 @@ dependencies:
|
|
|
29
29
|
requirements:
|
|
30
30
|
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 1.
|
|
32
|
+
version: 1.6.0
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: 1.
|
|
39
|
+
version: 1.6.0
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: rack
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
44
|
- - "~>"
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: 3.
|
|
46
|
+
version: 3.2.0
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: 3.
|
|
53
|
+
version: 3.2.0
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: puma
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
|
58
58
|
- - "~>"
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version:
|
|
60
|
+
version: 7.1.0
|
|
61
61
|
type: :runtime
|
|
62
62
|
prerelease: false
|
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
65
|
- - "~>"
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version:
|
|
67
|
+
version: 7.1.0
|
|
68
68
|
- !ruby/object:Gem::Dependency
|
|
69
69
|
name: logger
|
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -93,10 +93,53 @@ dependencies:
|
|
|
93
93
|
- - ">="
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
95
|
version: '0'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: minitest
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - "~>"
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '5.25'
|
|
103
|
+
type: :development
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - "~>"
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '5.25'
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: minitest-reporters
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - "~>"
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '1.7'
|
|
117
|
+
type: :development
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - "~>"
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '1.7'
|
|
124
|
+
- !ruby/object:Gem::Dependency
|
|
125
|
+
name: mocha
|
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - "~>"
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: '2.7'
|
|
131
|
+
type: :development
|
|
132
|
+
prerelease: false
|
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - "~>"
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '2.7'
|
|
96
138
|
description: A Ruby implementation of Model Context Protocol server for Rails projects
|
|
97
139
|
email:
|
|
98
140
|
- mario.chavez@gmail.com
|
|
99
141
|
executables:
|
|
142
|
+
- rails-mcp-config
|
|
100
143
|
- rails-mcp-server
|
|
101
144
|
- rails-mcp-server-download-resources
|
|
102
145
|
- rails-mcp-setup-claude
|
|
@@ -107,13 +150,23 @@ files:
|
|
|
107
150
|
- LICENSE.txt
|
|
108
151
|
- README.md
|
|
109
152
|
- config/resources.yml
|
|
153
|
+
- 'docs/AGENT.md '
|
|
110
154
|
- docs/RESOURCES.md
|
|
155
|
+
- exe/rails-mcp-config
|
|
111
156
|
- exe/rails-mcp-server
|
|
112
157
|
- exe/rails-mcp-server-download-resources
|
|
113
158
|
- exe/rails-mcp-setup-claude
|
|
159
|
+
- lib/rails-mcp-server/analyzers/analyze_controller_views.rb
|
|
160
|
+
- lib/rails-mcp-server/analyzers/analyze_environment_config.rb
|
|
161
|
+
- lib/rails-mcp-server/analyzers/analyze_models.rb
|
|
162
|
+
- lib/rails-mcp-server/analyzers/base_analyzer.rb
|
|
163
|
+
- lib/rails-mcp-server/analyzers/get_file.rb
|
|
164
|
+
- lib/rails-mcp-server/analyzers/get_routes.rb
|
|
165
|
+
- lib/rails-mcp-server/analyzers/get_schema.rb
|
|
166
|
+
- lib/rails-mcp-server/analyzers/list_files.rb
|
|
167
|
+
- lib/rails-mcp-server/analyzers/load_guide.rb
|
|
168
|
+
- lib/rails-mcp-server/analyzers/project_info.rb
|
|
114
169
|
- lib/rails-mcp-server/config.rb
|
|
115
|
-
- lib/rails-mcp-server/extensions/resource_templating.rb
|
|
116
|
-
- lib/rails-mcp-server/extensions/server_templating.rb
|
|
117
170
|
- lib/rails-mcp-server/helpers/resource_base.rb
|
|
118
171
|
- lib/rails-mcp-server/helpers/resource_downloader.rb
|
|
119
172
|
- lib/rails-mcp-server/helpers/resource_importer.rb
|
|
@@ -134,17 +187,11 @@ files:
|
|
|
134
187
|
- lib/rails-mcp-server/resources/stimulus_guides_resources.rb
|
|
135
188
|
- lib/rails-mcp-server/resources/turbo_guides_resource.rb
|
|
136
189
|
- lib/rails-mcp-server/resources/turbo_guides_resources.rb
|
|
137
|
-
- lib/rails-mcp-server/tools/analyze_controller_views.rb
|
|
138
|
-
- lib/rails-mcp-server/tools/analyze_environment_config.rb
|
|
139
|
-
- lib/rails-mcp-server/tools/analyze_models.rb
|
|
140
190
|
- lib/rails-mcp-server/tools/base_tool.rb
|
|
141
|
-
- lib/rails-mcp-server/tools/
|
|
191
|
+
- lib/rails-mcp-server/tools/execute_ruby.rb
|
|
192
|
+
- lib/rails-mcp-server/tools/execute_tool.rb
|
|
142
193
|
- lib/rails-mcp-server/tools/get_model.rb
|
|
143
|
-
- lib/rails-mcp-server/tools/
|
|
144
|
-
- lib/rails-mcp-server/tools/get_schema.rb
|
|
145
|
-
- lib/rails-mcp-server/tools/list_files.rb
|
|
146
|
-
- lib/rails-mcp-server/tools/load_guide.rb
|
|
147
|
-
- lib/rails-mcp-server/tools/project_info.rb
|
|
194
|
+
- lib/rails-mcp-server/tools/search_tools.rb
|
|
148
195
|
- lib/rails-mcp-server/tools/switch_project.rb
|
|
149
196
|
- lib/rails-mcp-server/utilities/run_process.rb
|
|
150
197
|
- lib/rails-mcp-server/version.rb
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
module RailsMcpServer
|
|
2
|
-
module Extensions
|
|
3
|
-
# Extension module to add URI templating capabilities to FastMcp::Resource
|
|
4
|
-
# Uses module prepending for clean method override behavior
|
|
5
|
-
module ResourceTemplating
|
|
6
|
-
# Class methods to be prepended to the singleton class
|
|
7
|
-
module ClassMethods
|
|
8
|
-
attr_reader :template_params
|
|
9
|
-
|
|
10
|
-
def variabilized_uri(params = {})
|
|
11
|
-
addressable_template.partial_expand(params).pattern
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def addressable_template
|
|
15
|
-
@addressable_template ||= Addressable::Template.new(uri)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def template_variables
|
|
19
|
-
addressable_template.variables
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def templated?
|
|
23
|
-
template_variables.any?
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def non_templated?
|
|
27
|
-
!templated?
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def match(uri)
|
|
31
|
-
addressable_template.match(uri)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def initialize_from_uri(uri)
|
|
35
|
-
new(params_from_uri(uri))
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def params_from_uri(uri)
|
|
39
|
-
match(uri).mapping.transform_keys(&:to_sym)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def instance(uri = self.uri)
|
|
43
|
-
@instances ||= {}
|
|
44
|
-
@instances[uri] ||= begin
|
|
45
|
-
resource_class = Class.new(self)
|
|
46
|
-
params = params_from_uri(uri)
|
|
47
|
-
resource_class.instance_variable_set(:@params, params)
|
|
48
|
-
|
|
49
|
-
resource_class.define_singleton_method(:instance) do
|
|
50
|
-
@instance ||= begin
|
|
51
|
-
instance = new
|
|
52
|
-
instance.instance_variable_set(:@params, params)
|
|
53
|
-
instance
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
resource_class.instance
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def params
|
|
62
|
-
@params || {}
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def name
|
|
66
|
-
return resource_name if resource_name
|
|
67
|
-
super
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def metadata
|
|
71
|
-
if templated?
|
|
72
|
-
{
|
|
73
|
-
uriTemplate: uri,
|
|
74
|
-
name: resource_name,
|
|
75
|
-
description: description,
|
|
76
|
-
mimeType: mime_type
|
|
77
|
-
}.compact
|
|
78
|
-
else
|
|
79
|
-
super
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# Instance methods to be prepended
|
|
85
|
-
module InstanceMethods
|
|
86
|
-
def initialize
|
|
87
|
-
@params = self.class.params
|
|
88
|
-
super if defined?(super)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def params
|
|
92
|
-
@params || self.class.params
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def name
|
|
96
|
-
self.class.resource_name
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
# Called when this module is prepended to a class
|
|
101
|
-
def self.prepended(base)
|
|
102
|
-
base.singleton_class.prepend(ClassMethods)
|
|
103
|
-
base.prepend(InstanceMethods)
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# Main setup class for resource extensions
|
|
108
|
-
class ResourceExtensionSetup
|
|
109
|
-
class << self
|
|
110
|
-
def setup!
|
|
111
|
-
return if @setup_complete
|
|
112
|
-
|
|
113
|
-
ensure_dependencies_loaded!
|
|
114
|
-
apply_extensions!
|
|
115
|
-
|
|
116
|
-
@setup_complete = true
|
|
117
|
-
RailsMcpServer.log(:info, "FastMcp::Resource extensions loaded successfully")
|
|
118
|
-
rescue => e
|
|
119
|
-
RailsMcpServer.log(:error, "Failed to setup resource extensions: #{e.message}")
|
|
120
|
-
raise
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def reset!
|
|
124
|
-
@setup_complete = false
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
def setup_complete?
|
|
128
|
-
@setup_complete || false
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
private
|
|
132
|
-
|
|
133
|
-
def ensure_dependencies_loaded!
|
|
134
|
-
# Check that FastMcp::Resource exists
|
|
135
|
-
unless defined?(FastMcp::Resource)
|
|
136
|
-
begin
|
|
137
|
-
require "fast-mcp"
|
|
138
|
-
rescue LoadError => e
|
|
139
|
-
raise LoadError, "fast-mcp gem is required but not available. Ensure it's in your Gemfile: #{e.message}"
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
# Verify the expected interface exists
|
|
144
|
-
unless FastMcp::Resource.respond_to?(:uri)
|
|
145
|
-
raise "FastMcp::Resource doesn't have expected interface. Check fast-mcp gem version."
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
# Load addressable template dependency
|
|
149
|
-
begin
|
|
150
|
-
require "addressable/template"
|
|
151
|
-
rescue LoadError => e
|
|
152
|
-
raise LoadError, "addressable gem is required for URI templating: #{e.message}"
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
# Optional: Version checking
|
|
156
|
-
if defined?(FastMcp::VERSION)
|
|
157
|
-
version = Gem::Version.new(FastMcp::VERSION)
|
|
158
|
-
minimum_version = Gem::Version.new("1.4.0")
|
|
159
|
-
|
|
160
|
-
if version < minimum_version
|
|
161
|
-
RailsMcpServer.log(:warn, "FastMcp version #{FastMcp::VERSION} detected. Extensions tested with #{minimum_version}+")
|
|
162
|
-
end
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
def apply_extensions!
|
|
167
|
-
# Apply extensions to FastMcp::Resource
|
|
168
|
-
FastMcp::Resource.prepend(ResourceTemplating)
|
|
169
|
-
|
|
170
|
-
# Also ensure our BaseResource gets the extensions
|
|
171
|
-
if defined?(RailsMcpServer::BaseResource)
|
|
172
|
-
# BaseResource already inherits from FastMcp::Resource, so it gets extensions automatically
|
|
173
|
-
RailsMcpServer.log(:debug, "BaseResource will inherit templating extensions")
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
# Setup server extensions as well
|
|
177
|
-
RailsMcpServer::Extensions::ServerExtensionSetup.setup!
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
end
|