@@ -3,6 +3,8 @@ import chalk from 'chalk';
33import fs from 'fs' ;
44import os from 'os' ;
55import path from 'path' ;
6+ import yaml from 'js-yaml' ;
7+ import { execFileSync } from 'child_process' ;
68
79interface MCPArgs {
810 url : string ;
@@ -35,6 +37,10 @@ interface CursorConfig {
3537 } ;
3638}
3739
40+ interface ClientConfig extends MCPConfig {
41+ [ key : string ] : any ;
42+ }
43+
3844type ErrorWithMessage = {
3945 message : string ;
4046} ;
@@ -51,9 +57,9 @@ const command: CommandModule<{}, MCPArgs> = {
5157 } )
5258 . option ( 'client' , {
5359 type : 'string' ,
54- describe : 'Client to use (claude, windsurf, cursor)' ,
60+ describe : 'Client to use (claude, cline, roocode, windsurf, witsy, enconvo, cursor, vscode, vscode-insiders, boltai, amazon-bedrock, amazonq, librechat, gemini-cli )' ,
5561 default : 'claude' ,
56- choices : [ 'claude' , 'windsurf' , 'cursor' ] ,
62+ choices : [ 'claude' , 'cline' , 'roocode' , ' windsurf', 'witsy' , 'enconvo' , ' cursor' , 'vscode' , 'vscode-insiders' , 'boltai' , 'amazon-bedrock' , 'amazonq' , 'librechat' , 'gemini-cli '] ,
5763 } )
5864 . option ( 'name' , {
5965 type : 'string' ,
@@ -128,21 +134,67 @@ function saveMcpConfig(url: string, clientType: string, name: string, mcpUrl: st
128134
129135 const { baseDir } = platformPaths [ platform ] ;
130136
137+ const defaultClaudePath = path . join ( baseDir , 'Claude' , 'claude_desktop_config.json' ) ;
138+
131139 // Define client paths using the platform-specific base directories
132140 const clientPaths : {
133- [ key : string ] : { configDir : string ; configPath : string } ;
141+ [ key : string ] : { type : 'file' | 'command' | 'yaml' ; path ?: string ; command ? : string } ;
134142 } = {
135- claude : {
136- configDir : path . join ( baseDir , 'Claude' ) ,
137- configPath : path . join ( baseDir , 'Claude' , 'claude_desktop_config.json' ) ,
143+ claude : {
144+ type : 'file' ,
145+ path : defaultClaudePath
146+ } ,
147+ cline : {
148+ type : 'file' ,
149+ path : path . join ( baseDir , platformPaths [ platform ] . vscodePath , 'saoudrizwan.claude-dev' , 'settings' , 'cline_mcp_settings.json' ) ,
150+ } ,
151+ roocode : {
152+ type : 'file' ,
153+ path : path . join ( baseDir , platformPaths [ platform ] . vscodePath , 'rooveterinaryinc.roo-cline' , 'settings' , 'mcp_settings.json' ) ,
138154 } ,
139155 windsurf : {
140- configDir : path . join ( homeDir , '.codeium' , 'windsurf' ) ,
141- configPath : path . join ( homeDir , '.codeium' , 'windsurf' , 'mcp_config.json' ) ,
156+ type : 'file' ,
157+ path : path . join ( homeDir , '.codeium' , 'windsurf' , 'mcp_config.json' ) ,
158+ } ,
159+ witsy : {
160+ type : 'file' ,
161+ path : path . join ( baseDir , 'Witsy' , 'settings.json' )
162+ } ,
163+ enconvo : {
164+ type : 'file' ,
165+ path : path . join ( homeDir , '.config' , 'enconvo' , 'mcp_config.json' ) ,
166+ } ,
167+ cursor : {
168+ type : 'file' ,
169+ path : path . join ( homeDir , '.cursor' , 'mcp.json' )
170+ } ,
171+ vscode : {
172+ type : 'command' ,
173+ command : process . platform === 'win32' ? 'code.cmd' : 'code' ,
174+ } ,
175+ 'vscode-insiders' : {
176+ type : 'command' ,
177+ command : process . platform === 'win32' ? 'code-insiders.cmd' : 'code-insiders' ,
178+ } ,
179+ boltai : {
180+ type : 'file' ,
181+ path : path . join ( homeDir , '.boltai' , 'mcp.json' )
182+ } ,
183+ 'amazon-bedrock' : {
184+ type : 'file' ,
185+ path : path . join ( homeDir , 'Amazon Bedrock Client' , 'mcp_config.json' ) ,
142186 } ,
143- cursor : {
144- configDir : path . join ( homeDir , '.cursor' ) ,
145- configPath : path . join ( homeDir , '.cursor' , 'mcp.json' ) ,
187+ amazonq : {
188+ type : 'file' ,
189+ path : path . join ( homeDir , '.aws' , 'amazonq' , 'mcp.json' ) ,
190+ } ,
191+ librechat : {
192+ type : 'yaml' ,
193+ path : path . join ( homeDir , 'LibreChat' , 'librechat.yaml' ) ,
194+ } ,
195+ 'gemini-cli' : {
196+ type : 'file' ,
197+ path : path . join ( homeDir , '.gemini' , 'settings.json' ) ,
146198 } ,
147199 } ;
148200
@@ -151,73 +203,127 @@ function saveMcpConfig(url: string, clientType: string, name: string, mcpUrl: st
151203 return ;
152204 }
153205
154- const { configDir, configPath } = clientPaths [ clientType ] ;
206+ const clientConfig = clientPaths [ clientType ] ;
207+ const newKey = name || url . split ( '/' ) . slice ( 3 ) . join ( '/' ) . replace ( / \/ / g, '-' ) ;
155208
156- if ( ! fs . existsSync ( configDir ) ) {
157- fs . mkdirSync ( configDir , { recursive : true } ) ;
209+ if ( clientConfig . type === 'command' ) {
210+ handleCommandClient ( clientConfig , newKey , config ) ;
211+ } else if ( clientConfig . type === 'yaml' ) {
212+ handleYamlClient ( clientConfig , newKey , config ) ;
213+ } else {
214+ handleFileClient ( clientConfig , newKey , config , mcpUrl ) ;
158215 }
216+ }
159217
160- const newKey = name || url . split ( '/' ) . slice ( 3 ) . join ( '/' ) . replace ( / \/ / g, '-' ) ;
218+ function handleCommandClient (
219+ clientConfig : { command ?: string } ,
220+ serverName : string ,
221+ config : MCPConfig
222+ ) : void {
223+ if ( ! clientConfig . command ) {
224+ throw new Error ( 'Command not specified for command-type client' ) ;
225+ }
161226
162- if ( clientType === 'claude' ) {
163- let claudeConfig : ClaudeConfig = { mcpServers : { } } ;
164- if ( fs . existsSync ( configPath ) ) {
165- try {
166- claudeConfig = JSON . parse ( fs . readFileSync ( configPath , 'utf8' ) ) ;
167- } catch ( error ) {
168- console . log ( chalk . yellow ( '⚠️ Creating new config file' ) ) ;
169- }
170- }
227+ const args : string [ ] = [ ] ;
228+ args . push ( '--add-mcp' , JSON . stringify ( { ...config , name : serverName } ) ) ;
171229
172- // Ensure mcpServers exists
173- if ( ! claudeConfig . mcpServers ) claudeConfig . mcpServers = { } ;
230+ try {
231+ const output = execFileSync ( clientConfig . command , args ) ;
232+ console . log ( chalk . green ( `✅ Configuration added via ${ clientConfig . command } : ${ output . toString ( ) } ` ) ) ;
233+ } catch ( error ) {
234+ if ( error && ( error as NodeJS . ErrnoException ) . code === 'ENOENT' ) {
235+ throw new Error (
236+ `Command '${ clientConfig . command } ' not found. Make sure ${ clientConfig . command } is installed and on your PATH`
237+ ) ;
238+ }
239+ throw error ;
240+ }
241+ }
174242
175- // Update only the mcpServers entry
176- claudeConfig . mcpServers [ newKey ] = config ;
243+ function handleYamlClient (
244+ clientConfig : { path ?: string } ,
245+ serverName : string ,
246+ config : MCPConfig
247+ ) : void {
248+ if ( ! clientConfig . path ) {
249+ throw new Error ( 'Path not specified for YAML client' ) ;
250+ }
177251
178- fs . writeFileSync ( configPath , JSON . stringify ( claudeConfig , null , 2 ) ) ;
252+ const configDir = path . dirname ( clientConfig . path ) ;
253+ if ( ! fs . existsSync ( configDir ) ) {
254+ fs . mkdirSync ( configDir , { recursive : true } ) ;
255+ }
179256
180- console . log ( chalk . green ( `✅ Configuration saved to: ${ configPath } ` ) ) ;
181- } else if ( clientType === 'windsurf' ) {
182- let windsurfConfig : WindsurfConfig = { mcpServers : { } } ;
183- if ( fs . existsSync ( configPath ) ) {
184- try {
185- windsurfConfig = JSON . parse ( fs . readFileSync ( configPath , 'utf8' ) ) ;
186- if ( ! windsurfConfig . mcpServers ) windsurfConfig . mcpServers = { } ;
187- } catch ( error ) {
188- console . log ( chalk . yellow ( '⚠️ Creating new config file' ) ) ;
189- }
257+ let existingYaml : any = { } ;
258+
259+ try {
260+ if ( fs . existsSync ( clientConfig . path ) ) {
261+ const originalContent = fs . readFileSync ( clientConfig . path , 'utf8' ) ;
262+ existingYaml = yaml . load ( originalContent ) as any || { } ;
190263 }
264+ } catch ( error ) {
265+ console . log ( chalk . yellow ( '⚠️ Creating new YAML config file' ) ) ;
266+ }
191267
192- windsurfConfig . mcpServers [ newKey ] = config ;
193- fs . writeFileSync ( configPath , JSON . stringify ( windsurfConfig , null , 2 ) ) ;
194- console . log ( chalk . green ( `✅ Configuration saved to: ${ configPath } ` ) ) ;
195- } else if ( clientType === 'cursor' ) {
196- let cursorConfig : CursorConfig = { mcpServers : { } } ;
197- if ( fs . existsSync ( configPath ) ) {
198- try {
199- cursorConfig = JSON . parse ( fs . readFileSync ( configPath , 'utf8' ) ) ;
200- if ( ! cursorConfig . mcpServers ) cursorConfig . mcpServers = { } ;
201- } catch ( error ) {
202- console . log ( chalk . yellow ( '⚠️ Creating new config file' ) ) ;
203- }
204- }
268+ // Initialize mcpServers if it doesn't exist
269+ if ( ! existingYaml . mcpServers ) {
270+ existingYaml . mcpServers = { } ;
271+ }
272+
273+ // Add the new server
274+ existingYaml . mcpServers [ serverName ] = config ;
275+
276+ // Write the updated YAML
277+ const yamlContent = yaml . dump ( existingYaml , {
278+ indent : 2 ,
279+ lineWidth : - 1 ,
280+ noRefs : true
281+ } ) ;
282+
283+ fs . writeFileSync ( clientConfig . path , yamlContent ) ;
284+ console . log ( chalk . green ( `✅ Configuration saved to: ${ clientConfig . path } ` ) ) ;
285+ }
205286
206- if ( cursorConfig . mcpServers [ newKey ] ) {
207- delete cursorConfig . mcpServers [ newKey ] ;
208- }
287+ function handleFileClient (
288+ clientConfig : { path ?: string } ,
289+ serverName : string ,
290+ config : MCPConfig ,
291+ mcpUrl : string
292+ ) : void {
293+ if ( ! clientConfig . path ) {
294+ throw new Error ( 'Path not specified for file client' ) ;
295+ }
209296
210- try {
297+ const configDir = path . dirname ( clientConfig . path ) ;
298+ if ( ! fs . existsSync ( configDir ) ) {
299+ fs . mkdirSync ( configDir , { recursive : true } ) ;
300+ }
211301
212- cursorConfig . mcpServers [ newKey ] = sseConfig ;
213- fs . writeFileSync ( configPath , JSON . stringify ( cursorConfig , null , 2 ) ) ;
214- console . log ( chalk . green ( `✅ Configuration saved to: ${ configPath } ` ) ) ;
215- } catch ( error ) {
216- console . log ( chalk . red ( '❌ Error occurred while setting up MCP:' ) ) ;
217- console . log ( chalk . red ( ` ${ ( error as ErrorWithMessage ) . message } ` ) ) ;
218- console . log ( chalk . yellow ( '\nPlease try again or contact support if the issue persists.\n' ) ) ;
302+ let existingConfig : ClientConfig = { mcpServers : { } } ;
303+
304+ try {
305+ if ( fs . existsSync ( clientConfig . path ) ) {
306+ existingConfig = JSON . parse ( fs . readFileSync ( clientConfig . path , 'utf8' ) ) ;
219307 }
308+ } catch ( error ) {
309+ console . log ( chalk . yellow ( '⚠️ Creating new config file' ) ) ;
220310 }
311+
312+ // Ensure mcpServers exists
313+ if ( ! existingConfig . mcpServers ) existingConfig . mcpServers = { } ;
314+
315+ // Special handling for Cursor which uses SSE configuration
316+ if ( clientConfig . path ?. includes ( '.cursor' ) ) {
317+ const sseConfig : MCPConfig = {
318+ url : mcpUrl ,
319+ } ;
320+ existingConfig . mcpServers [ serverName ] = sseConfig ;
321+ } else {
322+ existingConfig . mcpServers [ serverName ] = config ;
323+ }
324+
325+ fs . writeFileSync ( clientConfig . path , JSON . stringify ( existingConfig , null , 2 ) ) ;
326+ console . log ( chalk . green ( `✅ Configuration saved to: ${ clientConfig . path } ` ) ) ;
221327}
222328
223329export default command ;
0 commit comments