A second WebApplication runs the external MCP server on its own port (default 47822) so it can expose a different tool set under different auth than the internal /mcp endpoint. Shared singletons (config, broadcaster, queue, db factory) are injected by instance so both apps share runtime state. ExternalMcpAuthMiddleware enforces an optional X-ClaudeDo-Key header; loopback-only trust when no key is configured. Tools: ListTaskLists, ListTasks, GetTask, AddTask, UpdateTaskStatus, RunTaskNow, CancelTask. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
79 lines
2.7 KiB
C#
79 lines
2.7 KiB
C#
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using ClaudeDo.Data;
|
|
|
|
namespace ClaudeDo.Worker.Config;
|
|
|
|
public sealed class WorkerConfig
|
|
{
|
|
[JsonPropertyName("db_path")]
|
|
public string DbPath { get; set; } = "~/.todo-app/todo.db";
|
|
|
|
[JsonPropertyName("sandbox_root")]
|
|
public string SandboxRoot { get; set; } = "~/.todo-app/sandbox";
|
|
|
|
[JsonPropertyName("log_root")]
|
|
public string LogRoot { get; set; } = "~/.todo-app/logs";
|
|
|
|
/// <summary>"sibling" → place worktrees next to the target repo; "central" → under <see cref="CentralWorktreeRoot"/>.</summary>
|
|
[JsonPropertyName("worktree_root_strategy")]
|
|
public string WorktreeRootStrategy { get; set; } = "sibling";
|
|
|
|
[JsonPropertyName("central_worktree_root")]
|
|
public string CentralWorktreeRoot { get; set; } = "~/.todo-app/worktrees";
|
|
|
|
[JsonPropertyName("queue_backstop_interval_ms")]
|
|
public int QueueBackstopIntervalMs { get; set; } = 30_000;
|
|
|
|
[JsonPropertyName("signalr_port")]
|
|
public int SignalRPort { get; set; } = 47_821;
|
|
|
|
[JsonPropertyName("claude_bin")]
|
|
public string ClaudeBin { get; set; } = "claude";
|
|
|
|
/// <summary>Port for the external MCP endpoint. 0 disables the external listener entirely.</summary>
|
|
[JsonPropertyName("external_mcp_port")]
|
|
public int ExternalMcpPort { get; set; } = 47_822;
|
|
|
|
/// <summary>Optional API key clients must pass via X-ClaudeDo-Key header. Null/empty = loopback trust only.</summary>
|
|
[JsonPropertyName("external_mcp_api_key")]
|
|
public string? ExternalMcpApiKey { get; set; }
|
|
|
|
public static string DefaultConfigPath =>
|
|
Path.Combine(Paths.AppDataRoot(), "worker.config.json");
|
|
|
|
/// <summary>
|
|
/// Loads the config from <paramref name="path"/> (defaults to <see cref="DefaultConfigPath"/>).
|
|
/// Missing file → returns defaults. Resolves all path-typed fields to absolute paths.
|
|
/// </summary>
|
|
public static WorkerConfig Load(string? path = null)
|
|
{
|
|
path ??= DefaultConfigPath;
|
|
|
|
WorkerConfig cfg;
|
|
if (File.Exists(path))
|
|
{
|
|
var json = File.ReadAllText(path);
|
|
cfg = JsonSerializer.Deserialize<WorkerConfig>(json, JsonOpts)
|
|
?? throw new InvalidOperationException($"Failed to parse {path}");
|
|
}
|
|
else
|
|
{
|
|
cfg = new WorkerConfig();
|
|
}
|
|
|
|
cfg.DbPath = Paths.Expand(cfg.DbPath);
|
|
cfg.SandboxRoot = Paths.Expand(cfg.SandboxRoot);
|
|
cfg.LogRoot = Paths.Expand(cfg.LogRoot);
|
|
cfg.CentralWorktreeRoot = Paths.Expand(cfg.CentralWorktreeRoot);
|
|
|
|
return cfg;
|
|
}
|
|
|
|
private static readonly JsonSerializerOptions JsonOpts = new()
|
|
{
|
|
ReadCommentHandling = JsonCommentHandling.Skip,
|
|
AllowTrailingCommas = true,
|
|
};
|
|
}
|