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:
1 parent
ee0b817f25
commit
3b068a7866
14 files changed
+576
-6
No files matched your search
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user