feat(pdu): add PDU MCP server for AI-driven SNMP power control

New ETTerms.PduMcp stdio MCP server lets AI agents (Kiro/Claude CLI)
control SNMP PDU outlets directly. Unlike serial, SNMP is non-exclusive
so it talks to the PDU directly without bridging through the GUI.

Tools: pdu_connect / pdu_list / pdu_set_port / pdu_get_port / pdu_status
/ pdu_power_cycle / pdu_disconnect.

- McpRegistrar now registers both etterms-serial and etterms-pdu in one click
- Settings -> AI MCP shows both server paths
- publish target (PublishMcpServers) bundles both MCP servers
- bump version to 0.3.0, update About changelog, ARCHITECTURE.md, CLAUDE.md
This commit is contained in:
2026-06-08 10:12:15 +08:00
parent 89ec175669
commit 02f4ac49e8
12 changed files with 434 additions and 60 deletions
+31
View File
@@ -0,0 +1,31 @@
using System.Collections.Concurrent;
namespace ETTerms.PduMcp;
/// <summary>
/// Process-wide registry of connected PDUs, keyed by IP. The MCP server is long-lived,
/// so connections established by <c>pdu_connect</c> persist across tool calls.
/// </summary>
public sealed class PduRegistry
{
public static readonly PduRegistry Instance = new();
/// <summary>iPoMan II/III outlet count.</summary>
public const int PortCount = 12;
private readonly ConcurrentDictionary<string, PduController> _pdus = new(StringComparer.OrdinalIgnoreCase);
public PduController GetOrAdd(string ip) =>
_pdus.GetOrAdd(ip.Trim(), static k => new PduController(k));
public bool TryGet(string ip, out PduController pdu) =>
_pdus.TryGetValue(ip.Trim(), out pdu!);
public bool Remove(string ip)
{
if (_pdus.TryRemove(ip.Trim(), out var pdu)) { pdu.Dispose(); return true; }
return false;
}
public IReadOnlyCollection<string> ConnectedIps => _pdus.Keys.ToArray();
}