feat: MCP-Server für remote_execute + Inline-Choice-UI

- Python MCP-Server (stdio, JSON-RPC 2.0) für remote_execute via Claude CLI
- Interner API-Endpoint /api/internal/remote-execute mit Token-Auth
- MCP-Config wird automatisch generiert wenn Remote-Verbindung vorhanden
- Auth-Middleware: /api/internal/ von Auth ausgenommen
- Frontend: :::ASK_USER_CHOICE::: Blöcke als interaktive Buttons rendern
- Streaming: Unvollständige Choice-Blöcke werden während des Streams versteckt
- tool-ui.js: MCP-Tool-Namen generisch normalisieren (mcp__server__tool → tool)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
MelekiandClaude Opus 4.6 committed 2026-03-18 09:00:50 +01:00
1 parent ee0b817f25
commit 3b068a7866
14 files changed
+576 -6

No files matched your search

+60 -1
View File
@@ -1733,6 +1733,49 @@ public class AnthropicService
}
}
/// <summary>
/// Generiert eine temporäre MCP-Config-Datei für die Claude CLI.
/// Enthält den MCP-Server für remote_execute.
/// </summary>
private string GenerateMcpConfig(string sessionId)
{
// Port aus Konfiguration oder Default
var port = 19000;
var scriptPath = Path.Combine(AppContext.BaseDirectory, "scripts", "mcp-remote-execute.py");
// Fallback: Quell-Verzeichnis prüfen (für Dev-Modus)
if (!File.Exists(scriptPath))
{
// wwwroot ist typischerweise /app/gott/wwwroot → eine Ebene hoch = Projekt-Root
var projectRoot = Path.GetDirectoryName(AppPaths.WwwRoot) ?? AppContext.BaseDirectory;
var srcPath = Path.Combine(projectRoot, "scripts", "mcp-remote-execute.py");
if (File.Exists(srcPath)) scriptPath = srcPath;
}
var config = new
{
mcpServers = new Dictionary<string, object>
{
["gott-remote"] = new
{
command = "python3",
args = new[] { scriptPath },
env = new Dictionary<string, string>
{
["GOTT_API_URL"] = $"http://localhost:{port}",
["GOTT_SESSION_ID"] = sessionId,
["GOTT_AUTH_TOKEN"] = Endpoints.InternalEndpoint.InternalToken
}
}
}
};
var json = System.Text.Json.JsonSerializer.Serialize(config, new System.Text.Json.JsonSerializerOptions { WriteIndented = true });
var tempPath = Path.Combine(Path.GetTempPath(), $"gott-mcp-{sessionId}.json");
File.WriteAllText(tempPath, json);
return tempPath;
}
/// <summary>
/// Extrahiert Web-Search-Sources aus einem web_search_tool_result-Block.
/// </summary>
@@ -1886,6 +1929,7 @@ public class AnthropicService
bool planMode = false,
bool webSearch = false,
string sessionId = null,
RemoteConnectionManager remoteMgr = null,
CancellationToken workCt = default,
CancellationToken sseCt = default)
{
@@ -1900,6 +1944,7 @@ public class AnthropicService
var usage = new TokenUsage();
var fullText = new StringBuilder();
var tempImagePaths = new List<string>();
string mcpConfigPath = null;
var chain = new ChainRecord
{
@@ -1910,6 +1955,12 @@ public class AnthropicService
try
{
// MCP-Config für remote_execute generieren (wenn Remote-Verbindung vorhanden)
if (remoteMgr != null && !string.IsNullOrEmpty(sessionId) && remoteMgr.GetBySessionId(sessionId) != null)
{
mcpConfigPath = GenerateMcpConfig(sessionId);
}
// System-Prompt als Text extrahieren
var systemText = new StringBuilder();
if (systemPrompt != null)
@@ -2079,6 +2130,7 @@ Danach STOPPE die Textgenerierung sofort. Nutze dies sparsam — nur wenn die En
hasWorkspace: !string.IsNullOrEmpty(workspace),
planMode: planMode,
webSearch: webSearch,
mcpConfigPath: mcpConfigPath,
cancellationToken: workCt
);
@@ -2181,6 +2233,12 @@ Danach STOPPE die Textgenerierung sofort. Nutze dies sparsam — nur wenn die En
{
// Temp-Bilddateien aufräumen
CleanupTempImages(tempImagePaths);
// MCP-Config Temp-Datei aufräumen
if (mcpConfigPath != null)
{
try { File.Delete(mcpConfigPath); } catch { }
}
}
}
@@ -2200,7 +2258,8 @@ Danach STOPPE die Textgenerierung sofort. Nutze dies sparsam — nur wenn die En
"WebSearch" => "web_search",
"WebFetch" => "web_fetch",
"NotebookEdit" => "notebook_edit",
_ => cliName
"mcp__gott-remote__remote_execute" => "remote_execute",
_ => cliName?.StartsWith("mcp__") == true ? cliName.Split("__").LastOrDefault() ?? cliName : cliName
};
}
+9
View File
@@ -82,6 +82,7 @@ public class ClaudeCliService
bool hasWorkspace = false,
bool planMode = false,
bool webSearch = false,
string mcpConfigPath = null,
CancellationToken cancellationToken = default)
{
if (!_isAvailable)
@@ -111,6 +112,8 @@ public class ClaudeCliService
tools.AddRange(new[] { "Bash(*)", "Read", "Write", "Edit", "Glob", "Grep" });
}
if (webSearch) { tools.Add("WebFetch"); tools.Add("WebSearch"); }
// MCP-Tools erlauben (wenn MCP-Config vorhanden)
if (!string.IsNullOrEmpty(mcpConfigPath)) { tools.Add("mcp__gott-remote__remote_execute"); }
args.Add("--allowed-tools");
args.Add(tools.Count > 0 ? string.Join(" ", tools) : "\"\"");
@@ -132,6 +135,12 @@ public class ClaudeCliService
args.Add(systemPrompt);
}
if (!string.IsNullOrEmpty(mcpConfigPath))
{
args.Add("--mcp-config");
args.Add(mcpConfigPath);
}
return await RunCliStreamingAsync(args, prompt, onEvent, cancellationToken);
}