Skip to the content.

Task 100 — MCP hot-reload (IConfigReload)

Phase: 8 Status: done Owner: — Slug: mcp-hot-reload Studio Stage: 6

Goal

Реализовать McpClientService : IConfigReload, чтобы изменения MCP-серверов через PATCH /api/config применялись без рестарта процесса. POST /api/mcp/servers/reload переустанавливает соединения с обновлённым конфигом.

Acceptance criteria

Sub-tasks

Dependencies

Scope / Likely files

src/agent/Mcp/McpClientService.cs, src/agent/Hercules.WebApi/Program.cs, src/agent/Tools/Registry/IToolRegistryService.cs, src/agent/Tools/Registry/ToolRegistryService.cs, tests/Hercules.Agent.Tests/Mcp/McpClientServiceTests.cs, tests/Hercules.Agent.Tests/Tools/Registry/ToolRegistryServiceTests.cs

Implementation notes

Round 1 (this tick) — completed

MCP hot-reload для PATCH /api/config (Phase 8 task_100, Studio Stage 6).

Архитектурное изменение McpClientService:

Diff-логика ReloadFromConfigAsync(McpConfig newConfig):

  1. Строим Dictionary<string, McpServerConfig> желаемого состояния по Name.
  2. Remove: для каждого сервера в _connections, которого нет в newConfig — TryRemove + DisposeAsync (best effort) + UnregisterServerTools(name).
  3. Update: для каждого сервера, который есть и в _connections, и в newConfig, но ServerConfigChanged(existing.Config, desired) (сравниваем Transport, Command, Endpoint, Enabled, HealthCheckEnabled, TimeoutSeconds, Args) — disconnect + unregister tools + reconnect.
  4. Add: для каждого сервера, который есть в newConfig, но не в _connections — ConnectServerAsync.
    • Все операции сериализованы через SemaphoreSlim _reloadLock — параллельные PATCH-и + manual reload не race-ятся.
    • McpServerConnection теперь хранит оригинальный McpServerConfig — нужен для ServerConfigChanged.

Tool unregistration (новый API):

DI wiring (Program.cs):

Тесты — 13 McpClientService + 4 ToolRegistryService = 17 новых/обновлённых:

Round 1 — validation

Notes