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:
@@ -98,6 +98,17 @@ class MCPService {
|
||||
|
||||
func isPathAllowed(_ path: String) -> Bool {
|
||||
let resolved = ((path as NSString).expandingTildeInPath as NSString).standardizingPath
|
||||
// Always allow the system temp directory — external MCP servers and tools write
|
||||
// intermediate data there (e.g. Safari MCP page-content files, generated images).
|
||||
// Check both NSTemporaryDirectory() (per-user Darwin temp dir) and /tmp (what this
|
||||
// codebase's own temp files actually use, e.g. ChatViewModel's /tmp/oai_generated_*
|
||||
// and ExternalMCPClient's /tmp/oai_mcp_* — they resolve to different directories.
|
||||
let tmpCandidates = [
|
||||
(NSTemporaryDirectory() as NSString).standardizingPath,
|
||||
"/tmp",
|
||||
"/private/tmp"
|
||||
]
|
||||
if tmpCandidates.contains(where: { resolved.hasPrefix($0) }) { return true }
|
||||
return allowedFolders.contains { resolved.hasPrefix($0) }
|
||||
}
|
||||
|
||||
@@ -310,6 +321,9 @@ class MCPService {
|
||||
))
|
||||
}
|
||||
|
||||
// Add tools from external MCP servers (stdio JSON-RPC protocol)
|
||||
tools.append(contentsOf: ExternalMCPManager.shared.getToolSchemas())
|
||||
|
||||
return tools
|
||||
}
|
||||
|
||||
@@ -472,6 +486,10 @@ class MCPService {
|
||||
return await executePersonalDataAction(toolName: name, argumentsJSON: arguments, summary: summary)
|
||||
|
||||
default:
|
||||
// Route to external MCP servers (stdio JSON-RPC)
|
||||
if ExternalMCPManager.shared.isExternalTool(name) {
|
||||
return await ExternalMCPManager.shared.executeTool(name: name, argumentsJSON: arguments)
|
||||
}
|
||||
// Route anytype_* tools to AnytypeMCPService
|
||||
if name.hasPrefix("anytype_") {
|
||||
return await anytypeService.executeTool(name: name, arguments: arguments)
|
||||
|
||||
Reference in New Issue
Block a user