feat(installer): optionally register ClaudeDo MCP server with Claude
Add an install step and welcome-page opt-in that registers the ClaudeDo external MCP server with the Claude CLI. Failures are non-fatal and surface the manual command so a missing or old CLI never blocks the install. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -209,6 +209,8 @@ public partial class App : Application
|
|||||||
sc.AddSingleton<IInstallStep>(sp => sp.GetRequiredService<DownloadAndExtractStep>());
|
sc.AddSingleton<IInstallStep>(sp => sp.GetRequiredService<DownloadAndExtractStep>());
|
||||||
sc.AddSingleton<IInstallStep, WriteConfigStep>();
|
sc.AddSingleton<IInstallStep, WriteConfigStep>();
|
||||||
sc.AddSingleton<IInstallStep, InitDatabaseStep>();
|
sc.AddSingleton<IInstallStep, InitDatabaseStep>();
|
||||||
|
sc.AddSingleton<RegisterMcpStep>();
|
||||||
|
sc.AddSingleton<IInstallStep>(sp => sp.GetRequiredService<RegisterMcpStep>());
|
||||||
sc.AddSingleton<RegisterAutostartStep>();
|
sc.AddSingleton<RegisterAutostartStep>();
|
||||||
sc.AddSingleton<IInstallStep>(sp => sp.GetRequiredService<RegisterAutostartStep>());
|
sc.AddSingleton<IInstallStep>(sp => sp.GetRequiredService<RegisterAutostartStep>());
|
||||||
sc.AddSingleton<IInstallStep, CreateShortcutsStep>();
|
sc.AddSingleton<IInstallStep, CreateShortcutsStep>();
|
||||||
|
|||||||
@@ -32,4 +32,8 @@ public sealed class InstallContext
|
|||||||
|
|
||||||
// InstallPage
|
// InstallPage
|
||||||
public bool CreateDesktopShortcut { get; set; } = true;
|
public bool CreateDesktopShortcut { get; set; } = true;
|
||||||
|
|
||||||
|
// WelcomePage — register the external MCP endpoint with the Claude CLI.
|
||||||
|
public bool RegisterMcpWithClaude { get; set; } = true;
|
||||||
|
public int ExternalMcpPort { get; set; } = 47_822;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ public partial class InstallPageViewModel : ObservableObject, IInstallerPage
|
|||||||
_serviceProvider.GetRequiredService<DownloadAndExtractStep>(),
|
_serviceProvider.GetRequiredService<DownloadAndExtractStep>(),
|
||||||
// Migrates the legacy service away and (re)registers the logon task.
|
// Migrates the legacy service away and (re)registers the logon task.
|
||||||
_serviceProvider.GetRequiredService<RegisterAutostartStep>(),
|
_serviceProvider.GetRequiredService<RegisterAutostartStep>(),
|
||||||
|
_serviceProvider.GetRequiredService<RegisterMcpStep>(),
|
||||||
_serviceProvider.GetRequiredService<StartWorkerStep>(),
|
_serviceProvider.GetRequiredService<StartWorkerStep>(),
|
||||||
_serviceProvider.GetRequiredService<WriteInstallManifestStep>(),
|
_serviceProvider.GetRequiredService<WriteInstallManifestStep>(),
|
||||||
// Refresh the bundled uninstaller exe + Add/Remove-Programs version so a
|
// Refresh the bundled uninstaller exe + Add/Remove-Programs version so a
|
||||||
|
|||||||
@@ -32,6 +32,14 @@
|
|||||||
<TextBlock Text="{Binding InstallError}" Foreground="{StaticResource ErrorBrush}" FontSize="11"
|
<TextBlock Text="{Binding InstallError}" Foreground="{StaticResource ErrorBrush}" FontSize="11"
|
||||||
Visibility="{Binding InstallError, Converter={StaticResource NullToCollapsedConverter}}"/>
|
Visibility="{Binding InstallError, Converter={StaticResource NullToCollapsedConverter}}"/>
|
||||||
|
|
||||||
|
<CheckBox Content="Register MCP server with Claude"
|
||||||
|
IsChecked="{Binding RegisterMcp}"
|
||||||
|
Margin="0,24,0,0"/>
|
||||||
|
<TextBlock Text="Runs 'claude mcp add' so Claude can view and manage your ClaudeDo tasks. You can change this later."
|
||||||
|
TextWrapping="Wrap" FontSize="11"
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}"
|
||||||
|
Margin="0,4,0,0"/>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ public partial class WelcomePageViewModel : ObservableObject, IInstallerPage
|
|||||||
[ObservableProperty] private string _heading = "Install ClaudeDo";
|
[ObservableProperty] private string _heading = "Install ClaudeDo";
|
||||||
[ObservableProperty] private string _subheading = "Set the installation directory and continue.";
|
[ObservableProperty] private string _subheading = "Set the installation directory and continue.";
|
||||||
[ObservableProperty] private bool _installDirEditable = true;
|
[ObservableProperty] private bool _installDirEditable = true;
|
||||||
|
[ObservableProperty] private bool _registerMcp = true;
|
||||||
|
|
||||||
public WelcomePageViewModel(InstallContext context)
|
public WelcomePageViewModel(InstallContext context)
|
||||||
{
|
{
|
||||||
@@ -62,6 +63,7 @@ public partial class WelcomePageViewModel : ObservableObject, IInstallerPage
|
|||||||
public Task ApplyAsync()
|
public Task ApplyAsync()
|
||||||
{
|
{
|
||||||
_context.InstallDirectory = InstallDirectory;
|
_context.InstallDirectory = InstallDirectory;
|
||||||
|
_context.RegisterMcpWithClaude = RegisterMcp;
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
47
src/ClaudeDo.Installer/Steps/RegisterMcpStep.cs
Normal file
47
src/ClaudeDo.Installer/Steps/RegisterMcpStep.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using ClaudeDo.Installer.Core;
|
||||||
|
|
||||||
|
namespace ClaudeDo.Installer.Steps;
|
||||||
|
|
||||||
|
public sealed class RegisterMcpStep : IInstallStep
|
||||||
|
{
|
||||||
|
private const string ServerName = "claudedo";
|
||||||
|
|
||||||
|
public string Name => "Register MCP with Claude";
|
||||||
|
|
||||||
|
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 = $"http://127.0.0.1:{ctx.ExternalMcpPort}/mcp";
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user