diff --git a/LehrerApp.Core/Mcp/McpPipeConstants.cs b/LehrerApp.Core/Mcp/McpPipeConstants.cs index ddb01ba..6e712d3 100644 --- a/LehrerApp.Core/Mcp/McpPipeConstants.cs +++ b/LehrerApp.Core/Mcp/McpPipeConstants.cs @@ -9,4 +9,29 @@ namespace LehrerApp.Core.Mcp; public static class McpPipeConstants { public const string PipeName = "LehrerApp.Mcp"; + + /// + /// Unter macOS/Linux emuliert .NET Named Pipes über eine Socket-Datei unter + /// (intern "CoreFxPipe_" + Name), und die liest dafür die + /// TMPDIR-Umgebungsvariable des jeweiligen Prozesses. LehrerApp.Desktop (von Finder/Dock + /// gestartet) bekommt das reguläre per-Login-Session-TMPDIR unter /var/folders/.../T, + /// während LehrerApp.McpBridge als Kindprozess von Claude Desktop oft eine reduzierte + /// Umgebung mit TMPDIR=/tmp erbt - beide Prozesse suchen die Pipe-Datei dann an + /// unterschiedlichen Orten und finden sich nie ("MCP nicht erreichbar, obwohl LehrerApp + /// läuft", auch wenn beide Prozesse laufen und die Pipe grundsätzlich offen ist). Fix: TMPDIR + /// für beide Prozesse hart auf denselben Ordner setzen, bevor die erste + /// NamedPipeServerStream/-ClientStream-Instanz entsteht - dieselbe ApplicationData-Basis wie + /// AppBootstrapper (siehe dort) wird anders als TMPDIR zuverlässig an Kindprozesse + /// weitergereicht. Muss als eine der ersten Anweisungen in Main aufgerufen werden (Desktop wie + /// Bridge), auf Windows ein No-op (dort nutzen Named Pipes den Kernel-Namespace, keine + /// Socket-Datei). + /// + public static void EnsureStableUnixSocketDirectory() + { + if (OperatingSystem.IsWindows()) return; + var dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "LehrerApp", "ipc"); + Directory.CreateDirectory(dir); + Environment.SetEnvironmentVariable("TMPDIR", dir); + } } diff --git a/LehrerApp.Desktop/Program.cs b/LehrerApp.Desktop/Program.cs index e7a1814..de6aa6f 100644 --- a/LehrerApp.Desktop/Program.cs +++ b/LehrerApp.Desktop/Program.cs @@ -1,4 +1,5 @@ using Avalonia; +using LehrerApp.Core.Mcp; using LehrerApp.Desktop.Services; namespace LehrerApp.Desktop; @@ -8,6 +9,10 @@ class Program [STAThread] public static void Main(string[] args) { + // Muss vor der ersten NamedPipeServerStream-Instanz laufen (siehe + // McpServerHostedService), sonst löst TMPDIR-Auseinanderdriften zwischen diesem Prozess + // und LehrerApp.McpBridge auf macOS/Linux "MCP nicht erreichbar" aus - siehe Doku dort. + McpPipeConstants.EnsureStableUnixSocketDirectory(); var logger = AppBootstrapper.EnsureLogger(); GlobalExceptionHandler.Install(logger); diff --git a/LehrerApp.McpBridge/Program.cs b/LehrerApp.McpBridge/Program.cs index 7348f57..d12493e 100644 --- a/LehrerApp.McpBridge/Program.cs +++ b/LehrerApp.McpBridge/Program.cs @@ -1,3 +1,4 @@ +using LehrerApp.Core.Mcp; using LehrerApp.McpBridge; using ModelContextProtocol.Protocol; using ModelContextProtocol.Server; @@ -9,6 +10,11 @@ using ModelContextProtocol.Server; // Nachladen der echten Werkzeugliste, sobald LehrerApp erreichbar ist, übernimmt // RealServerBridge - Details und Begründung siehe dort. +// Muss vor der ersten NamedPipeClientStream-Instanz laufen (siehe RealServerBridge), sonst löst +// TMPDIR-Auseinanderdriften zwischen diesem als Claude-Desktop-Kindprozess gestarteten Prozess und +// LehrerApp.Desktop auf macOS/Linux "MCP nicht erreichbar" aus, obwohl LehrerApp läuft. +McpPipeConstants.EnsureStableUnixSocketDirectory(); + var bridge = new RealServerBridge(); await bridge.TryReconnectAsync(CancellationToken.None); bridge.EnsureFallbackTool();