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 +156 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +155 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +165 -0
- package/dist/index.test.js.map +1 -0
- package/package.json +45 -0
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
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -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 @@
|
|
|
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
|
+
}
|