just-bash-mcp 2.8.0 → 2.9.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.
package/README.md CHANGED
@@ -7,15 +7,20 @@ An MCP (Model Context Protocol) server that provides a sandboxed bash environmen
7
7
 
8
8
  Execute bash commands in a secure, isolated environment with an in-memory virtual filesystem.
9
9
 
10
- Built on top of [`just-bash`](https://github.com/vercel-labs/just-bash) v2.5.2.
10
+ Built on top of [`just-bash`](https://github.com/vercel-labs/just-bash) v2.10.2.
11
11
 
12
- ## What's New in v2.1.0
12
+ ## What's New in v2.9.0
13
13
 
14
+ - **Synced with upstream `just-bash` v2.10.2** - Latest upstream commands, APIs, and type exports
15
+ - **Defense-in-depth mode** - Opt-in monkey-patching of dangerous JS globals (`JUST_BASH_DEFENSE_IN_DEPTH=true`)
16
+ - **Python support** - Python3 via Pyodide (`JUST_BASH_ENABLE_PYTHON=true`)
17
+ - **Vercel Sandbox API** - Compatible `bash_sandbox_*` tools for isolated execution
18
+ - **oxlint/oxfmt toolchain** - Replaced tsc/biome with faster oxlint and oxfmt
19
+ - **Configurable limits** - Fine-grained control over glob ops, string length, array size, heredoc size, and more
14
20
  - **`rg` (ripgrep)** - Fast regex search with `--files`, `-d`, `--stats`, `-t markdown`
15
21
  - **`tar`** - Archive support with compression
16
22
  - **MountableFS** - Mount multiple filesystems at different paths
17
23
  - **ReadWriteFS** - Direct read-write access to real directories
18
- - **Multi-level glob patterns** - Improved `**/*.ts` style matching
19
24
 
20
25
  ## Features
21
26
 
@@ -120,6 +125,16 @@ Add to your MCP settings:
120
125
  | `JUST_BASH_MAX_CALL_DEPTH` | Maximum function recursion depth | `100` |
121
126
  | `JUST_BASH_MAX_COMMAND_COUNT` | Maximum total commands per execution | `10000` |
122
127
  | `JUST_BASH_MAX_LOOP_ITERATIONS` | Maximum iterations per loop | `10000` |
128
+ | `JUST_BASH_ENABLE_PYTHON` | Enable Python3 via Pyodide (`true`/`false`) | `false` |
129
+ | `JUST_BASH_DEFENSE_IN_DEPTH` | Enable defense-in-depth mode (`true`/`false`) | `false` |
130
+ | `JUST_BASH_DEFENSE_IN_DEPTH_AUDIT` | Audit mode: log violations but don't block | `false` |
131
+ | `JUST_BASH_DEFENSE_IN_DEPTH_LOG` | Log violations to console | `false` |
132
+ | `JUST_BASH_OVERLAY_READ_ONLY` | OverlayFS read-only mode | `false` |
133
+ | `JUST_BASH_MAX_RESPONSE_SIZE` | Max network response body size (bytes) | `10485760` |
134
+ | `JUST_BASH_MAX_FILE_READ_SIZE` | Max file read size for OverlayFs/ReadWriteFs | `10485760` |
135
+ | `JUST_BASH_ALLOWED_COMMANDS` | Comma-separated command allow-list | all |
136
+ | `JUST_BASH_ENABLE_LOGGING` | Enable execution logging | `false` |
137
+ | `JUST_BASH_ENABLE_TRACING` | Enable performance tracing | `false` |
123
138
 
124
139
  ## Tools
125
140
 
@@ -149,9 +164,28 @@ Reset the persistent bash environment, clearing all files and state.
149
164
 
150
165
  File operations in the persistent environment.
151
166
 
167
+ ### `bash_direct_read` / `bash_direct_write`
168
+
169
+ Direct filesystem read/write operations (bypass shell execution).
170
+
152
171
  ### `bash_info`
153
172
 
154
- Get information about the bash environment configuration.
173
+ Get information about the bash environment configuration, including defense-in-depth violation stats.
174
+
175
+ ### `bash_get_cwd` / `bash_get_env`
176
+
177
+ Get current working directory or environment variables.
178
+
179
+ ### Vercel Sandbox API
180
+
181
+ Compatible with the Vercel Sandbox API:
182
+
183
+ - `bash_sandbox_run` - Run a command in the sandbox
184
+ - `bash_sandbox_write_files` - Write multiple files at once
185
+ - `bash_sandbox_read_file` - Read a file (supports base64 encoding)
186
+ - `bash_sandbox_mkdir` - Create a directory
187
+ - `bash_sandbox_stop` - Stop and clean up the sandbox
188
+ - `bash_sandbox_reset` - Reset the sandbox state
155
189
 
156
190
  ## Supported Commands
157
191
 
@@ -235,6 +269,26 @@ Get information about the bash environment configuration.
235
269
  - Execution limits protect against infinite loops and recursion
236
270
  - No binary/WASM execution
237
271
  - Network disabled by default; when enabled, URL and method allow-lists enforced
272
+ - **Defense-in-depth mode** (opt-in): Monkey-patches dangerous JS globals (`Function`, `eval`, `setTimeout`, `process`, etc.) during script execution to block escape vectors
273
+ - **SecurityViolationLogger**: Tracks all defense-in-depth violations with full stats accessible via `bash_info`
274
+ - **Rich network error classification**: `NetworkAccessDeniedError`, `TooManyRedirectsError`, `RedirectNotAllowedError` for precise error messages
275
+
276
+ ## Upstream API Coverage
277
+
278
+ This wrapper integrates the full public API surface of `just-bash` v2.10.2:
279
+
280
+ | Category | Exports Used |
281
+ |----------|-------------|
282
+ | Core | `Bash`, `BashOptions`, `ExecOptions`, `BashExecResult` |
283
+ | Commands | `CommandName`, `AllCommandName`, `getCommandNames`, `getNetworkCommandNames`, `getPythonCommandNames` |
284
+ | Custom Commands | `defineCommand`, `CustomCommand`, `LazyCommand` |
285
+ | Filesystem | `InMemoryFs`, `OverlayFs`, `ReadWriteFs`, `MountableFs`, `IFileSystem` |
286
+ | Network | `NetworkConfig`, `NetworkAccessDeniedError`, `TooManyRedirectsError`, `RedirectNotAllowedError` |
287
+ | Sandbox | `Sandbox`, `SandboxCommand`, `SandboxOptions`, `OutputMessage` |
288
+ | Security | `DefenseInDepthBox`, `SecurityViolationLogger`, `SecurityViolationError`, `createConsoleViolationCallback` |
289
+ | Trace | `TraceCallback`, `TraceEvent` |
290
+
291
+ All types are re-exported from `src/types.ts` for downstream consumers.
238
292
 
239
293
  ## License
240
294
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "just-bash-mcp",
3
- "version": "2.8.0",
3
+ "version": "2.9.1",
4
4
  "description": "MCP server providing a sandboxed bash environment using just-bash",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -61,13 +61,13 @@
61
61
  "packageManager": "bun@1.3.8",
62
62
  "dependencies": {
63
63
  "@modelcontextprotocol/sdk": "^1.26.0",
64
- "just-bash": "^2.9.6",
64
+ "just-bash": "^2.10.2",
65
65
  "zod": "^4.3.6"
66
66
  },
67
67
  "devDependencies": {
68
- "@types/node": "^25.2.1",
68
+ "@types/node": "^25.2.3",
69
69
  "oxfmt": "^0.28.0",
70
- "oxlint": "^1.43.0",
71
- "oxlint-tsgolint": "^0.11.4"
70
+ "oxlint": "^1.48.0",
71
+ "oxlint-tsgolint": "^0.11.5"
72
72
  }
73
73
  }
@@ -3,23 +3,24 @@
3
3
  * Handles environment variable parsing and configuration building
4
4
  */
5
5
 
6
+ import { readFileSync } from "node:fs";
7
+ import { dirname, join } from "node:path";
8
+ import { fileURLToPath } from "node:url";
6
9
  import {
7
10
  type BashLogger,
8
11
  type BashOptions,
9
12
  type CommandName,
10
- InMemoryFs,
11
- MountableFs,
13
+ type DefenseInDepthConfig,
12
14
  type MountConfig,
13
15
  type NetworkConfig,
14
16
  OverlayFs,
15
17
  ReadWriteFs,
18
+ SecurityViolationLogger,
19
+ createConsoleViolationCallback,
16
20
  } from "just-bash";
17
21
 
18
- // ============================================================================
19
- // Types
20
- // ============================================================================
21
-
22
- export type HttpMethod = "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS";
22
+ // Re-export upstream types used by other modules
23
+ export type { DefenseInDepthConfig };
23
24
 
24
25
  export interface TraceEvent {
25
26
  category: string;
@@ -30,6 +31,12 @@ export interface TraceEvent {
30
31
 
31
32
  export type TraceCallback = (event: TraceEvent) => void;
32
33
 
34
+ // ============================================================================
35
+ // Types
36
+ // ============================================================================
37
+
38
+ export type HttpMethod = "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS";
39
+
33
40
  // ============================================================================
34
41
  // Environment Variable Parsing
35
42
  // ============================================================================
@@ -89,10 +96,28 @@ export interface Config {
89
96
  readonly ENABLE_TRACING: boolean;
90
97
  readonly ENABLE_PYTHON: boolean;
91
98
  readonly ENABLE_DEFENSE_IN_DEPTH: boolean;
99
+ readonly DEFENSE_IN_DEPTH_AUDIT: boolean;
100
+ readonly DEFENSE_IN_DEPTH_LOG: boolean;
92
101
  readonly OVERLAY_READ_ONLY: boolean;
93
102
  readonly ALLOWED_COMMANDS: CommandName[] | undefined;
94
103
  }
95
104
 
105
+ function readPackageVersion(relativePath: string): string {
106
+ try {
107
+ const __dirname = dirname(fileURLToPath(import.meta.url));
108
+ const packagePath = join(__dirname, relativePath);
109
+ const packageJson = JSON.parse(readFileSync(packagePath, "utf-8")) as { version?: string };
110
+ return packageJson.version || "unknown";
111
+ } catch {
112
+ return "unknown";
113
+ }
114
+ }
115
+
116
+ export const WRAPPER_VERSION = readPackageVersion("../../package.json");
117
+ export const UPSTREAM_JUST_BASH_VERSION = readPackageVersion(
118
+ "../../node_modules/just-bash/package.json",
119
+ );
120
+
96
121
  function getAllowedMethods(): HttpMethod[] {
97
122
  const methods = parseEnvStringArray("JUST_BASH_ALLOWED_METHODS");
98
123
  return methods.length > 0 ? (methods as HttpMethod[]) : ["GET", "HEAD"];
@@ -112,7 +137,7 @@ function parseEnvOptionalInt(key: string): number | undefined {
112
137
 
113
138
  export const config: Config = {
114
139
  // Server info
115
- VERSION: "2.8.0",
140
+ VERSION: WRAPPER_VERSION,
116
141
  SERVER_NAME: "just-bash-mcp",
117
142
 
118
143
  // Filesystem configuration
@@ -157,6 +182,8 @@ export const config: Config = {
157
182
  // Feature flags
158
183
  ENABLE_PYTHON: parseEnvBoolean("JUST_BASH_ENABLE_PYTHON", false),
159
184
  ENABLE_DEFENSE_IN_DEPTH: parseEnvBoolean("JUST_BASH_DEFENSE_IN_DEPTH", false),
185
+ DEFENSE_IN_DEPTH_AUDIT: parseEnvBoolean("JUST_BASH_DEFENSE_IN_DEPTH_AUDIT", false),
186
+ DEFENSE_IN_DEPTH_LOG: parseEnvBoolean("JUST_BASH_DEFENSE_IN_DEPTH_LOG", false),
160
187
  OVERLAY_READ_ONLY: parseEnvBoolean("JUST_BASH_OVERLAY_READ_ONLY", false),
161
188
 
162
189
  // Command filtering
@@ -186,7 +213,7 @@ export const bashLogger: BashLogger | undefined = config.ENABLE_LOGGING
186
213
  }
187
214
  : undefined;
188
215
 
189
- export const traceCallback: TraceCallback | undefined = config.ENABLE_TRACING
216
+ export const traceCallback: BashOptions["trace"] = config.ENABLE_TRACING
190
217
  ? (event: TraceEvent) => {
191
218
  if (event.details) {
192
219
  console.error(
@@ -199,6 +226,39 @@ export const traceCallback: TraceCallback | undefined = config.ENABLE_TRACING
199
226
  }
200
227
  : undefined;
201
228
 
229
+ // ============================================================================
230
+ // Defense-in-Depth Configuration
231
+ // ============================================================================
232
+
233
+ /**
234
+ * Shared SecurityViolationLogger instance for tracking violations across
235
+ * all Bash instances. Exposed so info-tools can report violation stats.
236
+ */
237
+ export const violationLogger = new SecurityViolationLogger();
238
+
239
+ /**
240
+ * Build the defense-in-depth configuration from environment variables.
241
+ * Returns `false` when disabled, or a full DefenseInDepthConfig object.
242
+ */
243
+ export function buildDefenseInDepthConfig(): DefenseInDepthConfig | false {
244
+ if (!config.ENABLE_DEFENSE_IN_DEPTH) {
245
+ return false;
246
+ }
247
+
248
+ const consoleCallback = config.DEFENSE_IN_DEPTH_LOG
249
+ ? createConsoleViolationCallback()
250
+ : undefined;
251
+
252
+ return {
253
+ enabled: true,
254
+ auditMode: config.DEFENSE_IN_DEPTH_AUDIT,
255
+ onViolation: (violation: { type: string; target: string; details: string }) => {
256
+ violationLogger.record(violation);
257
+ consoleCallback?.(violation);
258
+ },
259
+ };
260
+ }
261
+
202
262
  // ============================================================================
203
263
  // Configuration Builders
204
264
  // ============================================================================
@@ -315,6 +375,10 @@ export const ENVIRONMENT_VARIABLES = {
315
375
  JUST_BASH_ENABLE_PYTHON: "Enable python3/python commands via Pyodide (default: false)",
316
376
  JUST_BASH_DEFENSE_IN_DEPTH:
317
377
  "Enable defense-in-depth mode that patches dangerous JS globals (default: false)",
378
+ JUST_BASH_DEFENSE_IN_DEPTH_AUDIT:
379
+ "Audit mode: log violations but don't block them (default: false, requires DEFENSE_IN_DEPTH=true)",
380
+ JUST_BASH_DEFENSE_IN_DEPTH_LOG:
381
+ "Log violations to console via createConsoleViolationCallback (default: false)",
318
382
  } as const;
319
383
 
320
384
  // ============================================================================
@@ -340,19 +404,25 @@ export const COMMAND_CATEGORIES = {
340
404
  // ============================================================================
341
405
 
342
406
  export const FEATURES = {
343
- customCommands: "Define custom TypeScript commands using defineCommand()",
407
+ customCommands:
408
+ "Define custom TypeScript commands using defineCommand() from just-bash, supports lazy-loading via LazyCommand",
344
409
  rawScript: "Preserve leading whitespace in scripts (useful for here-docs)",
345
410
  logger: "Optional execution logging via BashLogger interface",
346
- trace: "Performance profiling via TraceCallback",
411
+ trace: "Performance profiling via TraceCallback (upstream type)",
347
412
  commandFilter: "Restrict available commands via JUST_BASH_ALLOWED_COMMANDS env var",
348
- sandboxApi: "Vercel Sandbox compatible API via bash_sandbox_* tools",
413
+ sandboxApi:
414
+ "Vercel Sandbox compatible API via bash_sandbox_* tools (run, write, read, mkdir, stop, reset)",
349
415
  python: "Python support via Pyodide (opt-in via JUST_BASH_ENABLE_PYTHON=true)",
350
416
  defenseInDepth:
351
- "Defense-in-depth mode that monkey-patches dangerous JS globals during execution (opt-in via JUST_BASH_DEFENSE_IN_DEPTH=true)",
417
+ "Defense-in-depth with SecurityViolationLogger, audit mode, and console logging (opt-in via JUST_BASH_DEFENSE_IN_DEPTH=true)",
352
418
  overlayReadOnly:
353
419
  "Read-only overlay filesystem mode (opt-in via JUST_BASH_OVERLAY_READ_ONLY=true)",
354
420
  networkResponseSize:
355
421
  "Configurable max network response body size via JUST_BASH_MAX_RESPONSE_SIZE",
356
422
  fileReadSizeLimit:
357
423
  "Configurable max file read size for OverlayFs/ReadWriteFs via JUST_BASH_MAX_FILE_READ_SIZE",
424
+ networkErrorHandling:
425
+ "Rich network error classification: NetworkAccessDeniedError, TooManyRedirectsError, RedirectNotAllowedError",
426
+ securityViolationTracking:
427
+ "SecurityViolationLogger tracks all defense-in-depth violations with stats via bash_info",
358
428
  } as const;
@@ -1,33 +1,79 @@
1
1
  /**
2
2
  * Bash instance management
3
3
  * Handles creation and lifecycle of Bash instances
4
+ *
5
+ * Uses all upstream just-bash APIs:
6
+ * - Bash, Sandbox, SandboxCommand for execution
7
+ * - DefenseInDepthBox with SecurityViolationLogger for security monitoring
8
+ * - defineCommand for custom command registration
9
+ * - All filesystem variants (InMemoryFs, MountableFs, OverlayFs, ReadWriteFs)
10
+ * - Network error classes for rich error reporting (handled in exec-tools/sandbox-tools)
4
11
  */
5
12
 
6
13
  import {
7
14
  Bash,
8
15
  type BashOptions,
9
16
  type CustomCommand,
17
+ DefenseInDepthBox,
10
18
  InMemoryFs,
11
19
  MountableFs,
12
20
  OverlayFs,
13
21
  ReadWriteFs,
14
22
  Sandbox,
15
23
  type SandboxOptions,
24
+ defineCommand,
16
25
  } from "just-bash";
17
26
 
18
27
  import {
19
28
  bashLogger,
29
+ buildDefenseInDepthConfig,
20
30
  buildExecutionLimits,
21
31
  buildNetworkConfig,
22
32
  config,
23
33
  parseMountsConfig,
24
34
  traceCallback,
35
+ violationLogger,
25
36
  } from "../config/index.ts";
26
37
 
27
38
  // ============================================================================
28
39
  // Bash Instance Factory
29
40
  // ============================================================================
30
41
 
42
+ // ============================================================================
43
+ // Defense-in-Depth Box (singleton)
44
+ // ============================================================================
45
+
46
+ let defenseInDepthBox: DefenseInDepthBox | null = null;
47
+
48
+ /**
49
+ * Get or create the shared DefenseInDepthBox instance.
50
+ * Returns null if defense-in-depth is disabled.
51
+ */
52
+ export function getDefenseInDepthBox(): DefenseInDepthBox | null {
53
+ const didConfig = buildDefenseInDepthConfig();
54
+ if (!didConfig) return null;
55
+
56
+ if (!defenseInDepthBox) {
57
+ defenseInDepthBox = new DefenseInDepthBox(didConfig);
58
+ }
59
+ return defenseInDepthBox;
60
+ }
61
+
62
+ /**
63
+ * Get the shared SecurityViolationLogger for querying violation stats.
64
+ */
65
+ export { violationLogger } from "../config/index.ts";
66
+
67
+ /**
68
+ * Re-export defineCommand so downstream consumers can create custom commands
69
+ * using the upstream API without importing just-bash directly.
70
+ */
71
+ export { defineCommand };
72
+
73
+ // ============================================================================
74
+ // Bash Instance Factory
75
+ // ============================================================================
76
+
31
77
  /**
32
78
  * Create a new Bash instance with the given configuration
33
79
  */
@@ -38,6 +84,7 @@ export function createBashInstance(
38
84
  ): Bash {
39
85
  const networkConfig = buildNetworkConfig();
40
86
  const executionLimits = buildExecutionLimits();
87
+ const defenseInDepthConfig = buildDefenseInDepthConfig();
41
88
 
42
89
  const baseOptions: BashOptions = {
43
90
  network: networkConfig,
@@ -49,7 +96,7 @@ export function createBashInstance(
49
96
  customCommands,
50
97
  commands: config.ALLOWED_COMMANDS,
51
98
  python: config.ENABLE_PYTHON,
52
- defenseInDepth: config.ENABLE_DEFENSE_IN_DEPTH,
99
+ defenseInDepth: defenseInDepthConfig,
53
100
  };
54
101
 
55
102
  // Check for mountable filesystem configuration
@@ -134,7 +181,8 @@ export function resetPersistentBash(): void {
134
181
  let persistentSandbox: Sandbox | null = null;
135
182
 
136
183
  /**
137
- * Get or create the persistent Sandbox instance
184
+ * Get or create the persistent Sandbox instance.
185
+ * Passes all available configuration including network and filesystem options.
138
186
  */
139
187
  export async function getPersistentSandbox(): Promise<Sandbox> {
140
188
  if (!persistentSandbox) {
@@ -153,8 +201,12 @@ export async function getPersistentSandbox(): Promise<Sandbox> {
153
201
  }
154
202
 
155
203
  /**
156
- * Reset the persistent Sandbox instance
204
+ * Reset the persistent Sandbox instance.
205
+ * Calls Sandbox.stop() to clean up resources before releasing.
157
206
  */
158
- export function resetPersistentSandbox(): void {
207
+ export async function resetPersistentSandbox(): Promise<void> {
208
+ if (persistentSandbox) {
209
+ await persistentSandbox.stop();
210
+ }
159
211
  persistentSandbox = null;
160
212
  }
@@ -1,13 +1,45 @@
1
1
  /**
2
2
  * Bash execution tools
3
3
  * Core tools for executing bash commands
4
+ *
5
+ * Uses upstream network error classes for rich error classification:
6
+ * - NetworkAccessDeniedError: URL not in allowlist
7
+ * - TooManyRedirectsError: Redirect limit exceeded
8
+ * - RedirectNotAllowedError: Redirect target not in allowlist
9
+ * - SecurityViolationError: Defense-in-depth violation detected
4
10
  */
5
11
 
6
12
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
13
+ import {
14
+ NetworkAccessDeniedError,
15
+ RedirectNotAllowedError,
16
+ SecurityViolationError,
17
+ TooManyRedirectsError,
18
+ } from "just-bash";
7
19
  import { z } from "zod/v4";
8
20
  import { createErrorResponse, formatExecResult } from "../utils/index.ts";
9
21
  import { createBashInstance, getPersistentBash, resetPersistentBash } from "./bash-instance.ts";
10
22
 
23
+ /**
24
+ * Classify errors from just-bash into user-friendly messages.
25
+ * Uses upstream error classes for precise classification.
26
+ */
27
+ function classifyError(error: unknown, prefix: string) {
28
+ if (error instanceof NetworkAccessDeniedError) {
29
+ return createErrorResponse(error, `${prefix} [Network Access Denied]`);
30
+ }
31
+ if (error instanceof TooManyRedirectsError) {
32
+ return createErrorResponse(error, `${prefix} [Too Many Redirects]`);
33
+ }
34
+ if (error instanceof RedirectNotAllowedError) {
35
+ return createErrorResponse(error, `${prefix} [Redirect Not Allowed]`);
36
+ }
37
+ if (error instanceof SecurityViolationError) {
38
+ return createErrorResponse(error, `${prefix} [Security Violation]`);
39
+ }
40
+ return createErrorResponse(error, prefix);
41
+ }
42
+
11
43
  /**
12
44
  * Register bash execution tools with the MCP server
13
45
  */
@@ -63,7 +95,7 @@ export function registerExecTools(server: McpServer): void {
63
95
  const result = await bash.exec(command, { cwd, env, rawScript });
64
96
  return formatExecResult(result);
65
97
  } catch (error) {
66
- return createErrorResponse(error, "Execution error");
98
+ return classifyError(error, "Execution error");
67
99
  }
68
100
  },
69
101
  );
@@ -104,7 +136,7 @@ export function registerExecTools(server: McpServer): void {
104
136
  const result = await bash.exec(command, { cwd, env, rawScript });
105
137
  return formatExecResult(result);
106
138
  } catch (error) {
107
- return createErrorResponse(error, "Execution error");
139
+ return classifyError(error, "Execution error");
108
140
  }
109
141
  },
110
142
  );
@@ -13,10 +13,13 @@ import { registerSandboxTools } from "./sandbox-tools.ts";
13
13
  // Re-export bash instance utilities
14
14
  export {
15
15
  createBashInstance,
16
+ defineCommand,
17
+ getDefenseInDepthBox,
16
18
  getPersistentBash,
17
19
  getPersistentSandbox,
18
20
  resetPersistentBash,
19
21
  resetPersistentSandbox,
22
+ violationLogger,
20
23
  } from "./bash-instance.ts";
21
24
  // Re-export individual registrations for fine-grained control
22
25
  export { registerExecTools } from "./exec-tools.ts";
@@ -1,13 +1,14 @@
1
1
  /**
2
2
  * Information and state tools
3
3
  * Tools for getting information about the bash environment
4
+ *
5
+ * Uses upstream command registry types (AllCommandName, CommandName) and
6
+ * SecurityViolationLogger for defense-in-depth violation reporting.
4
7
  */
5
8
 
6
- import { readFileSync } from "node:fs";
7
- import { dirname, join } from "node:path";
8
- import { fileURLToPath } from "node:url";
9
9
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
10
  import {
11
+ type AllCommandName,
11
12
  type CommandName,
12
13
  getCommandNames,
13
14
  getNetworkCommandNames,
@@ -20,25 +21,11 @@ import {
20
21
  ENVIRONMENT_VARIABLES,
21
22
  FEATURES,
22
23
  parseMountsConfig,
24
+ UPSTREAM_JUST_BASH_VERSION,
25
+ violationLogger,
23
26
  } from "../config/index.ts";
24
27
  import { createErrorResponse, createJsonResponse } from "../utils/index.ts";
25
- import { getPersistentBash } from "./bash-instance.ts";
26
-
27
- function getUpstreamVersion(): string {
28
- try {
29
- // Resolve the just-bash package directory relative to this module
30
- const __dirname = dirname(fileURLToPath(import.meta.url));
31
- const pkgPath = join(__dirname, "..", "..", "node_modules", "just-bash", "package.json");
32
- const pkg: { version: string } = JSON.parse(readFileSync(pkgPath, "utf-8")) as {
33
- version: string;
34
- };
35
- return pkg.version;
36
- } catch {
37
- return "unknown";
38
- }
39
- }
40
-
41
- const UPSTREAM_VERSION = getUpstreamVersion();
28
+ import { getDefenseInDepthBox, getPersistentBash } from "./bash-instance.ts";
42
29
 
43
30
  /**
44
31
  * Register information tools with the MCP server
@@ -67,12 +54,33 @@ export function registerInfoTools(server: McpServer): void {
67
54
  // Get actual available commands (respects ALLOWED_COMMANDS filter)
68
55
  const allBuiltinCommands = getCommandNames();
69
56
  const availableCommands = config.ALLOWED_COMMANDS
70
- ? allBuiltinCommands.filter((cmd) => config.ALLOWED_COMMANDS?.includes(cmd as CommandName))
57
+ ? allBuiltinCommands.filter((cmd) =>
58
+ config.ALLOWED_COMMANDS?.includes(cmd as AllCommandName as CommandName),
59
+ )
71
60
  : allBuiltinCommands;
72
61
 
62
+ // Build defense-in-depth status with violation stats
63
+ const didBox = getDefenseInDepthBox();
64
+ const defenseInDepthStatus = config.ENABLE_DEFENSE_IN_DEPTH
65
+ ? {
66
+ enabled: true,
67
+ auditMode: config.DEFENSE_IN_DEPTH_AUDIT,
68
+ consoleLogging: config.DEFENSE_IN_DEPTH_LOG,
69
+ boxActive: didBox?.isActive() ?? false,
70
+ ...(didBox && {
71
+ stats: didBox.getStats(),
72
+ }),
73
+ violations: {
74
+ total: violationLogger.getTotalCount(),
75
+ hasViolations: violationLogger.hasViolations(),
76
+ summary: violationLogger.getSummary(),
77
+ },
78
+ }
79
+ : { enabled: false };
80
+
73
81
  const info = {
74
82
  version: config.VERSION,
75
- upstreamVersion: UPSTREAM_VERSION,
83
+ upstreamVersion: UPSTREAM_JUST_BASH_VERSION,
76
84
  fsMode,
77
85
  fsRoot: config.READ_WRITE_ROOT || config.OVERLAY_ROOT || null,
78
86
  overlayReadOnly: config.OVERLAY_ROOT ? config.OVERLAY_READ_ONLY : null,
@@ -88,7 +96,7 @@ export function registerInfoTools(server: McpServer): void {
88
96
  loggingEnabled: config.ENABLE_LOGGING,
89
97
  tracingEnabled: config.ENABLE_TRACING,
90
98
  pythonEnabled: config.ENABLE_PYTHON,
91
- defenseInDepthEnabled: config.ENABLE_DEFENSE_IN_DEPTH,
99
+ defenseInDepth: defenseInDepthStatus,
92
100
  commandFilter: config.ALLOWED_COMMANDS || null,
93
101
  executionLimits: buildExecutionLimits(),
94
102
  availableCommands,
@@ -1,9 +1,21 @@
1
1
  /**
2
2
  * Sandbox API tools
3
3
  * Vercel Sandbox compatible tools for execution in an isolated environment
4
+ *
5
+ * Uses upstream Sandbox/SandboxCommand APIs:
6
+ * - Sandbox.create(), runCommand(), writeFiles(), readFile(), mkDir(), stop()
7
+ * - SandboxCommand: wait(), stdout(), stderr(), output(), logs(), kill()
8
+ * - Sandbox.domain getter for domain info
9
+ * - OutputMessage type for streaming output
4
10
  */
5
11
 
6
12
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
13
+ import {
14
+ NetworkAccessDeniedError,
15
+ RedirectNotAllowedError,
16
+ SecurityViolationError,
17
+ TooManyRedirectsError,
18
+ } from "just-bash";
7
19
  import { z } from "zod/v4";
8
20
  import { config } from "../config/index.ts";
9
21
  import {
@@ -14,6 +26,25 @@ import {
14
26
  } from "../utils/index.ts";
15
27
  import { getPersistentSandbox, resetPersistentSandbox } from "./bash-instance.ts";
16
28
 
29
+ /**
30
+ * Classify errors from just-bash into user-friendly messages.
31
+ */
32
+ function classifyError(error: unknown, prefix: string) {
33
+ if (error instanceof NetworkAccessDeniedError) {
34
+ return createErrorResponse(error, `${prefix} [Network Access Denied]`);
35
+ }
36
+ if (error instanceof TooManyRedirectsError) {
37
+ return createErrorResponse(error, `${prefix} [Too Many Redirects]`);
38
+ }
39
+ if (error instanceof RedirectNotAllowedError) {
40
+ return createErrorResponse(error, `${prefix} [Redirect Not Allowed]`);
41
+ }
42
+ if (error instanceof SecurityViolationError) {
43
+ return createErrorResponse(error, `${prefix} [Security Violation]`);
44
+ }
45
+ return createErrorResponse(error, prefix);
46
+ }
47
+
17
48
  /**
18
49
  * Register Vercel Sandbox compatible tools with the MCP server
19
50
  */
@@ -57,7 +88,7 @@ export function registerSandboxTools(server: McpServer): void {
57
88
  result.exitCode !== 0,
58
89
  );
59
90
  } catch (error) {
60
- return createErrorResponse(error, "Sandbox error");
91
+ return classifyError(error, "Sandbox error");
61
92
  }
62
93
  },
63
94
  );
@@ -82,7 +113,7 @@ export function registerSandboxTools(server: McpServer): void {
82
113
  `Successfully wrote ${Object.keys(files).length} file(s): ${Object.keys(files).join(", ")}`,
83
114
  );
84
115
  } catch (error) {
85
- return createErrorResponse(error, "Write error");
116
+ return classifyError(error, "Write error");
86
117
  }
87
118
  },
88
119
  );
@@ -96,12 +127,13 @@ export function registerSandboxTools(server: McpServer): void {
96
127
  description: "Read a file from the sandbox environment.",
97
128
  inputSchema: {
98
129
  path: z.string().describe("The file path to read"),
130
+ encoding: z.enum(["utf-8", "base64"]).optional().describe("File encoding (default: utf-8)"),
99
131
  },
100
132
  },
101
- async ({ path }: { path: string }) => {
133
+ async ({ path, encoding = "utf-8" }: { path: string; encoding?: "utf-8" | "base64" }) => {
102
134
  try {
103
135
  const sandbox = await getPersistentSandbox();
104
- const content = await sandbox.readFile(path);
136
+ const content = await sandbox.readFile(path, encoding);
105
137
 
106
138
  return {
107
139
  content: [
@@ -112,7 +144,7 @@ export function registerSandboxTools(server: McpServer): void {
112
144
  ],
113
145
  };
114
146
  } catch (error) {
115
- return createErrorResponse(error, "Read error");
147
+ return classifyError(error, "Read error");
116
148
  }
117
149
  },
118
150
  );
@@ -139,7 +171,27 @@ export function registerSandboxTools(server: McpServer): void {
139
171
 
140
172
  return createSuccessResponse(`Successfully created directory: ${path}`);
141
173
  } catch (error) {
142
- return createErrorResponse(error, "Mkdir error");
174
+ return classifyError(error, "Mkdir error");
175
+ }
176
+ },
177
+ );
178
+
179
+ // ========================================================================
180
+ // bash_sandbox_stop - Stop and clean up sandbox
181
+ // ========================================================================
182
+ server.registerTool(
183
+ "bash_sandbox_stop",
184
+ {
185
+ description:
186
+ "Stop and clean up the sandbox environment, releasing all resources. Use bash_sandbox_reset to just clear state.",
187
+ inputSchema: {},
188
+ },
189
+ async () => {
190
+ try {
191
+ await resetPersistentSandbox();
192
+ return createSuccessResponse("Sandbox environment has been stopped and cleaned up.");
193
+ } catch (error) {
194
+ return classifyError(error, "Stop error");
143
195
  }
144
196
  },
145
197
  );
@@ -154,8 +206,12 @@ export function registerSandboxTools(server: McpServer): void {
154
206
  inputSchema: {},
155
207
  },
156
208
  async () => {
157
- resetPersistentSandbox();
158
- return createSuccessResponse("Sandbox environment has been reset.");
209
+ try {
210
+ await resetPersistentSandbox();
211
+ return createSuccessResponse("Sandbox environment has been reset.");
212
+ } catch (error) {
213
+ return classifyError(error, "Reset error");
214
+ }
159
215
  },
160
216
  );
161
217
  }
package/src/types.ts ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Exact passthrough of the upstream just-bash public API.
3
+ *
4
+ * Keeping this as a wildcard export guarantees this wrapper stays 1:1 with
5
+ * whatever the installed just-bash version exposes.
6
+ */
7
+ export * from "just-bash";