@@ -3,6 +3,7 @@ import fs from "fs";
33import path from "path" ;
44import os from "os" ;
55import { applyEdits , findNodeAtLocation , modify , parseTree } from "jsonc-parser" ;
6+ import { exec } from "child_process" ;
67import type { Platform } from "./setupAiToolsUtils.js" ;
78import { formatError , getPlatform } from "./setupAiToolsUtils.js" ;
89
@@ -11,6 +12,19 @@ export type AIToolType = "cursor" | "vscode" | "windsurf" | "claudeDesktop" | "c
1112// These are tools that don't have a designated editor to open the config file
1213export const TOOLS_WITHOUT_EDITORS : AIToolType [ ] = [ "claudeDesktop" , "claudeCode" , "opencode" ] ;
1314
15+ // Mac: open path in default app, or in editor (e.g. "cursor") if supported
16+ const getOpenCommandMac = ( configPath : string , editor : AIToolType ) : string =>
17+ editor && ! TOOLS_WITHOUT_EDITORS . includes ( editor ) ? `open "${ editor } ://file${ configPath } "` : `open "${ configPath } "` ;
18+
19+ // Linux: open path in default app, or in editor if supported
20+ const getOpenCommandLinux = ( configPath : string , editor : AIToolType ) : string =>
21+ editor && ! TOOLS_WITHOUT_EDITORS . includes ( editor )
22+ ? `xdg-open "${ editor } ://file${ configPath } "`
23+ : `xdg-open "${ configPath } "` ;
24+
25+ // Windows: open path in default app (for tools without a dedicated editor)
26+ const getOpenCommandWindowsDefault = ( configPath : string ) : string => `start "" "${ configPath } "` ;
27+
1428const MCP_SERVER_KEY = "mongodb-mcp-server" ;
1529type EnvironmentKey = "env" | "environment" ;
1630type McpConfigEntry = {
@@ -143,7 +157,8 @@ const updateConfigInPlace = (
143157} ;
144158
145159export abstract class AITool {
146- abstract name : string ;
160+ abstract name : string ; // readable name for the tool for users
161+ abstract toolType : AIToolType ; // used internallly
147162 abstract configFileName : string ;
148163 abstract get configPath ( ) : string ;
149164 tip ?: string ;
@@ -217,19 +232,57 @@ export abstract class AITool {
217232 writeConfigFile ( configPath , config ) ;
218233 }
219234 }
235+
236+ /**
237+ * Returns the shell command to open the config file. Override in subclasses for editor-specific behavior.
238+ */
239+ getOpenConfigCommand ( configPath : string , platform : Platform , editor : AIToolType ) : string | null {
240+ switch ( platform ) {
241+ case "mac" :
242+ return getOpenCommandMac ( configPath , editor ) ;
243+ case "windows" :
244+ return getOpenCommandWindowsDefault ( configPath ) ;
245+ case "linux" :
246+ return getOpenCommandLinux ( configPath , editor ) ;
247+ default :
248+ return null ;
249+ }
250+ }
251+
252+ openConfigSettings ( ) : void {
253+ const platform = getPlatform ( ) ;
254+ if ( ! platform ) return ;
255+ const cmd = this . getOpenConfigCommand ( this . configPath , platform , this . toolType ) ;
256+ if ( cmd ) exec ( cmd ) ;
257+ }
220258}
221259
222260class Cursor extends AITool {
223261 name = "Cursor" ;
262+ toolType = "cursor" as AIToolType ;
224263 configFileName = "mcp.json" ;
225264 get configPath ( ) : string {
226265 return path . join ( getBasePath ( ) , ".cursor" , "mcp.json" ) ;
227266 }
228267 tip = `Tip: Press ${ getPlatform ( ) === "mac" ? "Cmd+I" : "Ctrl+I" } in Cursor to open the Agent panel.\n` ;
268+
269+ override getOpenConfigCommand ( configPath : string , platform : Platform ) : string | null {
270+ switch ( platform ) {
271+ case "mac" :
272+ return getOpenCommandMac ( configPath , "cursor" ) ;
273+ case "windows" :
274+ return `cursor "${ configPath } "` ;
275+ case "linux" :
276+ return getOpenCommandLinux ( configPath , "cursor" ) ;
277+ default :
278+ return null ;
279+ }
280+ }
229281}
230282
231283class VSCode extends AITool {
232284 name = "VS Code" ;
285+ toolType = "vscode" as AIToolType ;
233286 configFileName = "mcp.json" ;
234287 protected override getServersKey ( ) : McpServers {
235288 return "servers" ;
@@ -252,10 +305,24 @@ class VSCode extends AITool {
252305 return "" ;
253306 }
254307 tip = `Tip: Press ${ getPlatform ( ) === "mac" ? "Cmd+Shift+I" : "Ctrl+Shift+I" } in VS Code to open the Copilot panel.\n` ;
308+
309+ override getOpenConfigCommand ( configPath : string , platform : Platform ) : string | null {
310+ switch ( platform ) {
311+ case "mac" :
312+ return getOpenCommandMac ( configPath , "vscode" ) ;
313+ case "windows" :
314+ return `code "${ configPath } "` ;
315+ case "linux" :
316+ return getOpenCommandLinux ( configPath , "vscode" ) ;
317+ default :
318+ return null ;
319+ }
320+ }
255321}
256322
257323class Windsurf extends AITool {
258324 name = "Windsurf" ;
325+ toolType = "windsurf" as AIToolType ;
259326 configFileName = "mcp_config.json" ;
260327 get configPath ( ) : string {
261328 const platform : Platform | null = getPlatform ( ) ;
@@ -272,10 +339,23 @@ class Windsurf extends AITool {
272339 return "" ;
273340 }
274341 tip = `Tip: Press ${ getPlatform ( ) === "mac" ? "Cmd+L" : "Ctrl+L" } in Windsurf to open the AI panel.\n` ;
342+ getOpenConfigCommand ( configPath : string , platform : Platform ) : string | null {
343+ switch ( platform ) {
344+ case "mac" :
345+ return getOpenCommandMac ( configPath , "windsurf" ) ;
346+ case "windows" :
347+ return `windsurf "${ configPath } "` ;
348+ case "linux" :
349+ return getOpenCommandLinux ( configPath , "windsurf" ) ;
350+ default :
351+ return null ;
352+ }
353+ }
275354}
276355
277356class ClaudeDesktop extends AITool {
278357 name = "Claude Desktop" ;
358+ toolType = "claudeDesktop" as AIToolType ;
279359 configFileName = "claude_desktop_config.json" ;
280360 get configPath ( ) : string {
281361 const platform : Platform | null = getPlatform ( ) ;
@@ -298,6 +378,7 @@ class ClaudeDesktop extends AITool {
298378
299379class ClaudeCode extends AITool {
300380 name = "Claude Code" ;
381+ toolType = "claudeCode" as AIToolType ;
301382 configFileName = ".claude.json" ;
302383 get configPath ( ) : string {
303384 return path . join ( getBasePath ( ) , ".claude.json" ) ;
@@ -306,6 +387,7 @@ class ClaudeCode extends AITool {
306387
307388class OpenCode extends AITool {
308389 name = "Open Code" ;
390+ toolType = "opencode" as AIToolType ;
309391 configFileName = "opencode.json" ;
310392 get configPath ( ) : string {
311393 return path . join ( getBasePath ( ) , ".config" , "opencode" , "opencode.json" ) ;
@@ -330,6 +412,11 @@ class OpenCode extends AITool {
330412 }
331413}
332414
415+ // Opens the config file for the given tool using the tool's platform-specific command.
416+ export const openConfigSettings = ( tool : AIToolType ) : void => {
417+ AI_TOOL_REGISTRY [ tool ] . openConfigSettings ( ) ;
418+ } ;
419+
333420export const AI_TOOL_REGISTRY : Record < AIToolType , AITool > = {
334421 [ "cursor" ] : new Cursor ( ) ,
335422 [ "vscode" ] : new VSCode ( ) ,
0 commit comments