@cequenceai/mcp-cli
Version:
Cequence MCP CLI - Command-line tool for setting up Cequence MCP servers with AI clients
341 lines (296 loc) ⢠12.7 kB
text/typescript
import { Command } from 'commander';
import chalk from 'chalk';
import ora from 'ora';
import { setupCursor } from './clients/cursor';
import { setupClaude } from './clients/claude';
import { setupWindsurf } from './clients/windsurf';
import { validateMcpUrl } from './utils/validation';
const program = new Command();
program
.name('cequence-mcp')
.description('Cequence MCP CLI - Command-line tool for setting up Cequence MCP servers with AI clients')
.version('1.3.0');
program
.command('cursor')
.description('Setup Cequence MCP server with Cursor IDE')
.requiredOption('-u, --url <url>', 'Cequence MCP server URL')
.option('-n, --name <name>', 'MCP server name')
.option('-k, --api-key <key>', 'API key for authentication (if required)')
.option('--no-refresh', 'Disable automatic token refresh (requires manual login when token expires)')
.action(async (options) => {
const spinner = ora('Setting up Cequence MCP server with Cursor IDE...').start();
try {
// Validate URL
if (!validateMcpUrl(options.url)) {
spinner.fail('Invalid Cequence MCP server URL');
process.exit(1);
}
await setupCursor({
url: options.url,
name: options.name || 'Cequence MCP Gateway',
apiKey: options.apiKey,
noRefresh: options.noRefresh
});
spinner.succeed(chalk.green('ā
Cequence MCP server successfully configured for Cursor IDE!'));
console.log(chalk.cyan('\nš Next steps:'));
console.log(' 1. Restart Cursor IDE');
console.log(' 2. Open the command palette (Cmd/Ctrl + Shift + P)');
console.log(' 3. Look for MCP-related commands');
console.log(' 4. Your Cequence-secured APIs are now available as MCP tools');
} catch (error) {
spinner.fail('Failed to setup Cequence MCP server');
console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
process.exit(1);
}
});
program
.command('claude')
.description('Setup Cequence MCP server with Claude AI')
.requiredOption('-u, --url <url>', 'Cequence MCP server URL')
.option('-n, --name <name>', 'MCP server name')
.option('-k, --api-key <key>', 'API key for authentication (if required)')
.option('--no-refresh', 'Disable automatic token refresh (requires manual login when token expires)')
.action(async (options) => {
const spinner = ora('Setting up Cequence MCP server with Claude AI...').start();
try {
// Validate URL
if (!validateMcpUrl(options.url)) {
spinner.fail('Invalid Cequence MCP server URL');
process.exit(1);
}
await setupClaude({
url: options.url,
name: options.name || 'Cequence MCP Gateway',
apiKey: options.apiKey,
noRefresh: options.noRefresh
});
spinner.succeed(chalk.green('ā
Cequence MCP server successfully configured for Claude AI!'));
console.log(chalk.cyan('\nš Next steps:'));
console.log(' 1. Restart Claude AI application');
console.log(' 2. The Cequence MCP server should be available in your conversation');
console.log(' 3. Start using your secured APIs through Claude\'s interface');
} catch (error) {
spinner.fail('Failed to setup Cequence MCP server');
console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
process.exit(1);
}
});
program
.command('windsurf')
.description('Setup Cequence MCP server with Windsurf IDE')
.requiredOption('-u, --url <url>', 'Cequence MCP server URL')
.option('-n, --name <name>', 'MCP server name')
.option('-k, --api-key <key>', 'API key for authentication (if required)')
.option('--no-refresh', 'Disable automatic token refresh (requires manual login when token expires)')
.action(async (options) => {
const spinner = ora('Setting up Cequence MCP server with Windsurf IDE...').start();
try {
// Validate URL
if (!validateMcpUrl(options.url)) {
spinner.fail('Invalid Cequence MCP server URL');
process.exit(1);
}
const result = await setupWindsurf({
url: options.url,
name: options.name || 'Cequence MCP Gateway',
apiKey: options.apiKey,
noRefresh: options.noRefresh
});
if (!result.success) {
spinner.fail('Failed to setup Cequence MCP server');
console.error(chalk.red('Error:'), result.message);
process.exit(1);
}
spinner.succeed(chalk.green('ā
Cequence MCP server successfully configured for Windsurf IDE!'));
console.log(chalk.cyan(`\nš Configuration written to: ${result.configPath}`));
console.log(chalk.cyan('\nš Next steps:'));
console.log(' 1. Restart Windsurf IDE');
console.log(' 2. Open the command palette (Cmd/Ctrl + Shift + P)');
console.log(' 3. Look for MCP-related commands');
console.log(' 4. Your Cequence-secured APIs are now available as MCP tools');
} catch (error) {
spinner.fail('Failed to setup Cequence MCP server');
console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
process.exit(1);
}
});
program
.command('serve')
.description('Start MCP bridge server (internal use by Claude AI)')
.requiredOption('-u, --url <url>', 'HTTP MCP server URL to bridge')
.option('-n, --name <name>', 'MCP server name')
.option('-k, --api-key <key>', 'API key for authentication (if required)')
.action(async (options) => {
// This command creates a stdio-based MCP server that bridges to HTTP
const http = require('http');
const https = require('https');
const { URL } = require('url');
try {
const serverUrl = new URL(options.url);
const client = serverUrl.protocol === 'https:' ? https : http;
// Set up stdio for MCP protocol
process.stdin.setEncoding('utf8');
process.stdin.resume();
let buffer = '';
process.stdin.on('data', async (chunk) => {
buffer += chunk;
// Process complete JSON-RPC messages (each ends with newline)
let newlineIndex;
while ((newlineIndex = buffer.indexOf('\n')) >= 0) {
const line = buffer.slice(0, newlineIndex).trim();
buffer = buffer.slice(newlineIndex + 1);
if (line) {
try {
const request = JSON.parse(line);
// Validate JSON-RPC request structure
if (!request.jsonrpc || !request.method) {
const errorResponse = {
jsonrpc: '2.0',
id: request.id || null,
error: {
code: -32600,
message: 'Invalid Request: Missing jsonrpc or method'
}
};
process.stdout.write(JSON.stringify(errorResponse) + '\n');
continue;
}
// Forward request to HTTP MCP server
const postData = JSON.stringify(request);
const headers: Record<string, string | number> = {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData),
'Accept': 'application/json, text/event-stream'
};
// Add API key header if provided
if (options.apiKey) {
headers['Authorization'] = `Bearer ${options.apiKey}`;
}
const requestOptions = {
hostname: serverUrl.hostname,
port: serverUrl.port,
path: serverUrl.pathname,
method: 'POST',
headers,
timeout: 30000 // 30 second timeout
};
const httpReq = client.request(requestOptions, (httpRes: any) => {
let responseData = '';
httpRes.on('data', (chunk: any) => {
responseData += chunk;
});
httpRes.on('end', () => {
try {
if (responseData.trim()) {
const response = JSON.parse(responseData);
// Ensure response has proper JSON-RPC structure
if (!response.jsonrpc) {
response.jsonrpc = '2.0';
}
if (response.id === undefined) {
response.id = request.id;
}
process.stdout.write(JSON.stringify(response) + '\n');
} else {
// Empty response - send error
const errorResponse = {
jsonrpc: '2.0',
id: request.id || null,
error: {
code: -32603,
message: 'Empty response from HTTP server'
}
};
process.stdout.write(JSON.stringify(errorResponse) + '\n');
}
} catch (parseError: any) {
// Invalid JSON response
const errorResponse = {
jsonrpc: '2.0',
id: request.id || null,
error: {
code: -32603,
message: `Invalid JSON response from HTTP server: ${parseError?.message || 'Unknown error'}`
}
};
process.stdout.write(JSON.stringify(errorResponse) + '\n');
}
});
});
httpReq.on('timeout', () => {
httpReq.destroy();
const errorResponse = {
jsonrpc: '2.0',
id: request.id || null,
error: {
code: -32603,
message: 'Request timeout'
}
};
process.stdout.write(JSON.stringify(errorResponse) + '\n');
});
httpReq.on('error', (error: any) => {
const errorResponse = {
jsonrpc: '2.0',
id: request.id || null,
error: {
code: -32603,
message: `HTTP request error: ${error?.message || 'Unknown error'}`
}
};
process.stdout.write(JSON.stringify(errorResponse) + '\n');
});
httpReq.write(postData);
httpReq.end();
} catch (parseError: any) {
// Invalid JSON input
const errorResponse = {
jsonrpc: '2.0',
id: null,
error: {
code: -32700,
message: `Parse error: ${parseError?.message || 'Unknown error'}`
}
};
process.stdout.write(JSON.stringify(errorResponse) + '\n');
}
}
}
});
process.stdin.on('end', () => {
process.exit(0);
});
process.stdin.on('error', (error) => {
console.error('Stdin error:', error);
process.exit(1);
});
} catch (error) {
console.error('Bridge server error:', error);
process.exit(1);
}
});
program
.command('list')
.description('List configured Cequence MCP servers')
.action(async () => {
console.log(chalk.blue('š Configured Cequence MCP Servers:'));
// TODO: Implement listing functionality
console.log(chalk.yellow(' Feature coming soon...'));
});
// Handle unknown commands
program.on('command:*', () => {
console.error(chalk.red('Invalid command:'), program.args.join(' '));
console.log(chalk.cyan('Available commands:'));
console.log(' cursor - Setup Cequence MCP server with Cursor IDE');
console.log(' claude - Setup Cequence MCP server with Claude AI');
console.log(' windsurf - Setup Cequence MCP server with Windsurf IDE');
console.log(' serve - Start MCP bridge server (internal use)');
console.log(' list - List configured Cequence MCP servers');
process.exit(1);
});
// Show help when no command is provided
if (!process.argv.slice(2).length) {
program.outputHelp();
}
program.parse(process.argv);