chore: Enhance MCP server execution and compatibility with Cursor mode
- Introduce environment variables for Cursor compatibility in silent-mcp.sh and npx-entry.cjs - Implement process cleanup for existing MCP instances to prevent conflicts - Adjust logging behavior based on execution context to ensure proper message handling - Add test-cursor.sh script to simulate Cursor environment for testing purposes - Refactor stdio-server.ts to manage logging and message flushing based on compatibility mode
This commit is contained in:
@@ -4,9 +4,24 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { spawn } = require('child_process');
|
||||
|
||||
// Set environment variable - enable stdio transport and silence output
|
||||
// Set environment variable - enable stdio transport
|
||||
process.env.USE_STDIO_TRANSPORT = 'true';
|
||||
process.env.LOG_LEVEL = 'silent';
|
||||
|
||||
// Check if we're being called from Cursor (check for Cursor specific env vars)
|
||||
const isCursor = process.env.CURSOR_SESSION || process.env.CURSOR_CHANNEL;
|
||||
|
||||
// For Cursor, we need to ensure consistent stdio handling
|
||||
if (isCursor) {
|
||||
// Essential for Cursor compatibility
|
||||
process.env.LOG_LEVEL = 'info';
|
||||
process.env.CURSOR_COMPATIBLE = 'true';
|
||||
|
||||
// Ensure we have a clean environment for Cursor
|
||||
delete process.env.SILENT_MCP_RUNNING;
|
||||
} else {
|
||||
// For normal operation, silence logs
|
||||
process.env.LOG_LEVEL = 'silent';
|
||||
}
|
||||
|
||||
// Ensure logs directory exists
|
||||
const logsDir = path.join(process.cwd(), 'logs');
|
||||
@@ -22,15 +37,80 @@ if (!fs.existsSync(envPath) && fs.existsSync(envExamplePath)) {
|
||||
fs.copyFileSync(envExamplePath, envPath);
|
||||
}
|
||||
|
||||
// Start the MCP server with redirected stderr
|
||||
try {
|
||||
// Use our silent-mcp.sh script if it exists, otherwise use mcp-stdio.cjs
|
||||
const silentScriptPath = path.join(process.cwd(), 'silent-mcp.sh');
|
||||
// Define a function to ensure the child process is properly cleaned up on exit
|
||||
function setupCleanExit(childProcess) {
|
||||
const exitHandler = () => {
|
||||
if (childProcess && !childProcess.killed) {
|
||||
childProcess.kill();
|
||||
}
|
||||
process.exit();
|
||||
};
|
||||
|
||||
if (fs.existsSync(silentScriptPath) && fs.statSync(silentScriptPath).isFile()) {
|
||||
// Execute the silent-mcp.sh script instead
|
||||
const childProcess = spawn('/bin/bash', [silentScriptPath], {
|
||||
// Handle various termination signals
|
||||
process.on('SIGINT', exitHandler);
|
||||
process.on('SIGTERM', exitHandler);
|
||||
process.on('exit', exitHandler);
|
||||
}
|
||||
|
||||
// Start the MCP server
|
||||
try {
|
||||
// Critical: For Cursor, we need a very specific execution environment
|
||||
if (isCursor) {
|
||||
// Careful process cleanup for Cursor (optional but can help)
|
||||
try {
|
||||
const { execSync } = require('child_process');
|
||||
execSync('pkill -f "node.*stdio-server" || true', { stdio: 'ignore' });
|
||||
} catch (e) {
|
||||
// Ignore errors from process cleanup
|
||||
}
|
||||
|
||||
// Allow some time for process cleanup
|
||||
setTimeout(() => {
|
||||
const scriptPath = path.join(__dirname, 'mcp-stdio.cjs');
|
||||
|
||||
// For Cursor, we need very specific stdio handling
|
||||
// Using pipe for both stdin and stdout is critical
|
||||
const childProcess = spawn('node', [scriptPath], {
|
||||
stdio: ['pipe', 'pipe', 'pipe'], // All piped for maximum control
|
||||
env: {
|
||||
...process.env,
|
||||
USE_STDIO_TRANSPORT: 'true',
|
||||
CURSOR_COMPATIBLE: 'true',
|
||||
// Make sure stdin/stdout are treated as binary
|
||||
NODE_OPTIONS: '--no-force-async-hooks-checks'
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure no buffering to prevent missed messages
|
||||
childProcess.stdin.setDefaultEncoding('utf8');
|
||||
|
||||
// Create bidirectional pipes
|
||||
process.stdin.pipe(childProcess.stdin);
|
||||
childProcess.stdout.pipe(process.stdout);
|
||||
childProcess.stderr.pipe(process.stderr);
|
||||
|
||||
// Setup error handling
|
||||
childProcess.on('error', (err) => {
|
||||
console.error('Failed to start server:', err.message);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// Ensure child process is properly cleaned up
|
||||
setupCleanExit(childProcess);
|
||||
|
||||
}, 500); // Short delay to ensure clean start
|
||||
}
|
||||
// For regular use, if silent-mcp.sh exists, use it
|
||||
else if (!isCursor && fs.existsSync(path.join(process.cwd(), 'silent-mcp.sh')) &&
|
||||
fs.statSync(path.join(process.cwd(), 'silent-mcp.sh')).isFile()) {
|
||||
// Execute the silent-mcp.sh script
|
||||
const childProcess = spawn('/bin/bash', [path.join(process.cwd(), 'silent-mcp.sh')], {
|
||||
stdio: ['inherit', 'inherit', 'ignore'], // Redirect stderr to /dev/null
|
||||
env: {
|
||||
...process.env,
|
||||
USE_STDIO_TRANSPORT: 'true',
|
||||
LOG_LEVEL: 'silent'
|
||||
}
|
||||
});
|
||||
|
||||
childProcess.on('error', (err) => {
|
||||
@@ -38,25 +118,18 @@ try {
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// Properly handle process termination
|
||||
process.on('SIGINT', () => {
|
||||
childProcess.kill('SIGINT');
|
||||
});
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
childProcess.kill('SIGTERM');
|
||||
});
|
||||
} else {
|
||||
// Fall back to original method if silent-mcp.sh doesn't exist
|
||||
// Ensure child process is properly cleaned up
|
||||
setupCleanExit(childProcess);
|
||||
}
|
||||
// Otherwise run normally (direct non-Cursor)
|
||||
else {
|
||||
const scriptPath = path.join(__dirname, 'mcp-stdio.cjs');
|
||||
|
||||
// Use 'pipe' for stdout and ignore (null) for stderr
|
||||
const childProcess = spawn('node', [scriptPath], {
|
||||
stdio: ['inherit', 'pipe', 'ignore'], // Redirect stderr to /dev/null
|
||||
stdio: ['inherit', 'pipe', 'ignore'], // Redirect stderr to /dev/null for normal use
|
||||
env: {
|
||||
...process.env,
|
||||
USE_STDIO_TRANSPORT: 'true',
|
||||
LOG_LEVEL: 'silent'
|
||||
USE_STDIO_TRANSPORT: 'true'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -68,14 +141,8 @@ try {
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// Properly handle process termination
|
||||
process.on('SIGINT', () => {
|
||||
childProcess.kill('SIGINT');
|
||||
});
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
childProcess.kill('SIGTERM');
|
||||
});
|
||||
// Ensure child process is properly cleaned up
|
||||
setupCleanExit(childProcess);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error starting server:', error.message);
|
||||
|
||||
Reference in New Issue
Block a user