feat: Unity MCP-Server Integration pro Session
Sessions können jetzt einen Unity MCP-Server (URL + Token) konfigurieren. Die CLI erhält automatisch die MCP-Config mit korrektem Transport-Typ (URL-basierte Erkennung: /sse → SSE, sonst HTTP für streamableHttp). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
9c507e4716
commit
500c00f948
9 files changed
+61
-12
No files matched your search
@@ -245,7 +245,7 @@ public static class ChatEndpoint
|
||||
{
|
||||
if (anthropic.IsCliAvailable)
|
||||
{
|
||||
await anthropic.StreamChatViaCli(res, ctx.ModelId, ctx.SystemPrompt, messages, ctx.ChainId, workspace: ctx.Workspace, allowUserChoice: ctx.Agent?.AllowUserChoice ?? true, planMode: body.PlanMode, webSearch: !string.IsNullOrEmpty(ctx.WebSearchVersion), sessionId: sessionId, agentId: ctx.AgentId, remoteMgr: remoteMgr, workCt: workCt, sseCt: sseCt);
|
||||
await anthropic.StreamChatViaCli(res, ctx.ModelId, ctx.SystemPrompt, messages, ctx.ChainId, workspace: ctx.Workspace, allowUserChoice: ctx.Agent?.AllowUserChoice ?? true, planMode: body.PlanMode, webSearch: !string.IsNullOrEmpty(ctx.WebSearchVersion), sessionId: sessionId, agentId: ctx.AgentId, remoteMgr: remoteMgr, unityMcpUrl: ctx.Session?.UnityMcpUrl, unityMcpToken: ctx.Session?.UnityMcpToken, workCt: workCt, sseCt: sseCt);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -80,6 +80,8 @@ public static class SessionEndpoint
|
||||
s.MaxReviewIterations,
|
||||
s.FinalizerAgentId,
|
||||
s.PreparerAgentId,
|
||||
s.UnityMcpUrl,
|
||||
s.UnityMcpToken,
|
||||
s.IsDeleted,
|
||||
s.CreatedAt,
|
||||
s.UpdatedAt,
|
||||
|
||||
@@ -23,6 +23,8 @@ public class SessionRecord
|
||||
public int MaxReviewIterations { get; set; } = 3;
|
||||
public string FinalizerAgentId { get; set; }
|
||||
public string PreparerAgentId { get; set; }
|
||||
public string UnityMcpUrl { get; set; }
|
||||
public string UnityMcpToken { get; set; }
|
||||
public string CreatedAt { get; set; }
|
||||
public string UpdatedAt { get; set; }
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ public partial class AnthropicService
|
||||
string sessionId = null,
|
||||
string agentId = null,
|
||||
RemoteConnectionManager remoteMgr = null,
|
||||
string unityMcpUrl = null,
|
||||
string unityMcpToken = null,
|
||||
CancellationToken workCt = default,
|
||||
CancellationToken sseCt = default)
|
||||
{
|
||||
@@ -59,7 +61,7 @@ public partial class AnthropicService
|
||||
{
|
||||
// MCP-Config generieren (immer – für ask_user_choice, optional Memory-Tools + remote_execute)
|
||||
var hasRemote = remoteMgr != null && !string.IsNullOrEmpty(sessionId) && remoteMgr.GetBySessionId(sessionId) != null;
|
||||
mcpConfigPath = GenerateMcpConfig(sessionId, agentId, hasRemote);
|
||||
mcpConfigPath = GenerateMcpConfig(sessionId, agentId, hasRemote, unityMcpUrl, unityMcpToken);
|
||||
|
||||
// System-Prompt als Text extrahieren
|
||||
var systemText = new StringBuilder();
|
||||
@@ -210,6 +212,7 @@ public partial class AnthropicService
|
||||
planMode: planMode,
|
||||
webSearch: webSearch,
|
||||
mcpConfigPath: mcpConfigPath,
|
||||
hasUnityMcp: !string.IsNullOrEmpty(unityMcpUrl),
|
||||
cancellationToken: workCt
|
||||
);
|
||||
|
||||
@@ -486,7 +489,7 @@ public partial class AnthropicService
|
||||
/// 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, string agentId = null, bool hasRemote = false)
|
||||
private string GenerateMcpConfig(string sessionId, string agentId = null, bool hasRemote = false, string unityMcpUrl = null, string unityMcpToken = null)
|
||||
{
|
||||
// Port aus Konfiguration oder Default
|
||||
var port = 19000;
|
||||
@@ -509,19 +512,39 @@ public partial class AnthropicService
|
||||
["GOTT_HAS_REMOTE"] = hasRemote ? "true" : "false"
|
||||
};
|
||||
|
||||
var config = new
|
||||
var mcpServers = new Dictionary<string, object>
|
||||
{
|
||||
mcpServers = new Dictionary<string, object>
|
||||
["gott-remote"] = new
|
||||
{
|
||||
["gott-remote"] = new
|
||||
{
|
||||
command = "python3",
|
||||
args = new[] { scriptPath },
|
||||
env
|
||||
}
|
||||
command = "python3",
|
||||
args = new[] { scriptPath },
|
||||
env
|
||||
}
|
||||
};
|
||||
|
||||
// Unity MCP Server (pro Session konfigurierbar)
|
||||
// Claude CLI erfordert expliziten "type" – "http" für streamableHttp, "sse" für SSE
|
||||
if (!string.IsNullOrEmpty(unityMcpUrl))
|
||||
{
|
||||
// URL-basierte Typ-Erkennung: /sse → SSE-Transport, sonst HTTP (streamableHttp)
|
||||
var mcpType = unityMcpUrl.TrimEnd('/').EndsWith("/sse", StringComparison.OrdinalIgnoreCase) ? "sse" : "http";
|
||||
var unityConfig = new Dictionary<string, object>
|
||||
{
|
||||
["type"] = mcpType,
|
||||
["url"] = unityMcpUrl
|
||||
};
|
||||
if (!string.IsNullOrEmpty(unityMcpToken))
|
||||
{
|
||||
unityConfig["headers"] = new Dictionary<string, string>
|
||||
{
|
||||
["Authorization"] = $"Bearer {unityMcpToken}"
|
||||
};
|
||||
}
|
||||
mcpServers["unity"] = unityConfig;
|
||||
}
|
||||
|
||||
var config = new { mcpServers };
|
||||
|
||||
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);
|
||||
|
||||
@@ -83,6 +83,7 @@ public class ClaudeCliService
|
||||
bool planMode = false,
|
||||
bool webSearch = false,
|
||||
string mcpConfigPath = null,
|
||||
bool hasUnityMcp = false,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!_isAvailable)
|
||||
@@ -126,6 +127,11 @@ public class ClaudeCliService
|
||||
tools.Add("mcp__gott-remote__memory_write");
|
||||
tools.Add("mcp__gott-remote__memory_list");
|
||||
}
|
||||
// Unity MCP Tools erlauben (alle Tools vom unity MCP-Server)
|
||||
if (hasUnityMcp)
|
||||
{
|
||||
tools.Add("mcp__unity__*");
|
||||
}
|
||||
args.Add("--allowed-tools");
|
||||
args.Add(tools.Count > 0 ? string.Join(" ", tools) : "\"\"");
|
||||
|
||||
|
||||
@@ -145,6 +145,8 @@ public partial class DatabaseService
|
||||
if (body.TryGetProperty("maxReviewIterations", out var mri)) { sets.Add("max_review_iterations = @maxReviewIterations"); p.Add("maxReviewIterations", mri.GetInt32()); }
|
||||
if (body.TryGetProperty("finalizerAgentId", out var fa)) { sets.Add("finalizer_agent_id = @finalizerAgentId"); p.Add("finalizerAgentId", fa.ValueKind == System.Text.Json.JsonValueKind.Null ? null : fa.GetString()); }
|
||||
if (body.TryGetProperty("preparerAgentId", out var pa)) { sets.Add("preparer_agent_id = @preparerAgentId"); p.Add("preparerAgentId", pa.ValueKind == System.Text.Json.JsonValueKind.Null ? null : pa.GetString()); }
|
||||
if (body.TryGetProperty("unityMcpUrl", out var umu)) { sets.Add("unity_mcp_url = @unityMcpUrl"); p.Add("unityMcpUrl", umu.ValueKind == System.Text.Json.JsonValueKind.Null ? null : umu.GetString()); }
|
||||
if (body.TryGetProperty("unityMcpToken", out var umt)) { sets.Add("unity_mcp_token = @unityMcpToken"); p.Add("unityMcpToken", umt.ValueKind == System.Text.Json.JsonValueKind.Null ? null : umt.GetString()); }
|
||||
|
||||
await conn.ExecuteAsync($"UPDATE sessions SET {string.Join(", ", sets)} WHERE session_id = @sessionId", p);
|
||||
}
|
||||
|
||||
@@ -193,6 +193,8 @@ public partial class DatabaseService
|
||||
TryAddColumn(conn, "agents", "task_instruction", "TEXT");
|
||||
TryAddColumn(conn, "chains", "plan_mode", "INTEGER NOT NULL DEFAULT 0");
|
||||
TryAddColumn(conn, "sessions", "preparer_agent_id", "TEXT");
|
||||
TryAddColumn(conn, "sessions", "unity_mcp_url", "TEXT");
|
||||
TryAddColumn(conn, "sessions", "unity_mcp_token", "TEXT");
|
||||
TryAddColumn(conn, "messages", "tag", "TEXT");
|
||||
|
||||
conn.Execute(@"
|
||||
|
||||
@@ -158,6 +158,10 @@
|
||||
<label>Benutzerdefinierte Befehle</label>
|
||||
<div class="custom-commands" id="customCommandsList"></div>
|
||||
<button type="button" id="addCommandBtn" class="add-cmd-btn">+ Befehl hinzufügen</button>
|
||||
<label>Unity MCP URL <small>(optional)</small></label>
|
||||
<input type="text" id="sessionUnityMcpUrl" placeholder="z.B. http://localhost:8080" autocomplete="off">
|
||||
<label>Unity MCP Token <small>(optional)</small></label>
|
||||
<input type="password" id="sessionUnityMcpToken" placeholder="MCP-Server Token" autocomplete="off">
|
||||
<label>Telegram Bot</label>
|
||||
<div id="telegramBotSelectCard" class="agent-select-card">
|
||||
<div class="agent-select-empty"><i class="fa fa-paper-plane"></i> Kein Bot zugewiesen</div>
|
||||
|
||||
@@ -35,6 +35,8 @@ registerPage('session', {
|
||||
const deleteBtn = document.getElementById('sessionDeleteBtn');
|
||||
const commandsList = document.getElementById('customCommandsList');
|
||||
const addCommandBtn = document.getElementById('addCommandBtn');
|
||||
const unityMcpUrlEl = document.getElementById('sessionUnityMcpUrl');
|
||||
const unityMcpTokenEl = document.getElementById('sessionUnityMcpToken');
|
||||
const more = document.querySelector('.more');
|
||||
|
||||
// Avatar Editor initialisieren
|
||||
@@ -55,6 +57,10 @@ registerPage('session', {
|
||||
|
||||
// Custom Commands laden
|
||||
loadCommandsIntoUI(commandsList, session.customCommands);
|
||||
|
||||
// Unity MCP
|
||||
unityMcpUrlEl.value = session.unityMcpUrl || '';
|
||||
unityMcpTokenEl.value = session.unityMcpToken || '';
|
||||
} else {
|
||||
titleEl.textContent = 'Neue Session';
|
||||
deleteBtn.hidden = true;
|
||||
@@ -168,7 +174,9 @@ registerPage('session', {
|
||||
customCommands: commands,
|
||||
avatarScale: _sessionAvatarEditor.getScale(),
|
||||
avatarOffsetX: _sessionAvatarEditor.getOffsetX(),
|
||||
avatarOffsetY: _sessionAvatarEditor.getOffsetY()
|
||||
avatarOffsetY: _sessionAvatarEditor.getOffsetY(),
|
||||
unityMcpUrl: unityMcpUrlEl.value.trim() || null,
|
||||
unityMcpToken: unityMcpTokenEl.value.trim() || null
|
||||
};
|
||||
|
||||
if (isNew) {
|
||||
|
||||
Reference in new issue
Block a user