RegisterMcpStep built the registration URL from ctx.ExternalMcpPort, which the Update pipeline never repopulates from the existing installation, so any update silently re-registered the wizard default (47822) even when worker.config.json had a different port configured. InstallerWorkerConfig was also missing external_mcp_port entirely, so the installer had no way to read it back. Port 0 (external listener disabled) now skips registration instead of pointing Claude at 127.0.0.1:0/mcp.
67 lines
2.7 KiB
C#
67 lines
2.7 KiB
C#
using ClaudeDo.Installer.Core;
|
|
|
|
namespace ClaudeDo.Installer.Steps;
|
|
|
|
public sealed class RegisterMcpStep : IInstallStep
|
|
{
|
|
private const string ServerName = "claudedo";
|
|
private readonly Func<InstallerWorkerConfig> _loadWorkerConfig;
|
|
|
|
public RegisterMcpStep(Func<InstallerWorkerConfig>? loadWorkerConfig = null)
|
|
{
|
|
_loadWorkerConfig = loadWorkerConfig ?? InstallerWorkerConfig.Load;
|
|
}
|
|
|
|
public string Name => "Register MCP with Claude";
|
|
|
|
// Resolves the URL from the persisted worker.config.json rather than ctx.ExternalMcpPort:
|
|
// an Update run never repopulates ctx from the existing installation, so ctx would still
|
|
// hold the wizard default (47822) even when the installed config has a different port.
|
|
// Returns null when the port is 0 — WorkerConfig treats that as "external listener off",
|
|
// so registering a URL against it would just point Claude at nothing.
|
|
public static string? ResolveUrl(InstallerWorkerConfig cfg) =>
|
|
cfg.ExternalMcpPort == 0 ? null : $"http://127.0.0.1:{cfg.ExternalMcpPort}/mcp";
|
|
|
|
public async Task<StepResult> ExecuteAsync(InstallContext ctx, IProgress<string> progress, CancellationToken ct)
|
|
{
|
|
if (!ctx.RegisterMcpWithClaude)
|
|
{
|
|
progress.Report("Skipped (not selected)");
|
|
return StepResult.Ok();
|
|
}
|
|
|
|
var url = ResolveUrl(_loadWorkerConfig());
|
|
if (url is null)
|
|
{
|
|
progress.Report("Skipped (external_mcp_port is 0 — the external MCP listener is disabled).");
|
|
return StepResult.Ok();
|
|
}
|
|
|
|
// Drop any prior registration first so a re-run (e.g. update, changed port)
|
|
// overwrites cleanly instead of erroring on a duplicate name.
|
|
progress.Report($"Removing existing '{ServerName}' MCP registration (if any)...");
|
|
await ProcessRunner.RunAsync(ctx.ClaudeBin, $"mcp remove --scope user {ServerName}", null, progress, ct);
|
|
|
|
progress.Report($"Registering '{ServerName}' MCP server at {url}...");
|
|
var (exit, output) = await ProcessRunner.RunAsync(
|
|
ctx.ClaudeBin,
|
|
$"mcp add --transport http --scope user {ServerName} {url}",
|
|
null, progress, ct);
|
|
|
|
// Non-fatal: a missing/old Claude CLI must never block the install. Surface the
|
|
// manual command so the user can register it themselves later.
|
|
if (exit != 0)
|
|
{
|
|
progress.Report(
|
|
$"Could not register MCP automatically (claude exited {exit}). " +
|
|
$"Run manually: claude mcp add --transport http --scope user {ServerName} {url}");
|
|
}
|
|
else
|
|
{
|
|
progress.Report("MCP server registered with Claude.");
|
|
}
|
|
|
|
return StepResult.Ok();
|
|
}
|
|
}
|