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:
co-authored by
Claude Opus 5.5
parent
b5f65b8cf7
commit
cb46289bb6
@@ -123,6 +123,47 @@ internal sealed class InstanceForwarding(ILogger logger) : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #713: An MCP message for an instance — to its MCP socket, not its instance socket.
|
||||
///
|
||||
/// <para>This is the one forwarding that keeps <c>Authorization</c>: the MCP token is
|
||||
/// what the instance checks, and the manager adds nothing of its own. Through the
|
||||
/// instance socket the same request would have arrived token-free and with all rights —
|
||||
/// that is why it never goes there.</para>
|
||||
/// </summary>
|
||||
public async Task ForwardMcpAsync(HttpContext context, ManagedInstance ziel, CancellationToken ct)
|
||||
{
|
||||
HttpClient client;
|
||||
lock (_lock)
|
||||
{
|
||||
var key = "mcp|" + ziel.Directory;
|
||||
if (!_clients.TryGetValue(key, out client!))
|
||||
_clients[key] = client = InstanceEndpoint.CreateMcpClient(ziel.Directory);
|
||||
}
|
||||
|
||||
using var anfrage = new HttpRequestMessage(HttpMethod.Post, "/mcp")
|
||||
{
|
||||
Content = new StreamContent(context.Request.Body)
|
||||
};
|
||||
anfrage.Content.Headers.TryAddWithoutValidation("Content-Type", "application/json");
|
||||
if (context.Request.Headers.Authorization.Count > 0)
|
||||
anfrage.Headers.TryAddWithoutValidation("Authorization", context.Request.Headers.Authorization.ToString());
|
||||
|
||||
try
|
||||
{
|
||||
using var antwort = await client.SendAsync(anfrage, ct).ConfigureAwait(false);
|
||||
context.Response.StatusCode = (int)antwort.StatusCode;
|
||||
if (antwort.Content.Headers.ContentType is { } typ)
|
||||
context.Response.ContentType = typ.ToString();
|
||||
await antwort.Content.CopyToAsync(context.Response.Body, ct).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex) when (ex is not OperationCanceledException)
|
||||
{
|
||||
logger.LogWarning(ex, "Instanz {Name} war für MCP nicht erreichbar.", ziel.Name);
|
||||
context.Response.StatusCode = StatusCodes.Status502BadGateway;
|
||||
}
|
||||
}
|
||||
|
||||
private HttpClient ClientFor(string instanceDirectory)
|
||||
{
|
||||
lock (_lock)
|
||||
|
||||
Reference in New Issue
Block a user