mcp-terminal-runner 0.1.0

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.
package/README.md ADDED
@@ -0,0 +1,156 @@
1
+ # MCP Terminal Runner
2
+
3
+ An MCP server that allows AI agents to execute terminal commands on the host system.
4
+
5
+ ## Features
6
+
7
+ - **Execute Command**: Run shell commands and retrieve stdout, stderr, and exit code.
8
+ - **Security**: Strict allowlist system via `ALLOWED_COMMANDS` environment variable.
9
+ - **Cross-Platform**: Works on Linux, macOS, and Windows.
10
+
11
+ ## Prerequisites
12
+
13
+ - Node.js (version 18 or higher)
14
+
15
+ ## Configuration
16
+
17
+ ### Security: Allowed Commands
18
+
19
+ For security reasons, this server requires an explicit list of allowed commands. This is configured via the `ALLOWED_COMMANDS` environment variable.
20
+
21
+ - **Format**: Comma-separated list of command binaries (e.g., `ls,cat,echo`).
22
+ - **Wildcard**: Set to `*` to allow ALL commands (⚠️ **DANGEROUS**: Only use in trusted environments).
23
+
24
+ ### Security (Optional): Allowed Working Directory Roots
25
+
26
+ If you enable `cwd` (see tool input below), you can optionally restrict which working directories are allowed via `ALLOWED_CWD_ROOTS`.
27
+
28
+ - **Format**: Comma-separated list of allowed root paths.
29
+ - **Behavior**:
30
+ - If **unset or empty**, `cwd` is not restricted (any existing directory is allowed).
31
+ - If set, the resolved and canonical `cwd` must be within at least one configured root.
32
+ - If set and any configured root cannot be canonicalized (e.g., does not exist), requests that provide `cwd` are rejected (configuration error).
33
+
34
+ ## Usage
35
+
36
+ ### MCP Client Configuration
37
+
38
+ Add the following to your MCP client configuration (e.g., VS Code `settings.json`):
39
+
40
+ #### Basic Configuration
41
+
42
+ ```json
43
+ {
44
+ "mcpServers": {
45
+ "terminal-runner": {
46
+ "command": "npx",
47
+ "args": ["-y", "mcp-terminal-runner"],
48
+ "env": {
49
+ "ALLOWED_COMMANDS": "ls,cat,grep,echo"
50
+ }
51
+ }
52
+ }
53
+ }
54
+ ```
55
+
56
+ #### Configuration with Allowed Working Directories
57
+
58
+ ```json
59
+ {
60
+ "mcpServers": {
61
+ "terminal-runner": {
62
+ "command": "npx",
63
+ "args": ["-y", "mcp-terminal-runner"],
64
+ "env": {
65
+ "ALLOWED_COMMANDS": "ls,cat,grep,echo",
66
+ "ALLOWED_CWD_ROOTS": "/home/user/projects,/tmp"
67
+ }
68
+ }
69
+ }
70
+ }
71
+ ```
72
+
73
+ ### Available Tools
74
+
75
+ #### `execute_command`
76
+ Executes a shell command.
77
+
78
+ - **Input**:
79
+ - `command` (string): The full command string to execute (e.g., `ls -la src`).
80
+ - `cwd` (string, optional): Working directory to execute the command within.
81
+ - **Output**:
82
+ - Returns a YAML-formatted string containing:
83
+ - `exit_code`: The command's exit code.
84
+ - `stdout`: Standard output.
85
+ - `stderr`: Standard error.
86
+
87
+ ## Development
88
+
89
+ ### Setup
90
+
91
+ 1. Clone the repository:
92
+ ```bash
93
+ git clone <repository-url>
94
+ cd mcp-terminal-runner
95
+ ```
96
+
97
+ 2. Install dependencies:
98
+ ```bash
99
+ npm install
100
+ ```
101
+
102
+ 3. Build the project:
103
+ ```bash
104
+ npm run build
105
+ ```
106
+
107
+ ### Available Scripts
108
+
109
+ - `npm run build` - Build the TypeScript project
110
+ - `npm run dev` - Run in development mode
111
+ - `npm start` - Run the built JavaScript version
112
+ - `npm run check` - Check code with Ultracite
113
+ - `npm test` - Run tests with Vitest
114
+
115
+ ### Project Structure
116
+
117
+ ```
118
+ mcp-terminal-runner/
119
+ ├── src/
120
+ │ └── index.ts # Main server implementation
121
+ ├── dist/ # Built JavaScript files
122
+ ├── .husky/ # Git hooks
123
+ ├── biome.json # Biome configuration
124
+ ├── tsconfig.json # TypeScript configuration
125
+ ├── package.json # Project dependencies and scripts
126
+ └── README.md # This file
127
+ ```
128
+
129
+ ## License
130
+
131
+ MIT License - see LICENSE file for details.
132
+
133
+ ## Contributing
134
+
135
+ 1. Fork the repository
136
+ 2. Create a feature branch
137
+ 3. Make your changes
138
+ 4. Run `npm run quality` to ensure code quality
139
+ 5. Commit your changes (Husky will run pre-commit hooks)
140
+ 6. Push to your branch
141
+ 7. Create a Pull Request
142
+
143
+ ## Troubleshooting
144
+
145
+ ### Common Issues
146
+
147
+ 1. **Server not starting**: Ensure all dependencies are installed and the project is built
148
+ 2. **Tools not appearing**: Check that the MCP client configuration points to the correct path
149
+ 3. **Permission errors**: Make sure the built JavaScript file has execute permissions
150
+
151
+ ### Debug Mode
152
+
153
+ To enable debug logging, set the environment variable:
154
+ ```bash
155
+ DEBUG=mcp* npm start
156
+ ```
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ declare const server: McpServer;
4
+ export { server };
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAKA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AASpE,QAAA,MAAM,MAAM,WAUX,CAAC;AAwLF,OAAO,EAAE,MAAM,EAAE,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.server = void 0;
5
+ const node_child_process_1 = require("node:child_process");
6
+ const promises_1 = require("node:fs/promises");
7
+ const node_path_1 = require("node:path");
8
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
9
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
10
+ const args_tokenizer_1 = require("args-tokenizer");
11
+ const js_yaml_1 = require("js-yaml");
12
+ const zod_1 = require("zod");
13
+ // Hardcoded version to avoid import issues with outside rootDir
14
+ const version = '0.1.0';
15
+ const server = new mcp_js_1.McpServer({
16
+ name: 'mcp-terminal-runner',
17
+ version,
18
+ }, {
19
+ capabilities: {
20
+ tools: {},
21
+ },
22
+ });
23
+ exports.server = server;
24
+ const parseCommaSeparatedEnv = (value) => {
25
+ if (!value) {
26
+ return [];
27
+ }
28
+ return value
29
+ .split(',')
30
+ .map((item) => item.trim())
31
+ .filter((item) => item.length > 0);
32
+ };
33
+ const isWithinRoot = (root, target) => {
34
+ const rel = (0, node_path_1.relative)(root, target);
35
+ if (rel === '') {
36
+ return true;
37
+ }
38
+ const isOutsideRoot = rel.startsWith('..') || (0, node_path_1.isAbsolute)(rel);
39
+ return !isOutsideRoot;
40
+ };
41
+ const resolveAndValidateCwd = async (cwdInput) => {
42
+ const cwdResolved = (0, node_path_1.resolve)(process.cwd(), cwdInput);
43
+ try {
44
+ const cwdStat = await (0, promises_1.stat)(cwdResolved);
45
+ if (!cwdStat.isDirectory()) {
46
+ throw new Error(`cwd is not a directory: ${cwdInput}`);
47
+ }
48
+ }
49
+ catch (error) {
50
+ if (error instanceof Error &&
51
+ error.message.includes('cwd is not a directory')) {
52
+ throw error;
53
+ }
54
+ throw new Error(`cwd does not exist: ${cwdInput}`);
55
+ }
56
+ const cwdCanonical = await (0, promises_1.realpath)(cwdResolved);
57
+ const allowedRoots = parseCommaSeparatedEnv(process.env.ALLOWED_CWD_ROOTS);
58
+ if (allowedRoots.length === 0) {
59
+ return cwdResolved;
60
+ }
61
+ const canonicalRoots = await Promise.all(allowedRoots.map(async (root) => {
62
+ const rootResolved = (0, node_path_1.resolve)(process.cwd(), root);
63
+ try {
64
+ return await (0, promises_1.realpath)(rootResolved);
65
+ }
66
+ catch {
67
+ throw new Error(`Invalid configuration: ALLOWED_CWD_ROOTS contains an invalid root: ${root}`);
68
+ }
69
+ }));
70
+ const allowed = canonicalRoots.some((root) => isWithinRoot(root, cwdCanonical));
71
+ if (!allowed) {
72
+ throw new Error('cwd is not allowed by ALLOWED_CWD_ROOTS');
73
+ }
74
+ return cwdResolved;
75
+ };
76
+ const runCommand = async (bin, args, cwd) => new Promise((resolvePromise, rejectPromise) => {
77
+ const child = (0, node_child_process_1.spawn)(bin, args, {
78
+ cwd,
79
+ shell: false,
80
+ windowsHide: true,
81
+ });
82
+ let stdout = '';
83
+ let stderr = '';
84
+ child.stdout?.setEncoding('utf8');
85
+ child.stderr?.setEncoding('utf8');
86
+ child.stdout?.on('data', (chunk) => {
87
+ stdout += chunk;
88
+ });
89
+ child.stderr?.on('data', (chunk) => {
90
+ stderr += chunk;
91
+ });
92
+ child.on('error', (error) => {
93
+ rejectPromise(error);
94
+ });
95
+ child.on('close', (code) => {
96
+ resolvePromise({
97
+ exitCode: typeof code === 'number' ? code : 1,
98
+ stdout,
99
+ stderr,
100
+ });
101
+ });
102
+ });
103
+ server.tool('execute_command', 'Execute a shell command', {
104
+ command: zod_1.z.string().describe('The shell command to execute'),
105
+ cwd: zod_1.z
106
+ .string()
107
+ .optional()
108
+ .describe('Optional working directory to execute the command within'),
109
+ }, async (args) => {
110
+ const [bin, ...commandArgs] = (0, args_tokenizer_1.tokenizeArgs)(args.command);
111
+ const allowedCommands = parseCommaSeparatedEnv(process.env.ALLOWED_COMMANDS);
112
+ try {
113
+ if (!(allowedCommands.includes('*') || allowedCommands.includes(bin))) {
114
+ throw new Error(`Command "${bin}" is not allowed, allowed commands: ${allowedCommands.length > 0 ? allowedCommands.join(', ') : '(none)'}`);
115
+ }
116
+ const cwdResolved = args.cwd
117
+ ? await resolveAndValidateCwd(args.cwd)
118
+ : undefined;
119
+ const result = await runCommand(bin, commandArgs, cwdResolved);
120
+ return {
121
+ content: [
122
+ {
123
+ type: 'text',
124
+ text: (0, js_yaml_1.dump)({
125
+ exit_code: result.exitCode,
126
+ stdout: result.stdout,
127
+ stderr: result.stderr,
128
+ }),
129
+ },
130
+ ],
131
+ };
132
+ }
133
+ catch (error) {
134
+ return {
135
+ content: [
136
+ {
137
+ type: 'text',
138
+ text: `Error executing command: ${error instanceof Error ? error.message : String(error)}`,
139
+ },
140
+ ],
141
+ isError: true,
142
+ };
143
+ }
144
+ });
145
+ async function main() {
146
+ const transport = new stdio_js_1.StdioServerTransport();
147
+ await server.connect(transport);
148
+ }
149
+ if (require.main === module) {
150
+ main().catch((error) => {
151
+ console.error('Fatal error in main:', error);
152
+ process.exit(1);
153
+ });
154
+ }
155
+ //# 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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const promises_1 = require("node:fs/promises");
4
+ const node_os_1 = require("node:os");
5
+ const node_path_1 = require("node:path");
6
+ const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
7
+ const inMemory_js_1 = require("@modelcontextprotocol/sdk/inMemory.js");
8
+ const js_yaml_1 = require("js-yaml");
9
+ const index_js_2 = require("./index.js");
10
+ describe('MCP Server', () => {
11
+ let client;
12
+ beforeEach(async () => {
13
+ const [clientTransport, serverTransport] = inMemory_js_1.InMemoryTransport.createLinkedPair();
14
+ // Connect server
15
+ await index_js_2.server.connect(serverTransport);
16
+ // Connect client
17
+ client = new index_js_1.Client({ name: 'test-client', version: '1.0.0' }, { capabilities: {} });
18
+ await client.connect(clientTransport);
19
+ });
20
+ afterEach(async () => {
21
+ await client.close();
22
+ await index_js_2.server.close();
23
+ process.env.ALLOWED_COMMANDS = '';
24
+ process.env.ALLOWED_CWD_ROOTS = '';
25
+ });
26
+ it('should list execute_command tool', async () => {
27
+ const result = await client.listTools();
28
+ expect(result.tools).toBeDefined();
29
+ expect(result.tools.some((t) => t.name === 'execute_command')).toBe(true);
30
+ });
31
+ it('should execute allowed command', async () => {
32
+ process.env.ALLOWED_COMMANDS = 'echo';
33
+ const result = await client.callTool({
34
+ name: 'execute_command',
35
+ arguments: { command: 'echo hello' },
36
+ });
37
+ expect(result.content).toBeDefined();
38
+ expect(result.content[0].type).toBe('text');
39
+ const text = result.content[0].text;
40
+ const output = (0, js_yaml_1.load)(text);
41
+ expect(output.exit_code).toBe(0);
42
+ expect(output.stdout).toContain('hello');
43
+ });
44
+ it('should fail for disallowed command', async () => {
45
+ process.env.ALLOWED_COMMANDS = 'ls';
46
+ const result = await client.callTool({
47
+ name: 'execute_command',
48
+ arguments: { command: 'echo hello' },
49
+ });
50
+ expect(result.isError).toBe(true);
51
+ expect(result.content[0].text).toContain('not allowed');
52
+ });
53
+ it('should allow all commands with wildcard', async () => {
54
+ process.env.ALLOWED_COMMANDS = '*';
55
+ const result = await client.callTool({
56
+ name: 'execute_command',
57
+ arguments: { command: 'echo hello' },
58
+ });
59
+ expect(result.content).toBeDefined();
60
+ const text = result.content[0].text;
61
+ const output = (0, js_yaml_1.load)(text);
62
+ expect(output.exit_code).toBe(0);
63
+ });
64
+ it('should execute command within absolute cwd', async () => {
65
+ process.env.ALLOWED_COMMANDS = 'node';
66
+ const cwdDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'mcp-terminal-runner-cwd-'));
67
+ const result = await client.callTool({
68
+ name: 'execute_command',
69
+ arguments: {
70
+ command: 'node -p process.cwd()',
71
+ cwd: cwdDir,
72
+ },
73
+ });
74
+ if (result.isError) {
75
+ const message = result.content?.[0]?.text ?? 'unknown error';
76
+ throw new Error(message);
77
+ }
78
+ expect(result.isError).not.toBe(true);
79
+ const text = result.content[0].text;
80
+ const output = (0, js_yaml_1.load)(text);
81
+ expect(output.exit_code).toBe(0);
82
+ expect(String(output.stdout)).toContain(cwdDir);
83
+ });
84
+ it('should execute command within relative cwd', async () => {
85
+ process.env.ALLOWED_COMMANDS = 'node';
86
+ const baseDir = (0, node_path_1.join)(process.cwd(), '.tmp');
87
+ await (0, promises_1.mkdir)(baseDir, { recursive: true });
88
+ const cwdDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)(baseDir, 'mcp-terminal-runner-cwd-rel-'));
89
+ const relativeCwd = (0, node_path_1.relative)(process.cwd(), cwdDir);
90
+ const result = await client.callTool({
91
+ name: 'execute_command',
92
+ arguments: {
93
+ command: 'node -p process.cwd()',
94
+ cwd: relativeCwd,
95
+ },
96
+ });
97
+ if (result.isError) {
98
+ const message = result.content?.[0]?.text ?? 'unknown error';
99
+ throw new Error(message);
100
+ }
101
+ expect(result.isError).not.toBe(true);
102
+ const text = result.content[0].text;
103
+ const output = (0, js_yaml_1.load)(text);
104
+ expect(output.exit_code).toBe(0);
105
+ expect(String(output.stdout)).toContain(cwdDir);
106
+ });
107
+ it('should fail when cwd does not exist', async () => {
108
+ process.env.ALLOWED_COMMANDS = 'node';
109
+ const result = await client.callTool({
110
+ name: 'execute_command',
111
+ arguments: {
112
+ command: 'node -p process.cwd()',
113
+ cwd: 'does-not-exist',
114
+ },
115
+ });
116
+ expect(result.isError).toBe(true);
117
+ expect(result.content[0].text).toContain('cwd does not exist');
118
+ });
119
+ it('should restrict cwd when ALLOWED_CWD_ROOTS is set', async () => {
120
+ process.env.ALLOWED_COMMANDS = 'node';
121
+ const rootDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'mcp-terminal-runner-root-'));
122
+ const childDir = (0, node_path_1.join)(rootDir, 'frontend');
123
+ await (0, promises_1.mkdir)(childDir, { recursive: true });
124
+ process.env.ALLOWED_CWD_ROOTS = rootDir;
125
+ const allowedResult = await client.callTool({
126
+ name: 'execute_command',
127
+ arguments: {
128
+ command: 'node -p process.cwd()',
129
+ cwd: childDir,
130
+ },
131
+ });
132
+ expect(allowedResult.isError).not.toBe(true);
133
+ const allowedText = allowedResult.content[0].text;
134
+ const allowedOutput = (0, js_yaml_1.load)(allowedText);
135
+ expect(allowedOutput.exit_code).toBe(0);
136
+ expect(String(allowedOutput.stdout)).toContain(childDir);
137
+ const disallowedResult = await client.callTool({
138
+ name: 'execute_command',
139
+ arguments: {
140
+ command: 'node -p process.cwd()',
141
+ cwd: (0, node_os_1.tmpdir)(),
142
+ },
143
+ });
144
+ expect(disallowedResult.isError).toBe(true);
145
+ expect(disallowedResult.content[0].text).toContain('ALLOWED_CWD_ROOTS');
146
+ });
147
+ it('should fail with configuration error when ALLOWED_CWD_ROOTS contains invalid root', async () => {
148
+ process.env.ALLOWED_COMMANDS = 'node';
149
+ const rootDir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'mcp-terminal-runner-root-invalid-'));
150
+ const childDir = (0, node_path_1.join)(rootDir, 'frontend');
151
+ await (0, promises_1.mkdir)(childDir, { recursive: true });
152
+ process.env.ALLOWED_CWD_ROOTS = `${rootDir},${(0, node_path_1.join)(rootDir, 'does-not-exist')}`;
153
+ const result = await client.callTool({
154
+ name: 'execute_command',
155
+ arguments: {
156
+ command: 'node -p process.cwd()',
157
+ cwd: childDir,
158
+ },
159
+ });
160
+ expect(result.isError).toBe(true);
161
+ expect(result.content[0].text).toContain('Invalid configuration');
162
+ expect(result.content[0].text).toContain('ALLOWED_CWD_ROOTS');
163
+ });
164
+ });
165
+ //# sourceMappingURL=index.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "mcp-terminal-runner",
3
+ "version": "0.1.0",
4
+ "main": "index.js",
5
+ "bin": {
6
+ "mcp-terminal-runner": "dist/index.js"
7
+ },
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "dev": "ts-node src/index.ts",
11
+ "start": "node dist/index.js",
12
+ "check": "ultracite check",
13
+ "fix": "ultracite fix",
14
+ "typecheck": "tsc --noEmit",
15
+ "quality": "npm run typecheck && npm run check",
16
+ "test": "vitest --run",
17
+ "prepare": "husky"
18
+ },
19
+ "author": "atman-33",
20
+ "license": "MIT",
21
+ "description": "MCP server for executing shell commands safely",
22
+ "files": [
23
+ "dist",
24
+ "README.md",
25
+ "package.json",
26
+ "LICENSE"
27
+ ],
28
+ "devDependencies": {
29
+ "@biomejs/biome": "^2.3.10",
30
+ "@types/js-yaml": "^4.0.9",
31
+ "@types/node": "^24.3.0",
32
+ "husky": "^9.1.7",
33
+ "ts-node": "^10.9.2",
34
+ "typescript": "^5.9.2",
35
+ "ultracite": "^6.4.2",
36
+ "vitest": "^4.0.16"
37
+ },
38
+ "dependencies": {
39
+ "@modelcontextprotocol/sdk": "^1.17.4",
40
+ "args-tokenizer": "^0.3.0",
41
+ "js-yaml": "^4.1.1",
42
+ "zod": "^3.25.76",
43
+ "zod-to-json-schema": "^3.24.6"
44
+ }
45
+ }