just-bash-mcp 2.6.0 → 2.8.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/package.json CHANGED
@@ -1,33 +1,33 @@
1
1
  {
2
2
  "name": "just-bash-mcp",
3
- "version": "2.6.0",
3
+ "version": "2.8.0",
4
4
  "description": "MCP server providing a sandboxed bash environment using just-bash",
5
5
  "type": "module",
6
- "main": "./build/index.js",
7
- "types": "./build/index.d.ts",
6
+ "main": "./src/index.ts",
8
7
  "exports": {
9
8
  ".": {
10
- "types": "./build/index.d.ts",
11
- "import": "./build/index.js"
9
+ "import": "./src/index.ts"
12
10
  }
13
11
  },
14
12
  "bin": {
15
- "just-bash-mcp": "build/index.js"
13
+ "just-bash-mcp": "src/index.ts"
16
14
  },
17
15
  "files": [
18
- "build",
16
+ "src",
17
+ "tsconfig.json",
19
18
  "README.md",
20
19
  "LICENSE"
21
20
  ],
22
21
  "scripts": {
23
- "build": "tsc",
24
- "start": "node build/index.js",
25
- "dev": "bun src/index.ts",
26
- "typecheck": "bun tsc --noEmit",
27
- "lint": "bunx @biomejs/biome@latest lint",
28
- "format": "bunx @biomejs/biome@latest format --write",
29
- "check": "bunx @biomejs/biome@latest check --write",
30
- "prepublishOnly": "bun run build"
22
+ "start": "node src/index.ts",
23
+ "dev": "node --watch src/index.ts",
24
+ "lint": "oxlint --type-aware",
25
+ "lint:fix": "oxlint --type-aware --fix",
26
+ "typecheck": "oxlint --type-aware --type-check",
27
+ "format": "oxfmt --write src/",
28
+ "format:check": "oxfmt --check src/",
29
+ "check": "oxlint --type-aware --type-check && oxfmt --check src/",
30
+ "ci": "oxlint --type-aware --type-check && oxfmt --check src/"
31
31
  },
32
32
  "keywords": [
33
33
  "mcp",
@@ -56,16 +56,18 @@
56
56
  },
57
57
  "homepage": "https://github.com/dalist1/just-bash-mcp#readme",
58
58
  "engines": {
59
- "node": ">=18.0.0"
59
+ "node": ">=22.0.0"
60
60
  },
61
+ "packageManager": "bun@1.3.8",
61
62
  "dependencies": {
62
- "@modelcontextprotocol/sdk": "^1.25.2",
63
- "just-bash": "^2.7.0",
64
- "zod": "^4.3.5"
63
+ "@modelcontextprotocol/sdk": "^1.26.0",
64
+ "just-bash": "^2.9.6",
65
+ "zod": "^4.3.6"
65
66
  },
66
67
  "devDependencies": {
67
- "@biomejs/biome": "^2.3.11",
68
- "@types/node": "^25.0.6",
69
- "typescript": "^5.9.3"
68
+ "@types/node": "^25.2.1",
69
+ "oxfmt": "^0.28.0",
70
+ "oxlint": "^1.43.0",
71
+ "oxlint-tsgolint": "^0.11.4"
70
72
  }
71
73
  }
@@ -0,0 +1,358 @@
1
+ /**
2
+ * Configuration module for just-bash-mcp
3
+ * Handles environment variable parsing and configuration building
4
+ */
5
+
6
+ import {
7
+ type BashLogger,
8
+ type BashOptions,
9
+ type CommandName,
10
+ InMemoryFs,
11
+ MountableFs,
12
+ type MountConfig,
13
+ type NetworkConfig,
14
+ OverlayFs,
15
+ ReadWriteFs,
16
+ } from "just-bash";
17
+
18
+ // ============================================================================
19
+ // Types
20
+ // ============================================================================
21
+
22
+ export type HttpMethod = "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS";
23
+
24
+ export interface TraceEvent {
25
+ category: string;
26
+ name: string;
27
+ durationMs: number;
28
+ details?: Record<string, unknown>;
29
+ }
30
+
31
+ export type TraceCallback = (event: TraceEvent) => void;
32
+
33
+ // ============================================================================
34
+ // Environment Variable Parsing
35
+ // ============================================================================
36
+
37
+ function parseEnvString(key: string, defaultValue: string): string {
38
+ return process.env[key] || defaultValue;
39
+ }
40
+
41
+ function parseEnvBoolean(key: string, defaultValue: boolean): boolean {
42
+ return process.env[key] === "true" ? true : defaultValue;
43
+ }
44
+
45
+ function parseEnvInt(key: string, defaultValue: number): number {
46
+ const value = process.env[key];
47
+ if (!value) return defaultValue;
48
+ const parsed = Number.parseInt(value, 10);
49
+ return Number.isNaN(parsed) ? defaultValue : parsed;
50
+ }
51
+
52
+ function parseEnvStringArray(key: string): string[] {
53
+ return process.env[key]?.split(",").filter(Boolean) || [];
54
+ }
55
+
56
+ // ============================================================================
57
+ // Configuration Constants
58
+ // ============================================================================
59
+
60
+ export interface Config {
61
+ readonly VERSION: string;
62
+ readonly SERVER_NAME: string;
63
+ readonly OVERLAY_ROOT: string | undefined;
64
+ readonly READ_WRITE_ROOT: string | undefined;
65
+ readonly MOUNTS_CONFIG: string | undefined;
66
+ readonly INITIAL_CWD: string;
67
+ readonly ALLOW_NETWORK: boolean;
68
+ readonly ALLOWED_URL_PREFIXES: string[];
69
+ readonly ALLOWED_METHODS: HttpMethod[];
70
+ readonly MAX_REDIRECTS: number;
71
+ readonly NETWORK_TIMEOUT_MS: number;
72
+ readonly MAX_RESPONSE_SIZE: number | undefined;
73
+ readonly MAX_CALL_DEPTH: number;
74
+ readonly MAX_COMMAND_COUNT: number;
75
+ readonly MAX_LOOP_ITERATIONS: number;
76
+ readonly MAX_AWK_ITERATIONS: number;
77
+ readonly MAX_SED_ITERATIONS: number;
78
+ readonly MAX_JQ_ITERATIONS: number;
79
+ readonly MAX_GLOB_OPERATIONS: number;
80
+ readonly MAX_STRING_LENGTH: number;
81
+ readonly MAX_ARRAY_ELEMENTS: number;
82
+ readonly MAX_HEREDOC_SIZE: number;
83
+ readonly MAX_SUBSTITUTION_DEPTH: number;
84
+ readonly MAX_SQLITE_TIMEOUT_MS: number;
85
+ readonly MAX_PYTHON_TIMEOUT_MS: number;
86
+ readonly MAX_OUTPUT_LENGTH: number;
87
+ readonly MAX_FILE_READ_SIZE: number | undefined;
88
+ readonly ENABLE_LOGGING: boolean;
89
+ readonly ENABLE_TRACING: boolean;
90
+ readonly ENABLE_PYTHON: boolean;
91
+ readonly ENABLE_DEFENSE_IN_DEPTH: boolean;
92
+ readonly OVERLAY_READ_ONLY: boolean;
93
+ readonly ALLOWED_COMMANDS: CommandName[] | undefined;
94
+ }
95
+
96
+ function getAllowedMethods(): HttpMethod[] {
97
+ const methods = parseEnvStringArray("JUST_BASH_ALLOWED_METHODS");
98
+ return methods.length > 0 ? (methods as HttpMethod[]) : ["GET", "HEAD"];
99
+ }
100
+
101
+ function getAllowedCommands(): CommandName[] | undefined {
102
+ const commands = parseEnvStringArray("JUST_BASH_ALLOWED_COMMANDS");
103
+ return commands.length > 0 ? (commands as CommandName[]) : undefined;
104
+ }
105
+
106
+ function parseEnvOptionalInt(key: string): number | undefined {
107
+ const value = process.env[key];
108
+ if (!value) return undefined;
109
+ const parsed = Number.parseInt(value, 10);
110
+ return Number.isNaN(parsed) ? undefined : parsed;
111
+ }
112
+
113
+ export const config: Config = {
114
+ // Server info
115
+ VERSION: "2.8.0",
116
+ SERVER_NAME: "just-bash-mcp",
117
+
118
+ // Filesystem configuration
119
+ OVERLAY_ROOT: process.env.JUST_BASH_OVERLAY_ROOT,
120
+ READ_WRITE_ROOT: process.env.JUST_BASH_READ_WRITE_ROOT,
121
+ MOUNTS_CONFIG: process.env.JUST_BASH_MOUNTS,
122
+ INITIAL_CWD: parseEnvString("JUST_BASH_CWD", "/home/user"),
123
+
124
+ // Network configuration
125
+ ALLOW_NETWORK: parseEnvBoolean("JUST_BASH_ALLOW_NETWORK", false),
126
+ ALLOWED_URL_PREFIXES: parseEnvStringArray("JUST_BASH_ALLOWED_URLS"),
127
+ ALLOWED_METHODS: getAllowedMethods(),
128
+ MAX_REDIRECTS: parseEnvInt("JUST_BASH_MAX_REDIRECTS", 20),
129
+ NETWORK_TIMEOUT_MS: parseEnvInt("JUST_BASH_NETWORK_TIMEOUT_MS", 30000),
130
+ MAX_RESPONSE_SIZE: parseEnvOptionalInt("JUST_BASH_MAX_RESPONSE_SIZE"),
131
+
132
+ // Execution limits
133
+ MAX_CALL_DEPTH: parseEnvInt("JUST_BASH_MAX_CALL_DEPTH", 100),
134
+ MAX_COMMAND_COUNT: parseEnvInt("JUST_BASH_MAX_COMMAND_COUNT", 10000),
135
+ MAX_LOOP_ITERATIONS: parseEnvInt("JUST_BASH_MAX_LOOP_ITERATIONS", 10000),
136
+ MAX_AWK_ITERATIONS: parseEnvInt("JUST_BASH_MAX_AWK_ITERATIONS", 10000),
137
+ MAX_SED_ITERATIONS: parseEnvInt("JUST_BASH_MAX_SED_ITERATIONS", 10000),
138
+ MAX_JQ_ITERATIONS: parseEnvInt("JUST_BASH_MAX_JQ_ITERATIONS", 10000),
139
+ MAX_GLOB_OPERATIONS: parseEnvInt("JUST_BASH_MAX_GLOB_OPERATIONS", 100000),
140
+ MAX_STRING_LENGTH: parseEnvInt("JUST_BASH_MAX_STRING_LENGTH", 10485760),
141
+ MAX_ARRAY_ELEMENTS: parseEnvInt("JUST_BASH_MAX_ARRAY_ELEMENTS", 100000),
142
+ MAX_HEREDOC_SIZE: parseEnvInt("JUST_BASH_MAX_HEREDOC_SIZE", 10485760),
143
+ MAX_SUBSTITUTION_DEPTH: parseEnvInt("JUST_BASH_MAX_SUBSTITUTION_DEPTH", 50),
144
+ MAX_SQLITE_TIMEOUT_MS: parseEnvInt("JUST_BASH_MAX_SQLITE_TIMEOUT_MS", 5000),
145
+ MAX_PYTHON_TIMEOUT_MS: parseEnvInt("JUST_BASH_MAX_PYTHON_TIMEOUT_MS", 30000),
146
+
147
+ // Output limits
148
+ MAX_OUTPUT_LENGTH: parseEnvInt("JUST_BASH_MAX_OUTPUT_LENGTH", 30000),
149
+
150
+ // Filesystem limits
151
+ MAX_FILE_READ_SIZE: parseEnvOptionalInt("JUST_BASH_MAX_FILE_READ_SIZE"),
152
+
153
+ // Debugging
154
+ ENABLE_LOGGING: parseEnvBoolean("JUST_BASH_ENABLE_LOGGING", false),
155
+ ENABLE_TRACING: parseEnvBoolean("JUST_BASH_ENABLE_TRACING", false),
156
+
157
+ // Feature flags
158
+ ENABLE_PYTHON: parseEnvBoolean("JUST_BASH_ENABLE_PYTHON", false),
159
+ ENABLE_DEFENSE_IN_DEPTH: parseEnvBoolean("JUST_BASH_DEFENSE_IN_DEPTH", false),
160
+ OVERLAY_READ_ONLY: parseEnvBoolean("JUST_BASH_OVERLAY_READ_ONLY", false),
161
+
162
+ // Command filtering
163
+ ALLOWED_COMMANDS: getAllowedCommands(),
164
+ };
165
+
166
+ // ============================================================================
167
+ // Logger and Trace Callback
168
+ // ============================================================================
169
+
170
+ export const bashLogger: BashLogger | undefined = config.ENABLE_LOGGING
171
+ ? {
172
+ info(message: string, data?: Record<string, unknown>): void {
173
+ if (data) {
174
+ console.error(`[just-bash] INFO: ${message}`, data);
175
+ } else {
176
+ console.error(`[just-bash] INFO: ${message}`);
177
+ }
178
+ },
179
+ debug(message: string, data?: Record<string, unknown>): void {
180
+ if (data) {
181
+ console.error(`[just-bash] DEBUG: ${message}`, data);
182
+ } else {
183
+ console.error(`[just-bash] DEBUG: ${message}`);
184
+ }
185
+ },
186
+ }
187
+ : undefined;
188
+
189
+ export const traceCallback: TraceCallback | undefined = config.ENABLE_TRACING
190
+ ? (event: TraceEvent) => {
191
+ if (event.details) {
192
+ console.error(
193
+ `[just-bash] TRACE: ${event.category}/${event.name} ${event.durationMs}ms`,
194
+ JSON.stringify(event.details),
195
+ );
196
+ } else {
197
+ console.error(`[just-bash] TRACE: ${event.category}/${event.name} ${event.durationMs}ms`);
198
+ }
199
+ }
200
+ : undefined;
201
+
202
+ // ============================================================================
203
+ // Configuration Builders
204
+ // ============================================================================
205
+
206
+ export function buildNetworkConfig(): NetworkConfig | undefined {
207
+ if (!config.ALLOW_NETWORK) {
208
+ return undefined;
209
+ }
210
+
211
+ if (config.ALLOWED_URL_PREFIXES.length > 0) {
212
+ return {
213
+ allowedUrlPrefixes: config.ALLOWED_URL_PREFIXES,
214
+ allowedMethods: config.ALLOWED_METHODS,
215
+ maxRedirects: config.MAX_REDIRECTS,
216
+ timeoutMs: config.NETWORK_TIMEOUT_MS,
217
+ ...(config.MAX_RESPONSE_SIZE !== undefined && {
218
+ maxResponseSize: config.MAX_RESPONSE_SIZE,
219
+ }),
220
+ };
221
+ }
222
+
223
+ return {
224
+ dangerouslyAllowFullInternetAccess: true,
225
+ maxRedirects: config.MAX_REDIRECTS,
226
+ timeoutMs: config.NETWORK_TIMEOUT_MS,
227
+ ...(config.MAX_RESPONSE_SIZE !== undefined && {
228
+ maxResponseSize: config.MAX_RESPONSE_SIZE,
229
+ }),
230
+ };
231
+ }
232
+
233
+ export function buildExecutionLimits(): NonNullable<BashOptions["executionLimits"]> {
234
+ return {
235
+ maxCallDepth: config.MAX_CALL_DEPTH,
236
+ maxCommandCount: config.MAX_COMMAND_COUNT,
237
+ maxLoopIterations: config.MAX_LOOP_ITERATIONS,
238
+ maxAwkIterations: config.MAX_AWK_ITERATIONS,
239
+ maxSedIterations: config.MAX_SED_ITERATIONS,
240
+ maxJqIterations: config.MAX_JQ_ITERATIONS,
241
+ maxGlobOperations: config.MAX_GLOB_OPERATIONS,
242
+ maxStringLength: config.MAX_STRING_LENGTH,
243
+ maxArrayElements: config.MAX_ARRAY_ELEMENTS,
244
+ maxHeredocSize: config.MAX_HEREDOC_SIZE,
245
+ maxSubstitutionDepth: config.MAX_SUBSTITUTION_DEPTH,
246
+ maxSqliteTimeoutMs: config.MAX_SQLITE_TIMEOUT_MS,
247
+ maxPythonTimeoutMs: config.MAX_PYTHON_TIMEOUT_MS,
248
+ };
249
+ }
250
+
251
+ export function parseMountsConfig(): MountConfig[] {
252
+ if (!config.MOUNTS_CONFIG) return [];
253
+ try {
254
+ const parsed: unknown = JSON.parse(config.MOUNTS_CONFIG);
255
+ if (!Array.isArray(parsed)) return [];
256
+ return parsed.map(
257
+ (mount: { mountPoint: string; root: string; type?: string; readOnly?: boolean }) => {
258
+ const fsType = mount.type || "overlay";
259
+ const maxFileReadSize = config.MAX_FILE_READ_SIZE;
260
+ const filesystem =
261
+ fsType === "readwrite"
262
+ ? new ReadWriteFs({
263
+ root: mount.root,
264
+ ...(maxFileReadSize !== undefined && { maxFileReadSize }),
265
+ })
266
+ : new OverlayFs({
267
+ root: mount.root,
268
+ readOnly: mount.readOnly,
269
+ ...(maxFileReadSize !== undefined && { maxFileReadSize }),
270
+ });
271
+ return { mountPoint: mount.mountPoint, filesystem };
272
+ },
273
+ );
274
+ } catch {
275
+ return [];
276
+ }
277
+ }
278
+
279
+ // ============================================================================
280
+ // Environment Variables Documentation
281
+ // ============================================================================
282
+
283
+ export const ENVIRONMENT_VARIABLES = {
284
+ JUST_BASH_OVERLAY_ROOT: "Real directory to mount as overlay (read from disk, write to memory)",
285
+ JUST_BASH_OVERLAY_READ_ONLY:
286
+ "If true, all writes to overlay filesystem throw errors (default: false)",
287
+ JUST_BASH_READ_WRITE_ROOT: "Real directory with read-write access",
288
+ JUST_BASH_MOUNTS: "JSON array of mount configurations (supports type, readOnly fields)",
289
+ JUST_BASH_CWD: "Initial working directory (default: /home/user)",
290
+ JUST_BASH_ALLOW_NETWORK: "Enable network access (default: false)",
291
+ JUST_BASH_ALLOWED_URLS: "Comma-separated URL prefixes to allow",
292
+ JUST_BASH_ALLOWED_METHODS: "Comma-separated HTTP methods (default: GET,HEAD)",
293
+ JUST_BASH_ALLOWED_COMMANDS: "Comma-separated list of allowed commands",
294
+ JUST_BASH_MAX_REDIRECTS: "Max HTTP redirects (default: 20)",
295
+ JUST_BASH_NETWORK_TIMEOUT_MS: "Network timeout in ms (default: 30000)",
296
+ JUST_BASH_MAX_RESPONSE_SIZE: "Max network response body size in bytes (default: 10MB)",
297
+ JUST_BASH_MAX_CALL_DEPTH: "Max recursion depth (default: 100)",
298
+ JUST_BASH_MAX_COMMAND_COUNT: "Max commands per execution (default: 10000)",
299
+ JUST_BASH_MAX_LOOP_ITERATIONS: "Max bash loop iterations (default: 10000)",
300
+ JUST_BASH_MAX_AWK_ITERATIONS: "Max AWK while/for loop iterations (default: 10000)",
301
+ JUST_BASH_MAX_SED_ITERATIONS: "Max sed branch loop iterations (default: 10000)",
302
+ JUST_BASH_MAX_JQ_ITERATIONS: "Max jq loop iterations (default: 10000)",
303
+ JUST_BASH_MAX_GLOB_OPERATIONS: "Max glob filesystem operations (default: 100000)",
304
+ JUST_BASH_MAX_STRING_LENGTH: "Max string length in bytes (default: 10485760 = 10MB)",
305
+ JUST_BASH_MAX_ARRAY_ELEMENTS: "Max array elements (default: 100000)",
306
+ JUST_BASH_MAX_HEREDOC_SIZE: "Max heredoc size in bytes (default: 10485760 = 10MB)",
307
+ JUST_BASH_MAX_SUBSTITUTION_DEPTH: "Max command substitution nesting depth (default: 50)",
308
+ JUST_BASH_MAX_SQLITE_TIMEOUT_MS: "SQLite timeout in ms (default: 5000)",
309
+ JUST_BASH_MAX_PYTHON_TIMEOUT_MS: "Python timeout in ms (default: 30000)",
310
+ JUST_BASH_MAX_OUTPUT_LENGTH: "Max output length (default: 30000)",
311
+ JUST_BASH_MAX_FILE_READ_SIZE:
312
+ "Max file read size in bytes for OverlayFs/ReadWriteFs (default: 10MB)",
313
+ JUST_BASH_ENABLE_LOGGING: "Enable debug logging (default: false)",
314
+ JUST_BASH_ENABLE_TRACING: "Enable performance tracing (default: false)",
315
+ JUST_BASH_ENABLE_PYTHON: "Enable python3/python commands via Pyodide (default: false)",
316
+ JUST_BASH_DEFENSE_IN_DEPTH:
317
+ "Enable defense-in-depth mode that patches dangerous JS globals (default: false)",
318
+ } as const;
319
+
320
+ // ============================================================================
321
+ // Command Categories Documentation
322
+ // ============================================================================
323
+
324
+ export const COMMAND_CATEGORIES = {
325
+ fileOperations: "cat, cp, file, ln, ls, mkdir, mv, readlink, rm, rmdir, split, stat, touch, tree",
326
+ textProcessing:
327
+ "awk, base64, column, comm, cut, diff, expand, fold, grep (egrep, fgrep), head, join, md5sum, nl, od, paste, printf, rev, rg (ripgrep), sed, sha1sum, sha256sum, sort, strings, tac, tail, tr, unexpand, uniq, wc, xargs",
328
+ dataProcessing:
329
+ "jq (JSON), python3/python (Python via Pyodide), sqlite3 (SQLite), xan (CSV), yq (YAML/XML/TOML/CSV)",
330
+ compression: "gzip (gunzip, zcat), tar",
331
+ navigation:
332
+ "basename, cd, dirname, du, echo, env, export, find, hostname, printenv, pwd, tee, whoami",
333
+ shellUtilities:
334
+ "alias, bash, chmod, clear, date, expr, false, help, history, seq, sh, sleep, time, timeout, true, unalias, which",
335
+ network: "curl, html-to-markdown (when network enabled)",
336
+ } as const;
337
+
338
+ // ============================================================================
339
+ // Features Documentation
340
+ // ============================================================================
341
+
342
+ export const FEATURES = {
343
+ customCommands: "Define custom TypeScript commands using defineCommand()",
344
+ rawScript: "Preserve leading whitespace in scripts (useful for here-docs)",
345
+ logger: "Optional execution logging via BashLogger interface",
346
+ trace: "Performance profiling via TraceCallback",
347
+ commandFilter: "Restrict available commands via JUST_BASH_ALLOWED_COMMANDS env var",
348
+ sandboxApi: "Vercel Sandbox compatible API via bash_sandbox_* tools",
349
+ python: "Python support via Pyodide (opt-in via JUST_BASH_ENABLE_PYTHON=true)",
350
+ defenseInDepth:
351
+ "Defense-in-depth mode that monkey-patches dangerous JS globals during execution (opt-in via JUST_BASH_DEFENSE_IN_DEPTH=true)",
352
+ overlayReadOnly:
353
+ "Read-only overlay filesystem mode (opt-in via JUST_BASH_OVERLAY_READ_ONLY=true)",
354
+ networkResponseSize:
355
+ "Configurable max network response body size via JUST_BASH_MAX_RESPONSE_SIZE",
356
+ fileReadSizeLimit:
357
+ "Configurable max file read size for OverlayFs/ReadWriteFs via JUST_BASH_MAX_FILE_READ_SIZE",
358
+ } as const;
package/src/index.ts ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * just-bash-mcp - MCP Server for sandboxed bash execution
5
+ *
6
+ * A Model Context Protocol (MCP) server that provides AI agents with a
7
+ * secure, sandboxed bash environment powered by just-bash from Vercel Labs.
8
+ *
9
+ * @see https://github.com/vercel-labs/just-bash
10
+ * @see https://modelcontextprotocol.io
11
+ */
12
+
13
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
14
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
15
+
16
+ import { config } from "./config/index.ts";
17
+ import { registerAllTools } from "./tools/index.ts";
18
+
19
+ // ============================================================================
20
+ // Server Initialization
21
+ // ============================================================================
22
+
23
+ const server = new McpServer({
24
+ name: config.SERVER_NAME,
25
+ version: config.VERSION,
26
+ });
27
+
28
+ // Register all tools with the server
29
+ registerAllTools(server);
30
+
31
+ // ============================================================================
32
+ // Start Server
33
+ // ============================================================================
34
+
35
+ const transport = new StdioServerTransport();
36
+ await server.connect(transport);
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Bash instance management
3
+ * Handles creation and lifecycle of Bash instances
4
+ */
5
+
6
+ import {
7
+ Bash,
8
+ type BashOptions,
9
+ type CustomCommand,
10
+ InMemoryFs,
11
+ MountableFs,
12
+ OverlayFs,
13
+ ReadWriteFs,
14
+ Sandbox,
15
+ type SandboxOptions,
16
+ } from "just-bash";
17
+
18
+ import {
19
+ bashLogger,
20
+ buildExecutionLimits,
21
+ buildNetworkConfig,
22
+ config,
23
+ parseMountsConfig,
24
+ traceCallback,
25
+ } from "../config/index.ts";
26
+
27
+ // ============================================================================
28
+ // Bash Instance Factory
29
+ // ============================================================================
30
+
31
+ /**
32
+ * Create a new Bash instance with the given configuration
33
+ */
34
+ export function createBashInstance(
35
+ files?: Record<string, string>,
36
+ customCommands?: CustomCommand[],
37
+ env?: Record<string, string>,
38
+ ): Bash {
39
+ const networkConfig = buildNetworkConfig();
40
+ const executionLimits = buildExecutionLimits();
41
+
42
+ const baseOptions: BashOptions = {
43
+ network: networkConfig,
44
+ executionLimits,
45
+ files,
46
+ env,
47
+ logger: bashLogger,
48
+ trace: traceCallback,
49
+ customCommands,
50
+ commands: config.ALLOWED_COMMANDS,
51
+ python: config.ENABLE_PYTHON,
52
+ defenseInDepth: config.ENABLE_DEFENSE_IN_DEPTH,
53
+ };
54
+
55
+ // Check for mountable filesystem configuration
56
+ const mounts = parseMountsConfig();
57
+ if (mounts.length > 0) {
58
+ const mountableFs = new MountableFs({
59
+ base: new InMemoryFs(),
60
+ mounts,
61
+ });
62
+ return new Bash({
63
+ ...baseOptions,
64
+ fs: mountableFs,
65
+ cwd: config.INITIAL_CWD,
66
+ });
67
+ }
68
+
69
+ // Check for read-write filesystem configuration
70
+ if (config.READ_WRITE_ROOT) {
71
+ const rwfs = new ReadWriteFs({
72
+ root: config.READ_WRITE_ROOT,
73
+ ...(config.MAX_FILE_READ_SIZE !== undefined && {
74
+ maxFileReadSize: config.MAX_FILE_READ_SIZE,
75
+ }),
76
+ });
77
+ return new Bash({
78
+ ...baseOptions,
79
+ fs: rwfs,
80
+ cwd: config.READ_WRITE_ROOT,
81
+ });
82
+ }
83
+
84
+ // Check for overlay filesystem configuration
85
+ if (config.OVERLAY_ROOT) {
86
+ const overlay = new OverlayFs({
87
+ root: config.OVERLAY_ROOT,
88
+ readOnly: config.OVERLAY_READ_ONLY,
89
+ ...(config.MAX_FILE_READ_SIZE !== undefined && {
90
+ maxFileReadSize: config.MAX_FILE_READ_SIZE,
91
+ }),
92
+ });
93
+ return new Bash({
94
+ ...baseOptions,
95
+ fs: overlay,
96
+ cwd: overlay.getMountPoint(),
97
+ });
98
+ }
99
+
100
+ // Default: in-memory filesystem
101
+ return new Bash({
102
+ ...baseOptions,
103
+ cwd: config.INITIAL_CWD,
104
+ });
105
+ }
106
+
107
+ // ============================================================================
108
+ // Persistent Bash Instance (singleton pattern)
109
+ // ============================================================================
110
+
111
+ let persistentBash: Bash | null = null;
112
+
113
+ /**
114
+ * Get or create the persistent Bash instance
115
+ */
116
+ export function getPersistentBash(): Bash {
117
+ if (!persistentBash) {
118
+ persistentBash = createBashInstance();
119
+ }
120
+ return persistentBash;
121
+ }
122
+
123
+ /**
124
+ * Reset the persistent Bash instance
125
+ */
126
+ export function resetPersistentBash(): void {
127
+ persistentBash = null;
128
+ }
129
+
130
+ // ============================================================================
131
+ // Persistent Sandbox Instance (singleton pattern)
132
+ // ============================================================================
133
+
134
+ let persistentSandbox: Sandbox | null = null;
135
+
136
+ /**
137
+ * Get or create the persistent Sandbox instance
138
+ */
139
+ export async function getPersistentSandbox(): Promise<Sandbox> {
140
+ if (!persistentSandbox) {
141
+ const networkConfig = buildNetworkConfig();
142
+ const options: SandboxOptions = {
143
+ cwd: config.INITIAL_CWD,
144
+ network: networkConfig,
145
+ maxCallDepth: config.MAX_CALL_DEPTH,
146
+ maxCommandCount: config.MAX_COMMAND_COUNT,
147
+ maxLoopIterations: config.MAX_LOOP_ITERATIONS,
148
+ ...(config.OVERLAY_ROOT && { overlayRoot: config.OVERLAY_ROOT }),
149
+ };
150
+ persistentSandbox = await Sandbox.create(options);
151
+ }
152
+ return persistentSandbox;
153
+ }
154
+
155
+ /**
156
+ * Reset the persistent Sandbox instance
157
+ */
158
+ export function resetPersistentSandbox(): void {
159
+ persistentSandbox = null;
160
+ }