Add external MCP server support (stdio JSON-RPC)
Lets the AI connect to any external stdio MCP server (e.g. safaridriver --mcp) configured in Settings, with tools auto-discovered and prefixed by server slug. Includes crash detection with backoff restart (5s/15s/30s) and a Settings UI to add/enable/disable/remove servers. Fixes the temp-dir allowlist in MCPService.isPathAllowed to also match /tmp and /private/tmp (not just NSTemporaryDirectory(), which resolves to a different per-user Darwin temp dir) so the MCP file tools can actually read files external servers and image generation write there. Also switches the Add Server sheet's argument parsing to a quote-aware tokenizer so args containing spaces survive intact. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -461,6 +461,48 @@ class SettingsService {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - External MCP Servers
|
||||
|
||||
var externalMCPServers: [ExternalMCPServer] {
|
||||
get {
|
||||
guard let json = cache["externalMCPServers"],
|
||||
let data = json.data(using: .utf8) else { return [] }
|
||||
let decoder = JSONDecoder()
|
||||
decoder.dateDecodingStrategy = .iso8601
|
||||
return (try? decoder.decode([ExternalMCPServer].self, from: data)) ?? []
|
||||
}
|
||||
set {
|
||||
let encoder = JSONEncoder()
|
||||
encoder.dateEncodingStrategy = .iso8601
|
||||
if let data = try? encoder.encode(newValue),
|
||||
let json = String(data: data, encoding: .utf8) {
|
||||
cache["externalMCPServers"] = json
|
||||
DatabaseService.shared.setSetting(key: "externalMCPServers", value: json)
|
||||
}
|
||||
Task { @MainActor in ExternalMCPManager.shared.reconfigure(servers: newValue) }
|
||||
}
|
||||
}
|
||||
|
||||
func addExternalMCPServer(_ server: ExternalMCPServer) {
|
||||
externalMCPServers = externalMCPServers + [server]
|
||||
}
|
||||
|
||||
func updateExternalMCPServer(_ server: ExternalMCPServer) {
|
||||
externalMCPServers = externalMCPServers.map { $0.id == server.id ? server : $0 }
|
||||
}
|
||||
|
||||
func deleteExternalMCPServer(id: UUID) {
|
||||
externalMCPServers = externalMCPServers.filter { $0.id != id }
|
||||
}
|
||||
|
||||
func toggleExternalMCPServer(id: UUID) {
|
||||
externalMCPServers = externalMCPServers.map { s in
|
||||
s.id == id ? ExternalMCPServer(id: s.id, name: s.name, command: s.command,
|
||||
args: s.args, isEnabled: !s.isEnabled,
|
||||
timeout: s.timeout, createdAt: s.createdAt) : s
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Favorite Models
|
||||
|
||||
var favoriteModelIds: Set<String> {
|
||||
|
||||
Reference in New Issue
Block a user