# Directory Structure ``` ├── .gitignore ├── LICENSE ├── package.json ├── README.md ├── src │ └── index.ts └── tsconfig.json ``` # Files -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` # Dependencies node_modules/ # Build output build/ dist/ # Environment variables .env .env.local .env.*.local # IDE files .vscode/ .idea/ *.iml # Logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* # System files .DS_Store Thumbs.db ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown # WinTerm MCP A Model Context Protocol server that provides programmatic access to the Windows terminal. This server enables AI models to interact with the Windows command line interface through a set of standardized tools. ## Features - **Write to Terminal**: Execute commands or write text to the Windows terminal - **Read Terminal Output**: Retrieve output from previously executed commands - **Send Control Characters**: Send control signals (e.g., Ctrl+C) to the terminal - **Windows-Native**: Built specifically for Windows command line interaction ## Installation 1. **Clone the Repository**: ```bash git clone https://github.com/capecoma/winterm-mcp.git cd winterm-mcp ``` 2. **Install Dependencies**: ```bash npm install ``` 3. **Build the Project**: ```bash npm run build ``` 4. **Configure Claude Desktop**: Add the server config to `%APPDATA%/Claude/claude_desktop_config.json`: ```json { "mcpServers": { "github.com/capecoma/winterm-mcp": { "command": "node", "args": ["path/to/build/index.js"], "disabled": false, "autoApprove": [] } } } ``` Note: Replace "path/to/build/index.js" with the actual path to your built index.js file. ## Available Tools ### write_to_terminal Writes text or commands to the terminal. ```json { "command": "echo Hello, World!" } ``` ### read_terminal_output Reads the specified number of lines from terminal output. ```json { "linesOfOutput": 5 } ``` ### send_control_character Sends a control character to the terminal (e.g., Ctrl+C). ```json { "letter": "C" } ``` ## Development For development with auto-rebuild: ```bash npm run dev ``` ## License MIT License - see [LICENSE](LICENSE) file. ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json { "compilerOptions": { "target": "ES2020", "module": "ES2020", "moduleResolution": "node", "outDir": "./build", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": ["src/**/*"], "exclude": ["node_modules"] } ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json { "name": "winterm-mcp", "version": "1.0.0", "description": "Windows Terminal MCP Server - A Model Context Protocol server for Windows terminal interaction", "type": "module", "main": "build/index.js", "scripts": { "build": "npx tsc", "start": "node build/index.js", "dev": "npx tsc -w" }, "repository": { "type": "git", "url": "git+https://github.com/capecoma/winterm-mcp.git" }, "keywords": [ "mcp", "terminal", "windows", "claude", "automation" ], "author": "capecoma", "license": "MIT", "bugs": { "url": "https://github.com/capecoma/winterm-mcp/issues" }, "homepage": "https://github.com/capecoma/winterm-mcp#readme", "dependencies": { "@modelcontextprotocol/sdk": "latest", "@types/node": "^20.0.0", "typescript": "^5.0.0" } } ``` -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- ```typescript #!/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); ```