@cequenceai/mcp-cli
Version:
Cequence MCP CLI - Command-line tool for setting up Cequence MCP servers with AI clients
446 lines (387 loc) ⢠17.2 kB
text/typescript
import { Command } from 'commander';
import chalk from 'chalk';
import ora from 'ora';
import * as fs from 'fs';
import * as path from 'path';
import { setupCursor } from './clients/cursor';
import { setupClaude } from './clients/claude';
import { setupWindsurf } from './clients/windsurf';
import { setupClaudeCode } from './clients/claudecode';
import { setupVSCode } from './clients/vscode';
import { validateMcpUrl } from './utils/validation';
// Read version from package.json
const packageJsonPath = path.join(__dirname, '..', 'package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
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(packageJson.version);
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('claudecode')
.description('Setup Cequence MCP server with Claude Code')
.requiredOption('-u, --url <url>', 'Cequence MCP server URL')
.option('-n, --name <name>', 'MCP server name')
.option('-t, --transport <transport>', 'Transport type (http or sse)', 'http')
.option('-k, --api-key <key>', 'API key for authentication (if required)')
.action(async (options) => {
const spinner = ora('Setting up Cequence MCP server with Claude Code...').start();
try {
// Validate URL
if (!validateMcpUrl(options.url)) {
spinner.fail('Invalid Cequence MCP server URL');
process.exit(1);
}
// Validate transport
const transport = options.transport?.toLowerCase();
if (transport !== 'http' && transport !== 'sse') {
spinner.fail('Invalid transport type. Use "http" or "sse"');
process.exit(1);
}
const result = await setupClaudeCode({
url: options.url,
name: options.name || 'Cequence MCP Gateway',
apiKey: options.apiKey
}, transport as 'http' | 'sse');
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 Claude Code!'));
console.log(chalk.cyan('\nš Next steps:'));
console.log(' 1. Claude Code will automatically detect the new MCP server');
console.log(' 2. You can verify with: claude mcp list');
console.log(' 3. Start using your secured APIs through Claude Code');
} 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('vscode')
.description('Setup Cequence MCP server with VS Code')
.requiredOption('-u, --url <url>', 'Cequence MCP server URL')
.option('-n, --name <name>', 'MCP server name')
.option('-g, --global', 'Configure globally for all workspaces (default: workspace-specific)')
.option('-w, --workspace <path>', 'Workspace path for project-specific config')
.option('-k, --api-key <key>', 'API key for authentication (if required)')
.action(async (options) => {
const configType = options.global ? 'global' : 'workspace';
const spinner = ora(`Setting up Cequence MCP server with VS Code (${configType})...`).start();
try {
// Validate URL
if (!validateMcpUrl(options.url)) {
spinner.fail('Invalid Cequence MCP server URL');
process.exit(1);
}
const result = await setupVSCode({
url: options.url,
name: options.name || 'Cequence MCP Gateway',
apiKey: options.apiKey
}, options.global || false, options.workspace);
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 VS Code!'));
console.log(chalk.cyan(`\nš Configuration written to: ${result.configPath}`));
console.log(chalk.cyan('\nš Next steps:'));
console.log(' 1. Reload VS Code (Cmd/Ctrl + Shift + P ā "Developer: Reload Window")');
console.log(' 2. Ensure GitHub Copilot is installed and activated');
console.log(' 3. The MCP server should appear in your Copilot tools');
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 Desktop');
console.log(' claudecode - Setup Cequence MCP server with Claude Code');
console.log(' vscode - Setup Cequence MCP server with VS Code');
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);