@ellery/terminal-mcp 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +207 -0
  3. package/dist/client.d.ts +5 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +185 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/index.d.ts +3 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +192 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/prompts/index.d.ts +3 -0
  12. package/dist/prompts/index.d.ts.map +1 -0
  13. package/dist/prompts/index.js +79 -0
  14. package/dist/prompts/index.js.map +1 -0
  15. package/dist/server.d.ts +28 -0
  16. package/dist/server.d.ts.map +1 -0
  17. package/dist/server.js +58 -0
  18. package/dist/server.js.map +1 -0
  19. package/dist/terminal/index.d.ts +4 -0
  20. package/dist/terminal/index.d.ts.map +1 -0
  21. package/dist/terminal/index.js +3 -0
  22. package/dist/terminal/index.js.map +1 -0
  23. package/dist/terminal/manager.d.ts +54 -0
  24. package/dist/terminal/manager.d.ts.map +1 -0
  25. package/dist/terminal/manager.js +79 -0
  26. package/dist/terminal/manager.js.map +1 -0
  27. package/dist/terminal/session.d.ts +85 -0
  28. package/dist/terminal/session.d.ts.map +1 -0
  29. package/dist/terminal/session.js +246 -0
  30. package/dist/terminal/session.js.map +1 -0
  31. package/dist/tools/getContent.d.ts +32 -0
  32. package/dist/tools/getContent.d.ts.map +1 -0
  33. package/dist/tools/getContent.js +38 -0
  34. package/dist/tools/getContent.js.map +1 -0
  35. package/dist/tools/index.d.ts +4 -0
  36. package/dist/tools/index.d.ts.map +1 -0
  37. package/dist/tools/index.js +43 -0
  38. package/dist/tools/index.js.map +1 -0
  39. package/dist/tools/screenshot.d.ts +20 -0
  40. package/dist/tools/screenshot.d.ts.map +1 -0
  41. package/dist/tools/screenshot.js +28 -0
  42. package/dist/tools/screenshot.js.map +1 -0
  43. package/dist/tools/sendKey.d.ts +31 -0
  44. package/dist/tools/sendKey.d.ts.map +1 -0
  45. package/dist/tools/sendKey.js +38 -0
  46. package/dist/tools/sendKey.js.map +1 -0
  47. package/dist/tools/type.d.ts +31 -0
  48. package/dist/tools/type.d.ts.map +1 -0
  49. package/dist/tools/type.js +31 -0
  50. package/dist/tools/type.js.map +1 -0
  51. package/dist/transport/index.d.ts +2 -0
  52. package/dist/transport/index.d.ts.map +1 -0
  53. package/dist/transport/index.js +2 -0
  54. package/dist/transport/index.js.map +1 -0
  55. package/dist/transport/socket.d.ts +30 -0
  56. package/dist/transport/socket.d.ts.map +1 -0
  57. package/dist/transport/socket.js +168 -0
  58. package/dist/transport/socket.js.map +1 -0
  59. package/dist/ui/index.d.ts +24 -0
  60. package/dist/ui/index.d.ts.map +1 -0
  61. package/dist/ui/index.js +124 -0
  62. package/dist/ui/index.js.map +1 -0
  63. package/dist/utils/keys.d.ts +16 -0
  64. package/dist/utils/keys.d.ts.map +1 -0
  65. package/dist/utils/keys.js +98 -0
  66. package/dist/utils/keys.js.map +1 -0
  67. package/dist/utils/stats.d.ts +46 -0
  68. package/dist/utils/stats.d.ts.map +1 -0
  69. package/dist/utils/stats.js +89 -0
  70. package/dist/utils/stats.js.map +1 -0
  71. package/logo.png +0 -0
  72. package/package.json +47 -0
  73. package/tsconfig.json +18 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Ellery Familia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,207 @@
1
+ <p align="center">
2
+ <img src="logo.png" alt="Terminal MCP" width="600">
3
+ </p>
4
+
5
+ <p align="center">
6
+ A headless terminal emulator exposed via MCP (Model Context Protocol), enabling AI assistants to interact with terminal applications programmatically.
7
+ </p>
8
+
9
+ ## Quick Install
10
+
11
+ ```bash
12
+ curl -fsSL https://raw.githubusercontent.com/elleryfamilia/terminal-mcp/main/install.sh | bash
13
+ ```
14
+
15
+ ## Features
16
+
17
+ - **Full Terminal Emulation**: Uses xterm.js headless for accurate VT100/ANSI emulation
18
+ - **Cross-Platform PTY**: Native pseudo-terminal support via node-pty (macOS, Linux, Windows)
19
+ - **MCP Protocol**: Implements Model Context Protocol for AI assistant integration
20
+ - **Simple API**: Four intuitive tools for complete terminal control
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ # Install dependencies
26
+ npm install
27
+
28
+ # Build
29
+ npm run build
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ ### MCP Configuration
35
+
36
+ Add to your MCP client settings:
37
+
38
+ ```json
39
+ {
40
+ "mcpServers": {
41
+ "terminal": {
42
+ "command": "terminal-mcp"
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ With custom options:
49
+
50
+ ```json
51
+ {
52
+ "mcpServers": {
53
+ "terminal": {
54
+ "command": "terminal-mcp",
55
+ "args": ["--cols", "100", "--rows", "30", "--shell", "/bin/zsh"]
56
+ }
57
+ }
58
+ }
59
+ ```
60
+
61
+ ### Command-Line Options
62
+
63
+ ```
64
+ terminal-mcp [OPTIONS]
65
+
66
+ Options:
67
+ --cols <number> Terminal width in columns (default: 120)
68
+ --rows <number> Terminal height in rows (default: 40)
69
+ --shell <path> Shell to use (default: $SHELL or bash)
70
+ --help, -h Show help message
71
+ ```
72
+
73
+ ## MCP Tools
74
+
75
+ ### `type`
76
+ Send text input to the terminal.
77
+
78
+ ```json
79
+ {
80
+ "name": "type",
81
+ "arguments": {
82
+ "text": "echo hello"
83
+ }
84
+ }
85
+ ```
86
+
87
+ ### `sendKey`
88
+ Send special keys or key combinations.
89
+
90
+ ```json
91
+ {
92
+ "name": "sendKey",
93
+ "arguments": {
94
+ "key": "Enter"
95
+ }
96
+ }
97
+ ```
98
+
99
+ Supported keys:
100
+ - Basic: `Enter`, `Tab`, `Escape`, `Backspace`, `Delete`
101
+ - Arrow: `ArrowUp`, `ArrowDown`, `ArrowLeft`, `ArrowRight`
102
+ - Navigation: `Home`, `End`, `PageUp`, `PageDown`, `Insert`
103
+ - Function: `F1` through `F12`
104
+ - Control: `Ctrl+A` through `Ctrl+Z`, `Ctrl+C`, `Ctrl+D`, etc.
105
+
106
+ ### `getContent`
107
+ Get the terminal buffer as plain text.
108
+
109
+ ```json
110
+ {
111
+ "name": "getContent",
112
+ "arguments": {
113
+ "visibleOnly": false
114
+ }
115
+ }
116
+ ```
117
+
118
+ ### `takeScreenshot`
119
+ Capture the terminal state with cursor position and dimensions.
120
+
121
+ ```json
122
+ {
123
+ "name": "takeScreenshot",
124
+ "arguments": {}
125
+ }
126
+ ```
127
+
128
+ ## Architecture
129
+
130
+ ```
131
+ MCP Client (Claude Code, etc.)
132
+ │ STDIO (JSON-RPC)
133
+
134
+ Terminal MCP Server (Node.js)
135
+ ├── MCP SDK (@modelcontextprotocol/sdk)
136
+ ├── Terminal Emulator (@xterm/headless)
137
+ └── PTY Manager (node-pty)
138
+
139
+
140
+ Shell Process (bash, zsh, etc.)
141
+ ```
142
+
143
+ ## Example Session
144
+
145
+ ```bash
146
+ # Type a command
147
+ {"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"type","arguments":{"text":"ls -la"}}}
148
+
149
+ # Send Enter key
150
+ {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"sendKey","arguments":{"key":"Enter"}}}
151
+
152
+ # Get the output
153
+ {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"getContent","arguments":{}}}
154
+ ```
155
+
156
+ ## Development
157
+
158
+ ### Project Structure
159
+
160
+ ```
161
+ terminal-mcp/
162
+ ├── src/
163
+ │ ├── index.ts # Entry point with CLI
164
+ │ ├── server.ts # MCP server setup
165
+ │ ├── terminal/
166
+ │ │ ├── index.ts # Exports
167
+ │ │ ├── session.ts # PTY + xterm integration
168
+ │ │ └── manager.ts # Session lifecycle
169
+ │ ├── tools/
170
+ │ │ ├── index.ts # Tool registry
171
+ │ │ ├── type.ts # type tool
172
+ │ │ ├── sendKey.ts # sendKey tool
173
+ │ │ ├── getContent.ts # getContent tool
174
+ │ │ └── screenshot.ts # takeScreenshot tool
175
+ │ └── utils/
176
+ │ └── keys.ts # Key code mappings
177
+ ├── docs/ # Documentation
178
+ ├── package.json
179
+ └── tsconfig.json
180
+ ```
181
+
182
+ ### Building
183
+
184
+ ```bash
185
+ npm run build # Compile TypeScript
186
+ npm run dev # Run with tsx (development)
187
+ ```
188
+
189
+ ## Documentation
190
+
191
+ See the [docs](./docs/) folder for detailed documentation:
192
+
193
+ - [Overview](./docs/index.md)
194
+ - [Installation](./docs/installation.md)
195
+ - [Tools Reference](./docs/tools.md)
196
+ - [Configuration](./docs/configuration.md)
197
+ - [Examples](./docs/examples.md)
198
+ - [Architecture](./docs/architecture.md)
199
+
200
+ ## Requirements
201
+
202
+ - Node.js 18.0.0 or later
203
+ - Build tools for native module compilation (node-pty)
204
+
205
+ ## License
206
+
207
+ MIT
@@ -0,0 +1,5 @@
1
+ /**
2
+ * MCP Client Mode - connects to existing terminal socket and serves MCP over stdio
3
+ */
4
+ export declare function startMcpClientMode(socketPath: string): Promise<void>;
5
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAoBA;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoL1E"}
package/dist/client.js ADDED
@@ -0,0 +1,185 @@
1
+ import * as net from "net";
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { ListToolsRequestSchema, CallToolRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ /**
6
+ * MCP Client Mode - connects to existing terminal socket and serves MCP over stdio
7
+ */
8
+ export async function startMcpClientMode(socketPath) {
9
+ // Connect to the interactive terminal's socket
10
+ const socket = await connectToSocket(socketPath);
11
+ // Create MCP server
12
+ const server = new Server({
13
+ name: "terminal-mcp",
14
+ version: "0.1.0",
15
+ }, {
16
+ capabilities: {
17
+ tools: {},
18
+ },
19
+ });
20
+ // Request ID counter
21
+ let requestId = 0;
22
+ // Pending requests waiting for responses
23
+ const pendingRequests = new Map();
24
+ // Buffer for incoming data
25
+ let buffer = "";
26
+ // Handle responses from the interactive terminal
27
+ socket.on("data", (data) => {
28
+ buffer += data.toString();
29
+ const lines = buffer.split("\n");
30
+ buffer = lines.pop() || "";
31
+ for (const line of lines) {
32
+ if (line.trim()) {
33
+ try {
34
+ const response = JSON.parse(line);
35
+ const pending = pendingRequests.get(response.id);
36
+ if (pending) {
37
+ pendingRequests.delete(response.id);
38
+ if (response.error) {
39
+ pending.reject(new Error(response.error.message));
40
+ }
41
+ else {
42
+ pending.resolve(response.result);
43
+ }
44
+ }
45
+ }
46
+ catch {
47
+ // Ignore parse errors
48
+ }
49
+ }
50
+ }
51
+ });
52
+ socket.on("error", (error) => {
53
+ console.error("Socket error:", error.message);
54
+ process.exit(1);
55
+ });
56
+ socket.on("close", () => {
57
+ console.error("Socket closed");
58
+ process.exit(1);
59
+ });
60
+ // Helper to send request to interactive terminal
61
+ async function sendRequest(method, params) {
62
+ const id = ++requestId;
63
+ const request = { id, method, params };
64
+ return new Promise((resolve, reject) => {
65
+ pendingRequests.set(id, { resolve, reject });
66
+ socket.write(JSON.stringify(request) + "\n", (error) => {
67
+ if (error) {
68
+ pendingRequests.delete(id);
69
+ reject(error);
70
+ }
71
+ });
72
+ });
73
+ }
74
+ // Define tools that proxy to the interactive terminal
75
+ const tools = [
76
+ {
77
+ name: "type",
78
+ description: "Type text into the terminal",
79
+ inputSchema: {
80
+ type: "object",
81
+ properties: {
82
+ text: {
83
+ type: "string",
84
+ description: "The text to type",
85
+ },
86
+ },
87
+ required: ["text"],
88
+ },
89
+ },
90
+ {
91
+ name: "sendKey",
92
+ description: "Send a special key to the terminal (e.g., enter, tab, ctrl+c)",
93
+ inputSchema: {
94
+ type: "object",
95
+ properties: {
96
+ key: {
97
+ type: "string",
98
+ description: "The key to send (e.g., enter, tab, escape, up, down, left, right, ctrl+c, ctrl+d)",
99
+ },
100
+ },
101
+ required: ["key"],
102
+ },
103
+ },
104
+ {
105
+ name: "getContent",
106
+ description: "Get the current content of the terminal buffer",
107
+ inputSchema: {
108
+ type: "object",
109
+ properties: {
110
+ visibleOnly: {
111
+ type: "boolean",
112
+ description: "If true, only return visible content (default: false)",
113
+ },
114
+ },
115
+ },
116
+ },
117
+ {
118
+ name: "takeScreenshot",
119
+ description: "Take a screenshot of the terminal showing current screen and cursor position",
120
+ inputSchema: {
121
+ type: "object",
122
+ properties: {},
123
+ },
124
+ },
125
+ {
126
+ name: "clear",
127
+ description: "Clear the terminal screen",
128
+ inputSchema: {
129
+ type: "object",
130
+ properties: {},
131
+ },
132
+ },
133
+ ];
134
+ // Register list tools handler
135
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
136
+ tools,
137
+ }));
138
+ // Register call tool handler - proxy to socket
139
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
140
+ const { name, arguments: args } = request.params;
141
+ try {
142
+ const result = await sendRequest(name, args);
143
+ return result;
144
+ }
145
+ catch (error) {
146
+ const message = error instanceof Error ? error.message : String(error);
147
+ return {
148
+ content: [
149
+ {
150
+ type: "text",
151
+ text: `Error: ${message}`,
152
+ },
153
+ ],
154
+ isError: true,
155
+ };
156
+ }
157
+ });
158
+ // Connect MCP server to stdio
159
+ const transport = new StdioServerTransport();
160
+ await server.connect(transport);
161
+ }
162
+ /**
163
+ * Connect to the interactive terminal's socket
164
+ */
165
+ function connectToSocket(socketPath) {
166
+ return new Promise((resolve, reject) => {
167
+ const socket = net.createConnection(socketPath, () => {
168
+ resolve(socket);
169
+ });
170
+ socket.on("error", (error) => {
171
+ if (error.code === "ENOENT") {
172
+ reject(new Error(`No tmcp session found at ${socketPath}.\n` +
173
+ `Start an interactive session first by running: tmcp`));
174
+ }
175
+ else if (error.code === "ECONNREFUSED") {
176
+ reject(new Error(`Connection refused to ${socketPath}.\n` +
177
+ `The tmcp session may have crashed. Try restarting it.`));
178
+ }
179
+ else {
180
+ reject(error);
181
+ }
182
+ });
183
+ });
184
+ }
185
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env node
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import * as os from "os";
5
+ import { startMcpClientMode } from "./client.js";
6
+ import { TerminalManager } from "./terminal/index.js";
7
+ import { createToolProxyServer } from "./transport/index.js";
8
+ import { printBanner, printInfo, isInfoCommand } from "./ui/index.js";
9
+ import { getStats, resetStats } from "./utils/stats.js";
10
+ // Default socket path
11
+ const DEFAULT_SOCKET_PATH = path.join(os.tmpdir(), "terminal-mcp.sock");
12
+ // Parse command line arguments
13
+ const args = process.argv.slice(2);
14
+ const options = {};
15
+ for (let i = 0; i < args.length; i++) {
16
+ const arg = args[i];
17
+ const next = args[i + 1];
18
+ switch (arg) {
19
+ case "--cols":
20
+ if (next) {
21
+ options.cols = parseInt(next, 10);
22
+ i++;
23
+ }
24
+ break;
25
+ case "--rows":
26
+ if (next) {
27
+ options.rows = parseInt(next, 10);
28
+ i++;
29
+ }
30
+ break;
31
+ case "--shell":
32
+ if (next) {
33
+ options.shell = next;
34
+ i++;
35
+ }
36
+ break;
37
+ case "--socket":
38
+ if (next) {
39
+ options.socket = next;
40
+ i++;
41
+ }
42
+ break;
43
+ case "--help":
44
+ case "-h":
45
+ console.log(`
46
+ terminal-mcp - A headless terminal emulator exposed via MCP
47
+
48
+ Usage: terminal-mcp [options]
49
+
50
+ Options:
51
+ --cols <number> Terminal width in columns (default: auto or 120)
52
+ --rows <number> Terminal height in rows (default: auto or 40)
53
+ --shell <path> Shell to use (default: $SHELL or bash)
54
+ --socket <path> Unix socket path for MCP (default: ${DEFAULT_SOCKET_PATH})
55
+ --help, -h Show this help message
56
+
57
+ Mode Detection:
58
+ - If stdin is a TTY: Interactive mode (gives you a shell, exposes socket)
59
+ - If stdin is not a TTY: MCP client mode (connects to socket, serves MCP)
60
+
61
+ Interactive Mode (run in your terminal):
62
+ terminal-mcp
63
+
64
+ This gives you an interactive shell. AI can observe/interact via MCP.
65
+ Type /info for the MCP configuration to add to your MCP client.
66
+
67
+ MCP Client Mode (add to your MCP client config):
68
+ {
69
+ "mcpServers": {
70
+ "terminal": {
71
+ "command": "terminal-mcp"
72
+ }
73
+ }
74
+ }
75
+ `);
76
+ process.exit(0);
77
+ }
78
+ }
79
+ async function main() {
80
+ const socketPath = options.socket || DEFAULT_SOCKET_PATH;
81
+ const isInteractive = process.stdin.isTTY;
82
+ if (isInteractive) {
83
+ // Interactive mode: Shell on stdin/stdout, tool proxy on Unix socket
84
+ await startInteractiveMode(socketPath);
85
+ }
86
+ else {
87
+ // MCP client mode: Connect to socket, serve MCP over stdio
88
+ await startMcpClientMode(socketPath);
89
+ }
90
+ }
91
+ async function startInteractiveMode(socketPath) {
92
+ // Initialize stats
93
+ resetStats();
94
+ // Get terminal size from environment or use defaults
95
+ const cols = options.cols ?? (process.stdout.columns || 120);
96
+ const rows = options.rows ?? (process.stdout.rows || 40);
97
+ const shell = options.shell || process.env.SHELL || "/bin/bash";
98
+ // Print startup banner
99
+ printBanner({ socketPath, cols, rows, shell });
100
+ // Create terminal manager (prompt customization handled in session.ts)
101
+ const manager = new TerminalManager({
102
+ cols,
103
+ rows,
104
+ shell: options.shell,
105
+ });
106
+ // Get the session and set up interactive I/O
107
+ const session = manager.getSession();
108
+ // Pipe PTY output to stdout
109
+ session.onData((data) => {
110
+ process.stdout.write(data);
111
+ });
112
+ // Handle PTY exit
113
+ session.onExit((code) => {
114
+ console.log(`\n[terminal-mcp] Shell exited with code ${code}`);
115
+ cleanup();
116
+ process.exit(code);
117
+ });
118
+ // Set up raw mode for stdin if it's a TTY
119
+ if (process.stdin.isTTY) {
120
+ process.stdin.setRawMode(true);
121
+ }
122
+ process.stdin.resume();
123
+ // Buffer for detecting /info command
124
+ let inputBuffer = "";
125
+ // Pipe stdin to PTY, with /info command detection
126
+ process.stdin.on("data", (data) => {
127
+ const str = data.toString();
128
+ // Check for /info command (detect when user types /info and presses enter)
129
+ // In raw mode, we need to buffer input to detect the command
130
+ for (const char of str) {
131
+ if (char === "\r" || char === "\n") {
132
+ // Check if buffer is /info command
133
+ if (isInfoCommand(inputBuffer)) {
134
+ // Print info and clear the line
135
+ process.stdout.write("\r\n");
136
+ printInfo(socketPath, cols, rows, shell, getStats());
137
+ inputBuffer = "";
138
+ // Write newline to shell to maintain prompt
139
+ session.write("\n");
140
+ continue;
141
+ }
142
+ inputBuffer = "";
143
+ session.write(char);
144
+ }
145
+ else if (char === "\x7f" || char === "\b") {
146
+ // Backspace
147
+ inputBuffer = inputBuffer.slice(0, -1);
148
+ session.write(char);
149
+ }
150
+ else {
151
+ inputBuffer += char;
152
+ session.write(char);
153
+ }
154
+ }
155
+ });
156
+ // Handle terminal resize
157
+ process.stdout.on("resize", () => {
158
+ const newCols = process.stdout.columns || cols;
159
+ const newRows = process.stdout.rows || rows;
160
+ session.resize(newCols, newRows);
161
+ });
162
+ // Start tool proxy socket server
163
+ const socketServer = createToolProxyServer(socketPath, manager);
164
+ // Cleanup function
165
+ function cleanup() {
166
+ manager.dispose();
167
+ socketServer.close();
168
+ try {
169
+ fs.unlinkSync(socketPath);
170
+ }
171
+ catch {
172
+ // Ignore
173
+ }
174
+ }
175
+ // Handle signals
176
+ process.on("SIGINT", () => {
177
+ // Pass Ctrl+C to the shell instead of exiting
178
+ session.write("\x03");
179
+ });
180
+ process.on("SIGTERM", () => {
181
+ cleanup();
182
+ process.exit(0);
183
+ });
184
+ process.on("exit", () => {
185
+ cleanup();
186
+ });
187
+ }
188
+ main().catch((error) => {
189
+ console.error("Failed to start:", error);
190
+ process.exit(1);
191
+ });
192
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ export declare function registerPrompts(server: Server): void;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AA+DnE,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAwBpD"}