WinTerm MCP

#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import { spawn, exec } from 'child_process'; import * as os from 'os'; class WindowsTerminalServer { private server: Server; private outputBuffer: string[] = []; constructor() { this.server = new Server( { name: 'windows-terminal-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); this.setupToolHandlers(); this.server.onerror = (error) => console.error('[MCP Error]', error); process.on('SIGINT', async () => { await this.cleanup(); process.exit(0); }); } private setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: 'write_to_terminal', description: 'Write text or commands to the terminal', inputSchema: { type: 'object', properties: { command: { type: 'string', description: 'The text or command to write to the terminal', }, }, required: ['command'], }, }, { name: 'read_terminal_output', description: 'Read the output from the terminal', inputSchema: { type: 'object', properties: { linesOfOutput: { type: 'number', description: 'Number of lines of output to read', }, }, required: ['linesOfOutput'], }, }, { name: 'send_control_character', description: 'Send a control character to the terminal', inputSchema: { type: 'object', properties: { letter: { type: 'string', description: 'The letter corresponding to the control character (e.g., "C" for Ctrl+C)', }, }, required: ['letter'], }, }, ], })); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { switch (request.params.name) { case 'write_to_terminal': { const { command } = request.params.arguments as { command: string }; return new Promise((resolve, reject) => { const shell = os.platform() === 'win32' ? 'cmd.exe' : 'bash'; const shellArgs = os.platform() === 'win32' ? ['/c', command] : ['-c', command]; const proc = spawn(shell, shellArgs); let output = ''; proc.stdout.on('data', (data) => { output += data.toString(); this.outputBuffer.push(...data.toString().split('\n')); }); proc.stderr.on('data', (data) => { output += data.toString(); this.outputBuffer.push(...data.toString().split('\n')); }); proc.on('close', (code) => { resolve({ content: [ { type: 'text', text: `Command executed with exit code ${code}. Output:\n${output}`, }, ], }); }); proc.on('error', (err) => { reject(new McpError(ErrorCode.InternalError, err.message)); }); }); } case 'read_terminal_output': { const { linesOfOutput } = request.params.arguments as { linesOfOutput: number }; const output = this.outputBuffer.slice(-linesOfOutput).join('\n'); return { content: [ { type: 'text', text: output, }, ], }; } case 'send_control_character': { const { letter } = request.params.arguments as { letter: string }; // On Windows, we'll use taskkill to simulate Ctrl+C if (letter.toUpperCase() === 'C' && os.platform() === 'win32') { exec('taskkill /im node.exe /f'); } return { content: [ { type: 'text', text: `Sent Ctrl+${letter.toUpperCase()} signal`, }, ], }; } default: throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}` ); } }); } private async cleanup() { await this.server.close(); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Windows Terminal MCP server running on stdio'); } } const server = new WindowsTerminalServer(); server.run().catch(console.error);