Files
ClaudeDo/src/ClaudeDo.Installer/Steps/RegisterMcpStep.cs
T
mika kuns 3462ca1355 fix(installer): read the persisted MCP port on update instead of the wizard default
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.
2026-08-06 11:04:38 +02:00

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();
}
}