Skip to content

Commit 8e26247

Browse files
committed
feat: setup changes for mcp cli
1 parent 803d1c4 commit 8e26247

File tree

3 files changed

+184
-65
lines changed

3 files changed

+184
-65
lines changed

mcp/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@composio/mcp",
3-
"version": "1.0.8",
3+
"version": "1.0.9",
44
"description": "MCP CLI tool",
55
"main": "dist/index.js",
66
"bin": {
@@ -24,12 +24,14 @@
2424
"license": "ISC",
2525
"packageManager": "pnpm@10.6.1",
2626
"dependencies": {
27-
"composiohq-modelcontextprotocol-typescript-sdk": "1.12.2",
27+
"@types/js-yaml": "^4.0.9",
2828
"abortcontroller-polyfill": "^1.7.8",
2929
"chalk": "^4.1.2",
3030
"commander": "^13.1.0",
31+
"composiohq-modelcontextprotocol-typescript-sdk": "1.12.2",
3132
"event-source-polyfill": "^1.0.31",
3233
"event-target-polyfill": "^0.0.4",
34+
"js-yaml": "^4.1.0",
3335
"rollup-plugin-node-polyfills": "^0.2.1",
3436
"yargs": "^17.7.2",
3537
"zod": "^3.24.2"

mcp/pnpm-lock.yaml

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mcp/src/cli/commands/setup.ts

Lines changed: 169 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import chalk from 'chalk';
33
import fs from 'fs';
44
import os from 'os';
55
import path from 'path';
6+
import yaml from 'js-yaml';
7+
import { execFileSync } from 'child_process';
68

79
interface MCPArgs {
810
url: string;
@@ -35,6 +37,10 @@ interface CursorConfig {
3537
};
3638
}
3739

40+
interface ClientConfig extends MCPConfig {
41+
[key: string]: any;
42+
}
43+
3844
type 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

223329
export default command;

0 commit comments

Comments
 (0)