Module: Esp::McpInstaller
- Defined in:
- lib/esp/mcp_installer.rb
Overview
Registers ‘esp mcp serve` with a local MCP client by merging a stdio server entry into that client’s config JSON, preserving any other servers and top-level keys already there.
Deliberately kept out of Esp::Operations: rewriting a user’s AI-client config is a CLI/operator action, never something an agent should be able to drive over MCP itself.
The two clients share the ‘mcpServers` / stdio shape and an identical server entry; they differ only in where the config lives. The command is an absolute path to `bin/esp` — note `$CLAUDE_PROJECT_DIR` looks tempting for a portable Claude Code entry but is unset when `.mcp.json` is parsed (it’s a hooks-only variable), so the server silently fails to launch. The cost is a machine-specific path in the config.
Step-22.5 migration: the install action quietly drops any existing entry named ‘mw` (the historical pre-rename name) when it writes the new `esp` entry, so a re-install after the rename leaves a single, current entry.
Defined Under Namespace
Classes: Result
Constant Summary collapse
- SERVER_NAME =
'esp'.freeze
- LEGACY_NAME =
'mw'.freeze
- CLIENTS =
{ 'claude-code' => { label: 'Claude Code', path: -> { File.join(Esp::ROOT, '.mcp.json') } }, 'claude-desktop' => { label: 'Claude Desktop', path: -> { File.('~/Library/Application Support/Claude/claude_desktop_config.json') } } }.freeze
Class Method Summary collapse
- .clients ⇒ Object
- .entry_for(client) ⇒ Object
-
.install(client, name: SERVER_NAME, path: nil) ⇒ Object
Merge the entry into the client’s config.
Class Method Details
.clients ⇒ Object
42 43 44 |
# File 'lib/esp/mcp_installer.rb', line 42 def clients CLIENTS.keys end |
.entry_for(client) ⇒ Object
46 47 48 49 |
# File 'lib/esp/mcp_installer.rb', line 46 def entry_for(client) (client) # validate { 'type' => 'stdio', 'command' => File.join(Esp::ROOT, 'bin', 'esp'), 'args' => %w[mcp serve] } end |
.install(client, name: SERVER_NAME, path: nil) ⇒ Object
Merge the entry into the client’s config. path overrides the default location (tests point it at a tmpfile). Returns a Result whose :action is :created / :updated / :unchanged. The :migrated flag is true when this install removed a legacy ‘mw` entry pointing at the same install.
55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/esp/mcp_installer.rb', line 55 def install(client, name: SERVER_NAME, path: nil) = (client) config_path = path || [:path].call entry = entry_for(client) config = read_config(config_path) servers = (config['mcpServers'] ||= {}) migrated = migrate_legacy_entry(servers, name) action = action_for(servers[name], entry) servers[name] = entry write_config(config_path, config) unless action == :unchanged && !migrated Result.new(client: client, label: [:label], config_path: config_path, name: name, action: action, entry: entry, migrated: migrated) end |