Class: RailsAiContext::McpConfigGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/rails_ai_context/mcp_config_generator.rb

Overview

Generates per-tool MCP config files so each AI tool auto-discovers the MCP server.

Each tool has its own config file format:

Claude Code  → .mcp.json          (mcpServers key)
Cursor       → .cursor/mcp.json   (mcpServers key)
VS Code      → .vscode/mcp.json   (servers key)
OpenCode     → opencode.json      (mcp key, type: "local", command as array)
Codex CLI    → .codex/config.toml (TOML, [mcp_servers.NAME] section)

Constant Summary collapse

TOOL_CONFIGS =
{
  claude:   { path: ".mcp.json",          root_key: "mcpServers", format: :mcp_json },
  cursor:   { path: ".cursor/mcp.json",   root_key: "mcpServers", format: :mcp_json },
  copilot:  { path: ".vscode/mcp.json",   root_key: "servers",    format: :vscode_json },
  opencode: { path: "opencode.json",      root_key: "mcp",        format: :opencode_json },
  codex:    { path: ".codex/config.toml",  root_key: nil,          format: :codex_toml }
}.freeze
SERVER_NAME =
"rails-ai-context"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tools:, output_dir:, standalone: nil, tool_mode: :mcp) ⇒ McpConfigGenerator

Returns a new instance of McpConfigGenerator.

Parameters:

  • tools (Array<Symbol>)

    selected AI tool keys (e.g. [:claude, :cursor])

  • output_dir (String)

    project root path

  • standalone (Boolean, nil) (defaults to: nil)

    true writes bare rails-ai-context commands, false writes bundle exec commands. nil (the default) detects the install mode from the app's Gemfile.lock so every entry point that writes MCP configs (standalone CLI init, rake task, Rails generator) converges on identical config content for the same app instead of rewriting each other's command form on alternate runs.

  • tool_mode (Symbol) (defaults to: :mcp)

    :mcp or :cli



37
38
39
40
41
42
# File 'lib/rails_ai_context/mcp_config_generator.rb', line 37

def initialize(tools:, output_dir:, standalone: nil, tool_mode: :mcp)
  @tools = Array(tools).map(&:to_sym)
  @output_dir = output_dir
  @standalone = standalone.nil? ? InstallMode.standalone? : standalone
  @tool_mode = tool_mode
end

Class Method Details

.remove(tools:, output_dir:) ⇒ Array<String>

Removes only the rails-ai-context entry from each tool's MCP config file, preserving other servers. Deletes the file only if no other entries remain.

Parameters:

  • tools (Array<Symbol>)

    tool keys to remove MCP entries from

  • output_dir (String)

    project root path

Returns:

  • (Array<String>)

    paths that were modified or deleted



247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/rails_ai_context/mcp_config_generator.rb', line 247

def self.remove(tools:, output_dir:)
  cleaned = []
  Array(tools).map(&:to_sym).each do |tool|
    config = TOOL_CONFIGS[tool]
    next unless config

    path = File.join(output_dir, config[:path])
    next unless File.exist?(path)

    if config[:format] == :codex_toml
      cleaned << path if remove_toml_entry(path)
    else
      root_key = config[:root_key]
      cleaned << path if remove_json_entry(path, root_key)
    end
  end
  cleaned
end

Instance Method Details

#callHash

Returns { written: [paths], skipped: [paths] }.

Returns:

  • (Hash)

    { written: [paths], skipped: [paths] }



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/rails_ai_context/mcp_config_generator.rb', line 45

def call
  return { written: [], skipped: [] } if @tool_mode == :cli

  written = []
  skipped = []

  @tools.each do |tool|
    config = TOOL_CONFIGS[tool]
    next unless config

    path = File.join(@output_dir, config[:path])
    result = generate_for(tool, path, config)
    case result
    when :written then written << path
    when :skipped then skipped << path
    end
  end

  { written: written, skipped: skipped }
end