ClawdDotNet als MCP-Server: durch dieselben Pruefstellen (#713)

Eine fremde Laufzeit oder der eigene Claude Code des Menschen spricht per MCP
mit der Instanz. Der Server bietet genau die Werkzeuge des Agenten hinter dem
Token an; jeder Aufruf laeuft durch die Engine wie ein Aufruf des eigenen
Modells (Rechtetor, Vorpruefung, Werkzeugrechte, Staging, Audit mit Quelle
mcp, Rahmung), und der Aufrufer gilt als Leser fremder Inhalte (#715).

Eigener Socket mcp.sock statt manager.sock: ueber den Instanz-Socket antwortet
die Verwaltung ohne Token, ueber mcp.sock gibt es nur /mcp und nur mit
MCP-Token. Lauf-Tokens nur im Speicher, enden mit dem Lauf; persoenliche Tokens
(hoechstens 90 Tage, widerrufbar, nur als SHA-256) in der Agentenansicht.
Transporte: HTTP auf mcp.sock, Weboberflaeche und Manager (/mcp/{instanz}),
stdio ueber ClawdDotNet --mcp-bridge (Token nur aus Umgebung oder 0600-Datei).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-09-25 17:28:47 +02:00
co-authored by Claude Opus 5.5
parent b5f65b8cf7
commit cb46289bb6
25 changed files with 1672 additions and 12 deletions
@@ -28,6 +28,29 @@ public static class InstanceEndpoint
/// <summary>Der Name der Socketdatei im Instanzverzeichnis.</summary>
public const string SocketFileName = "manager.sock";
/// <summary>
/// #713: The MCP server's own socket. <b>Not</b> <see cref="SocketFileName"/>: over that
/// one the whole administration answers without a token, because the file system is the
/// boundary there. A foreign runtime must never be handed it — this socket serves only
/// <c>/mcp</c>, and only with a run's token. It is the one the sandbox (#654) mounts.
/// </summary>
public const string McpSocketFileName = "mcp.sock";
/// <summary>#713: The MCP socket of this instance.</summary>
public static string McpSocketPath(string instanceDirectory)
=> Path.Combine(Path.GetFullPath(instanceDirectory), McpSocketFileName);
/// <summary>#713: The MCP pipe of this instance (Windows).</summary>
public static string McpPipeName(string instanceDirectory)
=> "clawd-mcp-" + Fingerprint(instanceDirectory);
/// <summary>#713: A client for the MCP socket — what the stdio bridge speaks through.</summary>
public static HttpClient CreateMcpClient(string instanceDirectory)
=> new(CreateHandler(McpSocketPath(instanceDirectory), McpPipeName(instanceDirectory)))
{
BaseAddress = new Uri("http://instanz")
};
/// <summary>
/// Ob auf dieser Maschine eine Named Pipe statt eines Unix-Domain-Sockets benutzt
/// wird.
@@ -84,8 +107,12 @@ public static class InstanceEndpoint
{
if (OperatingSystem.IsWindows()) return;
var path = SocketPath(instanceDirectory);
ClearStaleFile(SocketPath(instanceDirectory));
ClearStaleFile(McpSocketPath(instanceDirectory));
}
private static void ClearStaleFile(string path)
{
try
{
if (File.Exists(path)) File.Delete(path);
@@ -111,7 +138,13 @@ public static class InstanceEndpoint
{
if (OperatingSystem.IsWindows()) return;
var path = SocketPath(instanceDirectory);
RestrictFile(SocketPath(instanceDirectory));
RestrictFile(McpSocketPath(instanceDirectory));
}
private static void RestrictFile(string path)
{
if (OperatingSystem.IsWindows()) return;
try
{
@@ -140,10 +173,13 @@ public static class InstanceEndpoint
/// <inheritdoc cref="CreateClient"/>
public static HttpMessageHandler CreateHandler(string instanceDirectory)
=> CreateHandler(SocketPath(instanceDirectory), PipeName(instanceDirectory));
private static HttpMessageHandler CreateHandler(string socketPath, string pipeName)
{
if (OperatingSystem.IsWindows())
{
var name = PipeName(instanceDirectory);
var name = pipeName;
return new SocketsHttpHandler
{
@@ -163,7 +199,7 @@ public static class InstanceEndpoint
};
}
var endpoint = new UnixDomainSocketEndPoint(SocketPath(instanceDirectory));
var endpoint = new UnixDomainSocketEndPoint(socketPath);
return new SocketsHttpHandler
{