mcpbash
Health Gecti
- License — License: Apache-2.0
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Community trust — 34 GitHub stars
Code Basarisiz
- eval() — Dynamic code execution via eval() in docs/src/components/FAQSection.jsx
Permissions Gecti
- Permissions — No dangerous permissions requested
This package provides a lightweight, shell-first sandbox environment for developers. It allows you to map MCP tools, TypeScript functions, and existing binaries into controlled commands, running them through a customized runtime instead of provisioning heavy virtual machines or containers.
Security Assessment
Because the tool is fundamentally designed to wrap and execute commands and binaries, it inherently performs local shell execution. It also facilitates making outbound network requests to interact with external MCP tool endpoints. The package does not request explicitly dangerous system permissions, and no hardcoded secrets were found, as it prefers resolving them dynamically at runtime.
However, the automated scan flagged a `FAIL` for dynamic code execution via `eval()` located in the documentation component (`docs/src/components/FAQSection.jsx`). While this is likely isolated to the website's frontend code rather than the core sandboxing engine, it remains a potential security risk. Overall risk is rated as Medium.
Quality Assessment
The project demonstrates strong maintenance and community confidence, with an active codebase showing pushes as recent as today. It is legally clear for adoption, using the standard Apache-2.0 license, and has garnered 34 GitHub stars, indicating a fair baseline of community trust and early adoption.
Verdict
Use with caution: while the core utility is well-maintained and licensed, users should be aware of the inherent shell execution risks, network capabilities, and the `eval()` vulnerability found in the documentation source code.
Embedded sandbox with MCP proxy
mcpbash
Build tiny sandboxes from MCP tools, TypeScript functions, CLIs, and just-bash.
mcpbash gives you a small shell-first sandbox object. Instead of provisioning a VM or container, you map capabilities into commands and run them through a controlled runtime.
Install
pnpm add mcpbash
# or
npm install mcpbash
Why use it?
- Map MCP tools to shell commands
- Map TypeScript functions to shell commands
- Wrap existing binaries
- Choose an in-memory, read-only, or read-write filesystem
- Opt into
gitsupport when you need it - Control outbound MCP access with a simple network policy
- Resolve secrets at runtime instead of hardcoding them
Quick start
This example creates one command, slugify, and runs it inside the sandbox.
import { createSandbox, fn } from "mcpbash";
const sandbox = await createSandbox({
filesystem: { mode: "memory" },
commands: {
slugify: fn({
input: { text: "$1" },
handler: ({ text }: { text: string }) =>
text.toLowerCase().replace(/\s+/g, "-"),
}),
},
});
const result = await sandbox.run('slugify "Hello World"');
console.log(result.stdout); // hello-world
console.log(result.exitCode); // 0
await sandbox.dispose();
Common patterns
Run a real MCP-backed command
This example is fully runnable. It starts a tiny local HTTP server that behaves like an MCP tool endpoint, maps that tool into the sandbox as repos.search, and calls it like a shell command.
import http from "node:http";
import { createSandbox, mcp } from "mcpbash";
async function startToolServer(): Promise<{ url: string; close(): Promise<void> }> {
return new Promise((resolve) => {
const server = http.createServer((req, res) => {
if (req.method !== "POST" || req.url !== "/tools/search_repositories") {
res.statusCode = 404;
res.end("not found");
return;
}
let body = "";
req.on("data", (chunk) => {
body += chunk.toString("utf8");
});
req.on("end", () => {
const { input } = JSON.parse(body) as { input?: { q?: string } };
const query = input?.q ?? "";
res.setHeader("content-type", "application/json");
res.end(
JSON.stringify({
result: {
query,
repos: [
`${query}-api`,
`${query}-web`,
`${query}-worker`,
],
},
})
);
});
});
server.listen(0, "127.0.0.1", () => {
const address = server.address();
if (!address || typeof address === "string") {
throw new Error("failed to start tool server");
}
resolve({
url: `http://127.0.0.1:${address.port}`,
close: () =>
new Promise<void>((done, reject) => {
server.close((error) => {
if (error) reject(error);
else done();
});
}),
});
});
});
}
const toolServer = await startToolServer();
try {
const sandbox = await createSandbox({
filesystem: { mode: "memory" },
network: {
allow: ["127.0.0.1"],
},
commands: {
"repos.search": mcp({
server: toolServer.url,
tool: "search_repositories",
input: { q: "$1" },
}),
},
});
const result = await sandbox.run('repos.search "billing"');
console.log(result.stdout);
// {
// "query": "billing",
// "repos": ["billing-api", "billing-web", "billing-worker"]
// }
await sandbox.dispose();
} finally {
await toolServer.close();
}
See also: packages/mcpbash/examples/mcp-demo.ts
Wrap a local CLI
import { cli, createSandbox } from "mcpbash";
const sandbox = await createSandbox({
commands: {
upper: cli({
command: process.execPath,
args: [
"-e",
"process.stdout.write((process.argv[1] ?? '').toUpperCase())",
"$1",
],
}),
},
});
console.log((await sandbox.run('upper "hello"')).stdout); // HELLO
Work in a writable directory with git
import { createSandbox } from "mcpbash";
const sandbox = await createSandbox({
filesystem: {
mode: "readwrite",
root: "./workspace",
},
integrations: {
git: true,
},
});
await sandbox.run("git init -q");
await sandbox.fs.write("README.md", "# demo\n");
const status = await sandbox.git?.status(["--short"]);
console.log(status?.stdout);
API at a glance
import { createSandbox, mcp, fn, cli, provider, secret } from "mcpbash";
createSandbox(...)creates a sandboxmcp(...)maps an MCP tool to a commandfn(...)maps a TypeScript handler to a commandcli(...)wraps a local binaryprovider(...)is an alias forcli(...)for external runtimessecret.env("NAME")resolves a secret at runtimesandbox.run(...)executes a commandsandbox.fsexposesread,write,list, andexistssandbox.gitexposesstatus,diff, andlogwhen git is enabled
Filesystem modes
memory: isolated in-memory sandboxreadonly: overlay an existing directory without writesreadwrite: back the sandbox with a real directory
Examples in this repo
packages/mcpbash/examples/mixed-demo.tspackages/mcpbash/examples/mcp-demo.tspackages/mcpbash/examples/git-demo.tspackages/mcpbash/examples/benchmark.ts
Run them:
pnpm install
pnpm --filter mcpbash demo:mixed
pnpm --filter mcpbash demo:mcp
pnpm --filter mcpbash demo:git
pnpm --filter mcpbash bench
Advanced usage
See ADVANCED.md for:
- filesystem allow/deny rules
- secrets
- network policy
- MCP auth and OAuth patterns
- provider examples with Daytona, Upstash Box, and Docker
- current limitations
Performance
Local benchmark on April 9, 2026, on an Apple M4 Pro with Bun 1.3.5:
| Operation | Mean | P50 | P95 |
|---|---|---|---|
createSandbox() |
0.067 ms | 0.040 ms | 0.090 ms |
built-in shell command (echo) |
0.460 ms | 0.406 ms | 0.687 ms |
mapped fn(...) command |
0.350 ms | 0.319 ms | 0.550 ms |
mapped provider(...) command (true) |
2.143 ms | 2.038 ms | 3.298 ms |
mapped cli(...) command (node -e) |
4.160 ms | 3.878 ms | 7.314 ms |
The important takeaway is category-level: mcpbash stays on the local fast path. It does not provision a VM, container, or remote session just to dispatch a mapped command, so startup and dispatch stay in the low-millisecond range.
Run the benchmark locally:
pnpm --filter mcpbash bench
Development
pnpm install
pnpm --filter mcpbash typecheck
pnpm --filter mcpbash build
pnpm --filter mcpbash test
Yorumlar (0)
Yorum birakmak icin giris yap.
Yorum birakSonuc bulunamadi