# mcp-bash [![mcp-bash framework banner](assets/mcp-bash-framework.png)](assets/mcp-bash-framework.png) [![CI](https://img.shields.io/github/actions/workflow/status/yaniv-golan/mcp-bash-framework/ci.yml?branch=main&label=CI)](https://github.com/yaniv-golan/mcp-bash-framework/actions) [![License](https://img.shields.io/github/license/yaniv-golan/mcp-bash-framework)](LICENSE) [![Bash](https://img.shields.io/badge/bash-%3E%3D3.2-green.svg)](https://www.gnu.org/software/bash/) [![MCP Protocol](https://img.shields.io/badge/MCP-2025--11--25-blue)](https://modelcontextprotocol.io/specification) [![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-lightgrey)](#runtime-requirements) > **Repository:** [`mcp-bash-framework`](https://github.com/yaniv-golan/mcp-bash-framework)  •  **CLI/Binary:** `mcp-bash` > **For AI agents:** Read [`llms.txt`](llms.txt) (compact) or [`llms-full.txt`](llms-full.txt) (detailed) for agent-optimized context — includes a step-by-step Quick Start, critical allowlist requirement, and CLI wrapper patterns. > > **Claude Code skill:** `/plugin install mcp-bash --marketplace yaniv-golan/mcp-bash-framework` installs a skill with the workflow and common traps for building mcp-bash servers. On older Claude Code: `/plugin marketplace add yaniv-golan/mcp-bash-framework`, then `/plugin install mcp-bash@mcp-bash`. ## Contents - [TL;DR](#tldr) - [What you’ll build](#what-youll-build) - [Design Principles](#design-principles) - [MCP Spec Coverage](#mcp-spec-coverage) - [Why Bash?](#why-bash) - [Quick Start](#quick-start) - [Security Model (60 seconds)](#security-model-60-seconds) - [Configure Your MCP Client](#configure-your-mcp-client) - [MCPB Bundles](#mcpb-bundles) - [Vendoring the Runtime](#vendoring-the-runtime) - [Project Structure](#project-structure) - [Direct Tool Execution (run-tool)](#direct-tool-execution-run-tool) - [Embedded resources in tool output](#embedded-resources-in-tool-output) - [Diagnostics & Validation](#diagnostics--validation) - [SDK Discovery](#sdk-discovery) - [Roots (scoping filesystem access)](#roots-scoping-filesystem-access) - [Completions](#completions) - [Tool Policy Hook (optional)](#tool-policy-hook-optional) - [Learn by Example](#learn-by-example) - [Features at a Glance](#features-at-a-glance) - [Configuration](#configuration) - [Troubleshooting (quick hits)](#troubleshooting-quick-hits) - [Requirements](#requirements) - [Uninstall](#uninstall) - [Documentation](#documentation) - [Built with mcp-bash](#built-with-mcp-bash) - [FAQ](#faq) > **The most complete MCP implementation in pure Bash.** Tools, resources, prompts, elicitation, roots, progress, cancellation—the full spec, no runtimes beyond your shell. > > - Runs on Bash 3.2+ (macOS/Linux stock). No Node, no Python, no containers. > - Handles concurrency, timeouts, and cancellation the way production systems need. > - You write the tools. The framework stays out of your way. ## TL;DR **Turn any Bash script into an MCP tool in minutes.** No Node, no Python, no containers. ```bash mcp-bash new my-server && cd my-server mcp-bash scaffold tool my-tool # edit tools/my-tool/tool.sh mcp-bash config --client cursor # paste into your MCP client mcp-bash bundle # create distributable package ``` ![mcp-bash demo: create a project, scaffold a tool, run it, and print a Claude Desktop config](assets/mcp-bash-demo.gif) ## What you’ll build ```mermaid flowchart TD Client["MCP client
(Claude Desktop / Cursor / Windsurf)"] Transport["stdio (JSON-RPC)"] Framework["mcp-bash framework
(registry + runtime + policy)"] Project["Your project
tools/ resources/ prompts/ server.d/"] Client --> Transport --> Framework --> Project ``` ## Design Principles - Tools shouldn’t need another runtime to talk to AI. - Everything must be inspectable. No magic. - If it’s not needed in production, it isn’t in the framework. - Your project stays yours. The framework upgrades cleanly. ## MCP Spec Coverage mcp-bash targets the **2025-11-25** MCP specification with negotiated downgrades to older versions. | Category | Coverage | Notes | |----------|----------|-------| | Core Protocol | ✅ Full | Lifecycle, ping, capabilities, downgrades | | Tools | ✅ Full | list, call, icons, errors, listChanged, annotations | | Resources | ✅ Full | list, read, subscriptions, templates, binary, annotations | | Prompts | ✅ Full | list, get, arguments, icons | | Utilities | ✅ Full | Progress, cancellation, logging, completion | | Elicitation | ✅ Full | Form, URL, enum, multi-choice modes | | Roots | ✅ Full | Server→client request, listChanged | | MCP Apps (UI) | ⚠️ Partial | `ui://` resources, templates; UI-initiated tool calls depend on host support | **Not yet implemented:** Audio content, sampling. Tasks (async job/poll) and server-identity discovery are HTTP-oriented and not applicable to stdio. Transport is stdio-only by design. See [Remote Connectivity](docs/REMOTE.md) for HTTP/SSE proxy options, including the shared-secret guard (`MCPBASH_REMOTE_TOKEN`) and readiness probe (`mcp-bash --health`). → [Full compliance matrix](SPEC-COMPLIANCE.md) For a complete feature-by-feature breakdown across all MCP versions, see the [Feature Support Matrix](SPEC-COMPLIANCE.md#feature-support-matrix) in `SPEC-COMPLIANCE.md`. ## Why Bash? | | mcp-bash | TypeScript SDK | Python SDK | |---|----------|----------------|------------| | Runtime | Bash 3.2+ (pre-installed) | Node.js 18+ | Python 3.10+ | | Install | `brew install` or `curl \| bash` | `npm install` | `pip install` | | Startup | No VM warmup | Node.js startup | Python startup | | Dependencies | jq or gojq | npm packages | pip packages | | Best for | Shell automation, existing scripts, air-gapped/minimal environments | Node.js applications | Python applications | If your tools are already shell scripts, wrapping them in Node or Python adds complexity for no benefit. mcp-bash lets you expose them directly. ## Quick Start When you run `mcp-bash` from inside a project (a directory containing `server.d/server.meta.json`), it auto-detects the project root. Running `mcp-bash` outside any project starts a temporary getting-started helper tool. For MCP clients, set `MCPBASH_PROJECT_ROOT` so the server can find your project regardless of working directory. ### 1. Check Requirements (10 seconds) ```bash # Preflight: Bash 3.2+ and jq (or gojq) for full functionality (tools/resources/prompts). bash --version | head -1 command -v jq >/dev/null 2>&1 || command -v gojq >/dev/null 2>&1 || printf '%s\n' '⚠ jq/gojq missing: minimal mode only (tools/resources/prompts disabled)' ``` ### 2. Install the Framework **Homebrew** (macOS and Linux — recommended): ```bash brew install yaniv-golan/mcp-bash/mcp-bash ``` Update later with `brew upgrade mcp-bash`. **curl installer** (CI, Docker, Windows/Git Bash, or environments without Homebrew): ```bash curl -fsSL "https://raw.githubusercontent.com/yaniv-golan/mcp-bash-framework/v1.9.6/install.sh" | bash -s -- --yes --version "v1.9.6" ``` Verified install (recommended for production / security-sensitive environments): ```bash version="v1.9.6" file="mcp-bash-${version}.tar.gz" curl -fsSLO "https://github.com/yaniv-golan/mcp-bash-framework/releases/download/${version}/${file}" curl -fsSLO "https://github.com/yaniv-golan/mcp-bash-framework/releases/download/${version}/SHA256SUMS" # Verify (macOS): grep -E "([[:space:]]|\\*)${file}$" SHA256SUMS | shasum -a 256 -c - # Verify (Linux): grep -E "([[:space:]]|\\*)${file}$" SHA256SUMS | sha256sum -c - curl -fsSLO "https://raw.githubusercontent.com/yaniv-golan/mcp-bash-framework/${version}/install.sh" bash install.sh --archive "${file}" --version "${version}" ``` Why this is “verified”: you download the release tarball + `SHA256SUMS`, verify the checksum locally, then run the installer against the verified archive. For tagged releases (`vX.Y.Z`), the installer also attempts to verify the archive against `SHA256SUMS` automatically when it’s available. Manual/offline install (for policy-driven or air-gapped environments): ```bash git clone https://github.com/yaniv-golan/mcp-bash-framework.git ~/.local/share/mcp-bash mkdir -p ~/.local/bin && ln -sf ~/.local/share/mcp-bash/bin/mcp-bash ~/.local/bin/mcp-bash echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc # or ~/.zshrc (if not already in PATH) ``` Pin a release with the installer (auto-prefixes `v` for bare versions): ```bash bash install.sh --verify --version 1.9.6 ``` ### 3. Verify It Works (30 seconds) Security defaults: the `server.d/register.sh` hook is disabled unless `MCPBASH_ALLOW_PROJECT_HOOKS=true` (`server.d/policy.sh` and `register.json` are always loaded), and tools require an explicit allowlist (`MCPBASH_TOOL_ALLOWLIST=*` to allow all in trusted projects). ```bash mcp-bash doctor # Expected output includes lines like: # ✓ Bash version: 5.x.x (>= 3.2 required) # ✓ jq installed: /usr/bin/jq # All checks passed! Ready to build MCP servers. # Preview and apply managed-install repairs (shim + optional upgrade when --min-version is set): mcp-bash doctor --dry-run mcp-bash doctor --fix # Quick end-to-end test (optional): mcp-bash new demo-server cd demo-server mcp-bash run-tool hello --allow-self --args '{"name":"World"}' # Expected output includes: "Hello, World!" # Cleanup (the demo-server directory persists until you remove it): cd .. && rm -rf demo-server ```
Something not working? (click to expand) | Symptom | Likely cause | Fix | |---------|--------------|-----| | `mcp-bash: command not found` | PATH not configured | `export PATH="$HOME/.local/bin:$PATH"` then open a new shell | | “Operating in minimal mode…” / tools missing | `jq`/`gojq` missing | `brew install jq` or `apt install jq` | | “blocked by policy” | Default-deny tool policy | CLI: re-run with `--allow-self`. MCP clients: set `MCPBASH_TOOL_ALLOWLIST` in client config | | Claude Desktop starts but shows no tools | GUI non-login env / PATH | Use `mcp-bash config --wrapper-env` and point the client at the wrapper | | macOS `Operation not permitted` / quarantine | Gatekeeper quarantine | `scripts/macos-dequarantine.sh ~/.local/share/mcp-bash` (trusted paths only), restart client | | Windows Git Bash path weirdness | MSYS path conversion | `MSYS2_ARG_CONV_EXCL="*"` (and prefer `jq` on Windows CI) | Still stuck? Run `mcp-bash doctor` (or `mcp-bash doctor --json`) and include output when opening an issue.
### 4. Create Your Project Your server code lives in a separate project directory: ```bash mcp-bash new my-mcp-server cd my-mcp-server ``` Already in a directory you created yourself? Run `mcp-bash init --name my-mcp-server [--no-hello]` instead. ### 5. Scaffold Your First Tool ```bash mcp-bash scaffold tool check-disk ``` This scaffolds `tools/check-disk/tool.sh` and `tools/check-disk/tool.meta.json` in your project. You write the logic. Each scaffolded tool also includes `tools/check-disk/smoke.sh`—run it after edits to ensure your tool’s stdout is valid JSON (update the sample args in the script if you change `tool.meta.json`). ### 6. (Optional) Add a Test Harness Create a lightweight runner for tool smoke tests: ```bash mcp-bash scaffold test ./test/run.sh --verbose # add run_test calls inside test/run.sh ``` The harness wraps `mcp-bash run-tool`, validates your project before running, and refuses to overwrite existing `test/run.sh` or `test/README.md`. ## Security Model (60 seconds) mcp-bash is **secure by default**. Here's what that means: | Layer | Default | What it does | |-------|---------|--------------| | **Tool allowlist** | Deny all | Tools won't run unless explicitly listed in `MCPBASH_TOOL_ALLOWLIST` | | **Hooks** | Disabled | `server.d/register.sh` is not executed unless `MCPBASH_ALLOW_PROJECT_HOOKS=true`. `server.d/policy.sh` is always sourced, so treat it as trusted code; `server.d/health-checks.sh` runs only under `mcp-bash --health`; `server.d/env.sh` is only sourced by `run-tool --with-server-env` | | **Tool environment** | Minimal | Tools inherit only essential vars (`PATH`, `HOME`, `TMPDIR`, `LANG`, Windows system vars, and `MCP_*`/`MCPBASH_*`). Use `MCPBASH_TOOL_ENV_MODE` to change, or declare it in `server.meta.json` `"env"` | ### Quick reference ```bash # Allow a single tool (CLI) mcp-bash run-tool my-tool --allow-self --args '{}' # Allow specific tools (MCP client config) MCPBASH_TOOL_ALLOWLIST="tool1,tool2" # Allow all tools (trusted projects only) MCPBASH_TOOL_ALLOWLIST="*" # Enable the registration hook (server.d/register.sh) MCPBASH_ALLOW_PROJECT_HOOKS=true # Tool environment modes MCPBASH_TOOL_ENV_MODE=minimal # default: PATH, HOME, TMPDIR, LANG (+ Windows system vars) MCPBASH_TOOL_ENV_MODE=inherit # pass through parent environment (needs MCPBASH_TOOL_ENV_INHERIT_ALLOW=true) MCPBASH_TOOL_ENV_MODE=allowlist # minimal + MCPBASH_TOOL_ENV_ALLOWLIST vars ``` To pass a host-injected secret (such as an MCPB `user_config` API key) to tools and providers, declare the policy in `server.d/server.meta.json` instead of a launcher script; the launch environment still wins: ```json "env": {"MCPBASH_TOOL_ENV_MODE": "allowlist", "MCPBASH_TOOL_ENV_ALLOWLIST": "MY_API_KEY"} ``` **Demo tip:** For local testing, use `--allow-self` (CLI) or `MCPBASH_TOOL_ALLOWLIST=*` (MCP clients). For production, allowlist only the tools you need. → [Full security guide](docs/SECURITY.md) · [Environment reference](docs/ENV_REFERENCE.md) ## Configure Your MCP Client Every client works the same way: point it at the framework and tell it where your project lives: 1. Set `MCPBASH_PROJECT_ROOT=/path/to/your/project`. 2. Point it at the `mcp-bash` binary (installed to `~/.local/bin/mcp-bash` by the installer). - If you generated a wrapper via `mcp-bash config --wrapper` or `--wrapper-env`, you can point clients at `/.sh`; the wrapper already wires `MCPBASH_PROJECT_ROOT` for you. ### Generate Config (CLI) ```bash mcp-bash config --show mcp-bash config --json # machine-readable descriptor (name/command/env) mcp-bash config --client cursor # client-specific snippet mcp-bash config --inspector # ready-to-run Inspector command (stdio) mcp-bash config --wrapper-env # wrapper that sources your shell profile first (macOS-safe) mcp-bash config --wrapper # TTY: creates ./.sh; piped/redirected: stdout ``` `config --show` prints one labeled snippet per supported client (headings like `# Claude Desktop`, `# Cursor`, etc.) so you can see which block to copy; use `--client ` or `--json` when you only want a single paste-ready block. Copy the snippet for your client (Claude Desktop/CLI/Code, Cursor, Windsurf, LibreChat, etc.) and paste it into the appropriate config file. This sets `MCPBASH_PROJECT_ROOT` and the `mcp-bash` command path for you. When run in a terminal (stdout is a TTY), `config --wrapper` writes `/.sh`, marks it executable, and prints the path to stderr; piping or redirecting prints the wrapper script to stdout. Picking a wrapper: - Use `--wrapper` when your PATH/env is already correct in non-login shells (e.g., Linux, or macOS with absolute paths). - Use `--wrapper-env` when you need your login shell to set PATH/version managers/vars before starting the server (common on macOS Claude Desktop). - Distributing a server? Ship the env wrapper by default for GUI launches (macOS/Windows clients), and include a non-login wrapper or absolute runtime path for CI/WSL/Linux users who want fast, side-effect-free startups. ### Per-Client Snippets - **Claude Desktop**: Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows) and add: ```jsonc "mcpServers": { "mcp-bash": { "command": "/Users/you/.local/bin/mcp-bash", "env": { "MCPBASH_PROJECT_ROOT": "/Users/you/my-mcp-server", "MCPBASH_TOOL_ALLOWLIST": "*" } } } ``` - macOS runtime note: Claude Desktop launches servers from a minimal, non-login shell, so your PATH, version managers (nvm/pyenv/uv/rbenv), and env vars from `.zshrc`/`.bash_profile` are skipped. Use absolute paths to runtimes (e.g., `/opt/homebrew/bin/node`) and set missing vars in the `env` block, or generate a login-aware wrapper: ```bash mcp-bash config --project-root /Users/you/my-mcp-server --wrapper-env > /Users/you/my-mcp-server/mcp-bash.sh chmod +x /Users/you/my-mcp-server/mcp-bash.sh ``` Then point Claude Desktop at `/Users/you/my-mcp-server/mcp-bash.sh` as the `command`. - macOS quarantine: Gatekeeper can block quarantined downloads (typically from browsers/DMGs/AirDrop) even when paths are correct. CLI downloads (curl/wget/git) often skip quarantine. If you see `ENOENT`, `transport closed unexpectedly`, or `Operation not permitted` despite correct paths, clear quarantine and restart Claude Desktop: ```bash xattr -r -d com.apple.quarantine ~/.local/share/mcp-bash xattr -r -d com.apple.quarantine /Users/you/my-mcp-server ``` Helper: `scripts/macos-dequarantine.sh [path]` will clear quarantine for the repo (or a specific path). `xattr -cr` clears all extended attributes; only use it on trusted paths. - macOS folder permissions: Desktop/Documents/Downloads are TCC-protected and Downloads is often quarantined. Move servers to a neutral folder (e.g., `~/mcp-servers`) or grant Claude “Full Disk Access” and “Files and Folders” in System Settings. - **Claude CLI/Claude Code**: Run once: ```bash claude mcp add --transport stdio mcp-bash \ --env MCPBASH_PROJECT_ROOT="$HOME/my-mcp-server" \ --env MCPBASH_TOOL_ALLOWLIST="*" \ -- "$HOME/.local/bin/mcp-bash" ``` - **Cursor**: Create `~/.cursor/mcp.json` (or `.cursor/mcp.json` in a project) with the same `mcpServers` JSON as above. - **Windsurf (Cascade)**: Edit `~/.codeium/windsurf/mcp_config.json` via Settings → Advanced → Cascade, and add the same `mcpServers` entry. - **LibreChat**: In `librechat.yaml` add: ```yaml mcpServers: mcp-bash: type: stdio command: /Users/you/.local/bin/mcp-bash env: MCPBASH_PROJECT_ROOT: /Users/you/my-mcp-server MCPBASH_TOOL_ALLOWLIST: "*" ``` - **OpenAI Agents SDK (Python)**: Use `MCPServerStdio(params=...)`; the constructor does not take a `name` kwarg. ```python import os from agents.mcp import MCPServerStdio os.environ["MCPBASH_PROJECT_ROOT"] = "/Users/you/my-mcp-server" os.environ["MCPBASH_TOOL_ALLOWLIST"] = "*" async with MCPServerStdio( params={ "command": "/Users/you/.local/bin/mcp-bash", # optionally add args/env/cwd if your server needs them } ) as server: ... ``` - **OpenClaw** (formerly Clawdbot): register the server with [OpenClaw](https://github.com/openclaw/openclaw)'s MCP registry, which saves it under `mcp.servers` in OpenClaw's config: ```bash openclaw mcp add mcp-bash \ --command /Users/you/.local/bin/mcp-bash \ --env MCPBASH_PROJECT_ROOT=/Users/you/my-mcp-server \ --env 'MCPBASH_TOOL_ALLOWLIST=*' ``` Check it with `openclaw mcp show mcp-bash`. Servers in mcporter's own config are a separate registry that `openclaw mcp` does not read. - **NanoClaw** ([nanoclaw.dev](https://nanoclaw.dev); checked against v2026.10.0): MCP servers run inside each agent group's container, so mount the framework and your project into it, add `jq` (not in the default image), and register the server. Point the registry cache at a writable path, because the mounts are read-only: ```bash ncl groups config add-mount --id --host ~/.local/share/mcp-bash --container /opt/mcp-bash --ro ncl groups config add-mount --id --host /Users/you/my-mcp-server --container /opt/my-mcp-server --ro ncl groups config add-package --id --apt jq ncl groups config add-mcp-server --id --name mcp-bash \ --command /opt/mcp-bash/bin/mcp-bash \ --env '{"MCPBASH_PROJECT_ROOT":"/opt/my-mcp-server","MCPBASH_TOOL_ALLOWLIST":"*","MCPBASH_REGISTRY_DIR":"/tmp/mcp-bash-registry"}' ncl groups restart --id --rebuild ``` `--host` is the framework install directory (`~/.local/share/mcp-bash` for the curl installer). Mounting host paths needs operator approval. - **Highlight AI**: Go to Highlight → Settings → Connections → Add → Custom command: - **Connection name**: `mcp-bash` - **Command**: `/Users/you/.local/bin/mcp-bash` - **Environment variables**: `MCPBASH_PROJECT_ROOT=/Users/you/my-mcp-server MCPBASH_TOOL_ALLOWLIST=*` - **Windows note**: Git Bash (CI-tested) or WSL both work. Git Bash ships with Git for Windows; WSL behaves like Linux. See [Windows Support](docs/WINDOWS.md) for details. > **Security note:** `MCPBASH_TOOL_ALLOWLIST="*"` allows all registered tools. For production deployments, replace `*` with a comma-separated list of specific tool names (e.g., `"search-notes,get-note"`). ### Compatibility Notes | Client | Status | Known issues / notes | |--------|--------|----------------------| | Claude Desktop | Tested (macOS, Windows) | macOS: non-login shell PATH/env (use `config --wrapper-env`); macOS quarantine/TCC can block execution; restart required after config changes | | Claude CLI / Claude Code | Tested | Generally straightforward; ensure `MCPBASH_PROJECT_ROOT` points at your project | | OpenClaw (formerly Clawdbot) | Config documented | `openclaw mcp add`; saved under `mcp.servers` in OpenClaw's config | | NanoClaw | Config documented | Runs in the agent's container: mount the framework and project, add `jq`, set `MCPBASH_REGISTRY_DIR` to a writable path | | Highlight AI | Tested | UI-based config: Settings → Connections → Add → Custom command | | Cursor | Config documented | Config file location differs by install; use `mcp-bash config --client cursor` | | Windsurf (Cascade) | Config documented | Use the app's MCP config UI/file; see snippet in README | | LibreChat | Config documented | YAML config format; see snippet in README | | OpenAI Agents SDK | Example provided | Python example only; verify SDK version and stdio wiring | **Tested** = maintainers have manually verified end-to-end. **Config documented** = configuration instructions provided but not regularly tested. **CI-tested platforms:** Ubuntu, macOS, Windows (Git Bash). CI validates the MCP protocol layer via integration tests, not specific client applications. Using a different client? Any MCP-compliant stdio client should work. [Open an issue](https://github.com/yaniv-golan/mcp-bash-framework/issues) if you hit compatibility problems. ## MCPB Bundles Package your server for one-click installation in Claude Desktop: ```bash mcp-bash bundle # Creates: my-server-1.0.0.mcpb ``` Double-click the `.mcpb` file to install, or drag it to Claude Desktop. ### Quick Configuration Create `mcpb.conf` in your project root to customize the bundle: ```bash MCPB_NAME="my-server" MCPB_AUTHOR_NAME="Your Name" MCPB_AUTHOR_EMAIL="you@example.com" MCPB_REPOSITORY="https://github.com/you/my-server" ``` Without a config file, metadata is auto-resolved from `server.meta.json`, `VERSION`, and git config. ### Bundle Options ```bash mcp-bash bundle --validate # Check without creating mcp-bash bundle --output ./dist # Output to specific directory mcp-bash bundle --verbose # Show detailed progress ``` The bundle includes your tools, resources, prompts, and an embedded copy of the mcp-bash framework—fully self-contained for distribution. To list the bundle in the MCP Registry, run `mcp-bash publish my-server-1.0.0.mcpb` (`--dry-run` validates without submitting; needs a registry token, see `mcp-bash publish --help`). > [Full bundling guide](docs/MCPB.md) ## Vendoring the Runtime Embed the mcp-bash runtime directly into your project repository so that no system install is needed at runtime. Useful for dev teams, CI, and MCP clients that don't support MCPB bundles: ```bash mcp-bash vendor # embed .mcp-bash/ into project root git add .mcp-bash/ # commit it mcp-bash vendor --verify # verify integrity at any time ``` > [Full vendoring guide](docs/VENDORING.md) ## Project Structure ``` Framework (Install Once) Your Project (Version Control This) ~/.local/share/mcp-bash/ ~/my-mcp-server/ ├── bin/mcp-bash ├── tools/ ├── lib/ │ └── check-disk/ ├── handlers/ │ ├── tool.sh └── ... │ └── tool.meta.json ├── prompts/ ~/.local/bin/ ├── resources/ └── mcp-bash → ../share/mcp-bash/... ├── server.d/ │ └── server.meta.json (optional) └── .registry/ (auto-generated) ``` ## Direct Tool Execution (run-tool) Use `run-tool` to invoke a single tool without starting the full MCP server. This wires the same environment as the server (SDK path, args, metadata, roots). Tool names must match `^[a-zA-Z0-9_-]{1,64}$`; some clients, including Claude Desktop, enforce this and reject dotted names, so prefer hyphens/underscores for namespaces. ```bash # Basic invocation (project inferred from CWD or MCPBASH_PROJECT_ROOT) mcp-bash run-tool my-tool --allow-self --args '{"value":"hello"}' # Simulate roots (comma-separated), stream stderr, override timeout, or print env mcp-bash run-tool my-tool --allow-self --args '{"value":"hi"}' --roots /tmp/project,/data/shared --verbose --timeout 15 # Inspect wiring without executing mcp-bash run-tool my-tool --print-env --dry-run # Dry-run validates metadata/args without executing the tool mcp-bash run-tool my-tool --dry-run ``` Flags: `--args` (JSON object), `--roots` (comma-separated paths), `--dry-run`, `--timeout `, `--verbose` (stream tool stderr), `--no-refresh` (reuse the cached registry; by default, run-tool rescans when anything under `tools/` is newer than the cache), `--minimal` (force degraded mode), `--project-root `, `--print-env` (dump wiring without executing), `--allow-self` / `--allow TOOL` / `--allow-all` (allowlist this run; tools are denied by default), `--with-server-env` (source `server.d/env.sh` first), `--source FILE` (source a file first; repeatable). Elicitation is not supported in CLI mode. The scaffolder and examples use per-tool directories (e.g., `tools/check-disk/tool.sh`); automatic discovery requires tools to live under subdirectories of `tools/` (root-level scripts are not discovered). See [**Project Structure Guide**](docs/PROJECT-STRUCTURE.md) for detailed layouts, Docker deployment, and multi-environment setups. ## Embedded resources in tool output Tools can attach files directly to the MCP response as `type:"resource"` content parts; binary files are auto-base64-encoded into the `blob` field, text stays in `text`. Use `mcp_result_text_with_resource` to embed files with your tool result: ```bash payload_path="${MCPBASH_PROJECT_ROOT}/resources/report.txt" printf 'Report content' >"${payload_path}" mcp_result_text_with_resource \ "$(mcp_json_obj message "See embedded report")" \ --path "${payload_path}" --mime text/plain ``` This helper stays as is in 1.x and changes in 2.0; `mcp-bash migrate` will show the replacement once it is final. See the dedicated example at `examples/06-embedded-resources/` and [BEST-PRACTICES.md](docs/BEST-PRACTICES.md#embedding-resources-in-tool-responses) for full documentation. ## Diagnostics & Validation - Readiness/health probe: `mcp-bash --health [--project-root DIR] [--timeout SECS]` (`0`=ready, `1`=unhealthy, `2`=misconfigured) - Project checks: `mcp-bash validate [--project-root DIR] [--fix] [--json] [--explain-defaults] [--strict] [--inspector] [--deprecations-as-errors]`. Uses of APIs that change in 2.0 are reported as deprecations, counted separately from errors and warnings. - Migrating to the 2.0 helpers: `mcp-bash migrate [--project-root DIR]` lists the uses of APIs removed in 2.0 (check only by default); `--write` applies the safe rewrites (to `mcp_result_json` and `mcp_fail --type`), `--list` / `--explain ID` describe the rules, `--json` gives a machine-readable report. If the project vendors the runtime, run `mcp-bash vendor --upgrade` first. See [BEST-PRACTICES.md](docs/BEST-PRACTICES.md#migrating-to-the-20-helpers). - Environment check: `mcp-bash doctor [--json] [--dry-run|--fix]` - Registry cache introspection: `mcp-bash registry status [--project-root DIR]` - Client config: `mcp-bash config --json` (machine-readable), `--client ` (pasteable JSON), `--wrapper` (generate auto-install wrapper) ## SDK Discovery Every tool sources shared helpers from `sdk/tool-sdk.sh`. When `mcp-bash` launches a tool it exports `MCP_SDK=/path/to/framework/sdk`, so tool scripts can run: ```bash source "${MCP_SDK}/tool-sdk.sh" ``` If you copy a tool out of this repository (or build your own project layout) and run it directly, set `MCP_SDK` before executing the script: ```bash export MCP_SDK=~/.local/share/mcp-bash/sdk ./tools/check-disk/tool.sh ``` If the SDK can’t be resolved, the script exits with a clear error. ## Roots (scoping filesystem access) - If the client supports MCP Roots, mcp-bash requests them after `initialized` and exposes them to tools via env (`MCP_ROOTS_JSON`, `MCP_ROOTS_PATHS`, `MCP_ROOTS_COUNT`) and SDK helpers (`mcp_roots_list`, `mcp_roots_count`, `mcp_roots_contains`). - If the client does not provide roots or times out, you can supply them via `MCPBASH_ROOTS="/path/one:/path/two"` or an optional `config/roots.json` in your project. Paths are normalized and enforced consistently. ## Completions Register completions declaratively in `server.d/register.json`, or place a script next to a prompt or resource (for example `prompts//.completion.sh`; see [docs/COMPLETION.md](docs/COMPLETION.md)): ```json { "version": 1, "completions": [ {"name":"example.completion","path":"completions/example.sh","timeoutSecs":5} ] } ``` Paths are resolved relative to `MCPBASH_PROJECT_ROOT`, and registry refreshes pick them up automatically. ## Tool Policy Hook (optional) Projects can restrict tool execution centrally in `server.d/policy.sh`. The built-in policy runs first and denies every tool unless it is allowlisted (`MCPBASH_TOOL_ALLOWLIST`, or `--allow-self` for `run-tool`); a project rule can only narrow that further. The preferred form is the restrict-only hook `mcp_tools_policy_project`. The framework calls it after the built-in checks pass, with the tool name and the call's arguments (JSON): ```bash # server.d/policy.sh mcp_tools_policy_project() { local tool_name="$1" args_json="$2" if [ "${MYPROJECT_READ_ONLY:-0}" = "1" ] && [[ "${tool_name}" != myproj-get* ]]; then printf 'Read-only mode: %s is disabled. Ask the user to turn off read-only mode.\n' "${tool_name}" return 1 fi return 0 } ``` - Exit 0 allows the call. Any other status denies it, and what the hook printed becomes the message the model and the user see (trailing newlines dropped, capped at 1 KiB; with no output it is `Tool '' denied by server policy`). Output on allow is discarded. - The hook runs in a subshell (`$(...)`), because the server's stdout is the protocol channel. Variables it sets don't survive between calls: keep any state in files. A denial, whether from the allowlist or a project rule, comes back as a tool result with `isError: true` and `_meta["mcpbash/error"].type` = `policy_denied`, not as a JSON-RPC error, so clients such as Claude Desktop show the reason. A rejected tool path (a broken install) is still a JSON-RPC error. Older projects define `mcp_tools_policy_check` instead. That still works, but it **replaces** the built-in policy, including the deny-by-default allowlist (`MCPBASH_TOOL_ALLOWLIST`) and tool path checks, so it must start with `mcp_tools_policy_check_default "$@" || return 1`; `mcp-bash validate` and `doctor` warn when it doesn't. Its denial message (`mcp_tools_error CODE "message" [data]`) and data are kept; the code is not, since a tool result has none. If `policy.sh` defines both, `mcp_tools_policy_check` wins and the project hook is not called. Either way this guards against accidentally widening access, not against a hostile project: `policy.sh` is trusted shell code sourced at startup and can redefine anything. Keep the logic lightweight; the hook runs on every invocation. ## Learn by Example The [`examples/`](examples/) directory shows common patterns end-to-end: | Example | Concepts Covered | |---------|------------------| | [**00-hello-tool**](examples/00-hello-tool/) | Basic "Hello World" tool structure and metadata. | | [**01-args-and-validation**](examples/01-args-and-validation/) | Handling JSON arguments and input validation. | | [**02-logging-and-levels**](examples/02-logging-and-levels/) | Sending logs to the client and managing verbosity. | | [**03-progress-and-cancellation**](examples/03-progress-and-cancellation/) | Long-running tasks, reporting progress, and handling user cancellation. | | [**04-roots-basics**](examples/04-roots-basics/) | MCP roots scoping for tools; allows/denies file reads based on configured roots. | | [**05-resources-basics**](examples/05-resources-basics/) | Listing and reading resources via the built-in file provider. | | [**06-embedded-resources**](examples/06-embedded-resources/) | Embedding file content directly in tool responses. | | [**07-prompts-basics**](examples/07-prompts-basics/) | Discovering and rendering prompt templates. | | [**08-elicitation**](examples/08-elicitation/) | Client-driven elicitation prompts that gate tool execution. | | [**09-registry-overrides**](examples/09-registry-overrides/) | Declarative registry overrides, live progress streaming, and a custom resource provider. | | [**10-completions**](examples/10-completions/) | Completion registration, query filtering, and pagination/hasMore. | | [**11-resource-templates**](examples/11-resource-templates/) | Resource template discovery, manual overrides, client-side expansion, and template-aware reads. | | [**12-config-and-downloads**](examples/12-config-and-downloads/) | Layered configuration (`mcp_config_load`) and SSRF-safe downloads (`mcp_download_safe`). | | [**13-ui-basics**](examples/13-ui-basics/) | MCP Apps: `ui://` resources, tool-associated and standalone UIs, `ui.meta.json`. | | [**14-ui-templates**](examples/14-ui-templates/) | A result-driven dashboard built with the bundled UI templates. | | [**15-cli-wrapper**](examples/15-cli-wrapper/) | Wrapping an external CLI: PATH resolution (`mcp_detect_cli`), error handling, result formatting. Start here if your server calls a CLI. | | [**Advanced: register-sh-hooks**](examples/advanced/register-sh-hooks/) | Dynamic registration from `server.d/register.sh` (opt-in project hook). | | [**Advanced: ffmpeg-studio**](examples/advanced/ffmpeg-studio/) | Real-world application: video processing pipeline with media inspection (optional, heavy deps). | ## Features at a Glance - **Auto-Discovery**: Place scripts in your project's `tools/`, `resources/`, or `prompts/` directories—the framework finds them automatically. - **Scaffolding**: Generate compliant tool, resource, prompt templates, and a test harness (`mcp-bash scaffold `, `mcp-bash scaffold test`). - **Stdio Transport**: Standard input/output. No custom daemons or sidecars. - **Framework/Project Separation**: Install the framework once, create unlimited projects. - **Graceful Degradation**: Automatically detects an available JSON tool (`jq`, then `gojq`) or falls back to minimal mode if none are present. - **Progress Streaming**: Emits progress and log notifications; set `MCPBASH_ENABLE_LIVE_PROGRESS=true` to stream them during execution (uses a lightweight background flusher). - **Debug Mode**: Run `mcp-bash debug` to capture all JSON-RPC messages for analysis. See [docs/DEBUGGING.md](docs/DEBUGGING.md). - **Elicitation**: Ask the user for input mid-tool (form and URL modes) when the client supports it. See [docs/ELICITATION.md](docs/ELICITATION.md). - **Completions**: Argument completions for prompts and resource templates. See [docs/COMPLETION.md](docs/COMPLETION.md). - **MCP Apps (UI resources)**: Serve `ui://` HTML views alongside tools, from files or built-in templates. See [docs/guides/ui-resources.md](docs/guides/ui-resources.md). - **Distribution**: `mcp-bash bundle` builds an MCPB package, `mcp-bash publish` submits it to the MCP Registry, and `mcp-bash vendor` embeds the runtime in your repo. See [MCPB Bundles](#mcpb-bundles) and [Vendoring the Runtime](#vendoring-the-runtime). ## Configuration ### Required Configuration | Variable | Description | |----------|-------------| | `MCPBASH_PROJECT_ROOT` | **Required for MCP clients.** Path to your project directory containing `tools/`, `prompts/`, `resources/`. CLI commands (`mcp-bash scaffold`, `mcp-bash validate`, etc.) auto-detect the project root from the current directory. | ### Optional Configuration | Variable | Default | Description | |----------|---------|-------------| | `MCPBASH_TOOLS_DIR` / `MCPBASH_RESOURCES_DIR` / `MCPBASH_PROMPTS_DIR` / `MCPBASH_SERVER_DIR` | Derived from `MCPBASH_PROJECT_ROOT` | Override content and server hook locations. | | `MCPBASH_REGISTRY_DIR` | `$MCPBASH_PROJECT_ROOT/.registry` | Registry cache location. | | `MCPBASH_MAX_CONCURRENT_REQUESTS` | `16` | Cap concurrent worker slots. | | `MCPBASH_MAX_TOOL_OUTPUT_SIZE` | `10485760` | Tool stdout limit; stderr/resources inherit when unset. | | `MCPBASH_LOG_LEVEL` | `info` | Log level; use `debug` for discovery traces. | | `MCPBASH_DEBUG_ERRORS` | `false` | Include tool diagnostics in outputSchema validation errors (exit code, stderr tail, trace line). | | `MCPBASH_DEBUG_LOG` | (unset) | Override per-tool debug log path; SDK `mcp_debug` appends to it. | | `MCPBASH_ENABLE_LIVE_PROGRESS` | `false` | Stream progress/log notifications during execution (starts a background flusher). | | `MCPBASH_ENV_PAYLOAD_THRESHOLD` | `65536` | Spill args/metadata to temp files above this size. | | `MCPBASH_TOOL_ENV_MODE` | `minimal` | Tool env isolation: `minimal`, `inherit`, or `allowlist`. | | `MCPBASH_TOOL_ENV_INHERIT_ALLOW` | `false` | Must be `true` to allow `MCPBASH_TOOL_ENV_MODE=inherit`. | | `MCPBASH_DEFAULT_TOOL_TIMEOUT` | `30` | Default tool timeout (seconds). | | `MCPBASH_REMOTE_TOKEN` | (unset) | Shared secret for proxied deployments (minimum 32 chars; failures throttled). | | `MCPBASH_HTTPS_ALLOW_HOSTS` / `MCPBASH_HTTPS_DENY_HOSTS` | (unset) | HTTPS provider host allow/deny lists; private/loopback always blocked. Allow list is required unless `MCPBASH_HTTPS_ALLOW_ALL=true`. | | `MCPBASH_HTTPS_ALLOW_ALL` | `false` | Explicitly allow all public HTTPS hosts (unsafe; prefer `MCPBASH_HTTPS_ALLOW_HOSTS`). | | `MCPBASH_CI_MODE` | (unset) | CI defaults: safe tmp/log dirs, keep-logs, timestamped logs, failure summary (`failure-summary.jsonl`), env snapshot (`env-snapshot.json`); `MCPBASH_CI_VERBOSE=true` starts at debug; GH annotations when tracing provides file/line. | Full list and defaults: see [docs/ENV_REFERENCE.md](docs/ENV_REFERENCE.md). ### Server Metadata Server identity is configured via `server.d/server.meta.json`. All fields are optional—smart defaults are applied when omitted: | Field | Default | Description | |-------|---------|-------------| | `name` | Project directory name | Server identifier (e.g., `my-server`) | | `title` | Titlecase of name | Human-readable display name (e.g., `My Server`) | | `version` | `VERSION` file, `package.json`, or `0.0.0` | Server version | | `description` | (omitted) | Brief description of the server | | `websiteUrl` | (omitted) | URL to server homepage or documentation | | `icons` | (omitted) | Array of icon objects for visual identification | | `env` | (omitted) | Tool/provider env policy: only `MCPBASH_TOOL_ENV_MODE`, `MCPBASH_TOOL_ENV_ALLOWLIST`, `MCPBASH_PROVIDER_ENV_MODE`, `MCPBASH_PROVIDER_ENV_ALLOWLIST`. Never put secret values here. See [docs/MCPB.md](docs/MCPB.md#passing-secrets-to-tools) | Example `server.d/server.meta.json`: ```json { "name": "weather-api", "title": "Weather API Server", "version": "1.0.0", "description": "Provides weather data for any location", "websiteUrl": "https://example.com/weather-api", "icons": [ {"src": "https://example.com/icon.svg", "sizes": ["any"], "mimeType": "image/svg+xml"} ] } ``` If no `server.meta.json` exists, the server uses smart defaults based on your project directory name. Optional initialize guidance can be defined in `server.d/server.instructions.md`. When present and non-empty, its contents are returned in `initialize.result.instructions` for protocol versions `2025-03-26` and newer (omitted for `2024-11-05`). ### Resource Annotations Resources can carry optional `annotations` metadata (audience hints, priority, etc.) that clients may use for filtering or display. Annotations are supported through all discovery paths: - **`*.meta.json`**: include an `annotations` object in the metadata file. - **Inline `# mcp:` header**: add `annotations` to the JSON payload. - **Manual registration** (`register.sh`): pass `annotations` in the `mcp_register_resource` JSON. Example `resources/report.meta.json` with annotations: ```json { "name": "monthly-report", "description": "Monthly analytics report", "uri": "file:///data/report.csv", "mimeType": "text/csv", "annotations": { "audience": ["user"], "priority": 0.8 } } ``` When annotations are absent, the field is omitted from `resources/list` responses (not emitted as `null`). ### Tool SDK environment - `MCPBASH_JSON_TOOL` and `MCPBASH_JSON_TOOL_BIN` point to the detected JSON processor (`gojq`/`jq`) and are injected into tool processes when available. - `MCPBASH_MODE` is `full` when JSON tooling is present and `minimal` otherwise; SDK helpers warn and downgrade behaviour when running in minimal mode. - `MCPBASH_DEBUG_LOG` points to a per-invocation debug log file (when available); use `mcp_debug` inside tools to append file-based checkpoints. - `MCPBASH_TOOL_ENV_MODE` controls isolation for tool processes (`minimal`, `inherit`, or `allowlist`), but MCPBASH/MCP-prefixed variables (including JSON tool hints) are always propagated. Example allowlist for minimal exposure: `MCPBASH_TOOL_ENV_MODE=allowlist MCPBASH_TOOL_ENV_ALLOWLIST=HOME,PATH`. ### Capability Modes | Mode | Supported surface | Limitations / when it applies | |------|-------------------|--------------------------------| | Full | Lifecycle, ping, logging/setLevel, tools/resources/prompts (list, call/read/subscribe), completion, pagination, `listChanged` notifications | Requires `jq`/`gojq` available; default mode. | | Minimal | Lifecycle, ping, logging/setLevel | Tools/resources/prompts/completion are disabled and registry notifications are suppressed. Activated when no JSON tool is found or `MCPBASH_FORCE_MINIMAL=true`. | ### Registry Maintenance - Auto-refresh: registries re-scan on TTL expiry (default 5s) and use lightweight file-list hashing to skip rebuilds when nothing changed. - Manual refresh: `bin/mcp-bash registry refresh [--project-root DIR] [--no-notify] [--quiet] [--filter PATH]` rebuilds `.registry/*.json` and returns a status JSON. In minimal mode the command is skipped gracefully. ## Troubleshooting (quick hits) - `PATH` issues (mcp-bash not found): ensure `~/.local/bin` is on PATH; rerun `source ~/.bashrc` or `~/.zshrc`. - Missing JSON tooling (`jq`/`gojq`): install one; otherwise the server enters minimal mode (tools/resources/prompts disabled). - macOS quarantine blocks execution: run `xattr -d com.apple.quarantine ~/.local/share/mcp-bash/bin/mcp-bash` (and your project path if needed). - Git Bash/MSYS exec-limit quirks: set `MCPBASH_JSON_TOOL=jq` and `MSYS2_ARG_CONV_EXCL="*"` before running `mcp-bash`. ## Requirements ### Runtime Requirements * **Bash**: version 3.2 or higher (standard on macOS, Linux, WSL, and Git Bash on Windows). * **JSON Processor**: `jq` (preferred) or `gojq`. * *Note*: If no JSON tool is found, the server runs in "Minimal Mode" (Lifecycle & Ping only). ### Development Requirements If you plan to contribute to the core framework, see [CONTRIBUTING.md](CONTRIBUTING.md) for setup instructions (linting, tests, etc). ### Testing (quick start) From the repo root: ```bash ./test/lint.sh ./test/unit/run.sh ./test/integration/run.sh # Optional: # ./test/compatibility/run.sh # ./test/stress/run.sh ``` ### Windows Notes - Signals from the client may not reliably terminate subprocesses on Git Bash; prefer explicit `shutdown`/`exit` and short tool timeouts. - Paths are normalized to `/c/...` style; avoid mixing Windows- and POSIX-style roots in the same project. - Large payloads can be slower under MSYS; keep registry TTLs reasonable. See [docs/WINDOWS.md](docs/WINDOWS.md) for full guidance and workarounds. ## Uninstall **Homebrew:** ```bash brew uninstall mcp-bash brew untap yaniv-golan/mcp-bash # optional ``` **curl installer or manual install:** ```bash # 1. Remove the framework installation rm -rf ~/.local/share/mcp-bash # 2. Remove the symlink rm -f ~/.local/bin/mcp-bash # 3. (Optional) Remove the PATH addition from your shell config # Edit ~/.bashrc or ~/.zshrc and remove the line: # export PATH="$HOME/.local/bin:$PATH" ``` **Either way**, remove the wrapper script if you created one with `mcp-bash config --wrapper` or `--wrapper-env` (one file per project, named after the server; your other scripts stay): ```bash rm -f /path/to/your/project/.sh # e.g., my-server.sh ``` Your project directories (`tools/`, `resources/`, `prompts/`) are untouched; they live in your own repos. To reinstall, follow [Install the Framework](#2-install-the-framework). --- ## Documentation The [**documentation index**](docs/README.md) lists every guide. ### Getting Started - [**Project Structure Guide**](docs/PROJECT-STRUCTURE.md) - Layouts, Docker deployment, multi-environment setups. - [**Examples**](examples/) - Learn by example: hello-world, args, logging, progress, real-world video processing. ### Feature Guides - [**MCPB Bundles**](docs/MCPB.md) - One-click distribution via Claude Desktop and MCP Registry. - [**Vendoring**](docs/VENDORING.md) - Embed the runtime in your git repo for CI and non-MCPB clients. - [**Elicitation**](docs/ELICITATION.md) - Form and URL modes, SDK helpers, and client capability checks. - [**Roots**](docs/ROOTS.md) - Roots/list flow, env wiring, validation, and fallbacks. - [**Completions**](docs/COMPLETION.md) - Manual registration, provider types, pagination, and script contracts. - [**Registry**](docs/REGISTRY.md) - Registry envelopes, TTL, manual registration, and cache formats. - [**Resource Templates**](docs/RESOURCE-TEMPLATES.md) - Auto/manual discovery, pagination, collisions, client-side expansion, and template-aware `resources/read`. - [**Limits & Performance**](docs/LIMITS.md) - Concurrency, payload ceilings, throttling. - [**Errors**](docs/ERRORS.md) - Protocol errors vs tool execution errors (SEP-1303). - [**Best Practices**](docs/BEST-PRACTICES.md) - Development, testing, operations guidance. - [**Environment Variables**](docs/ENV_REFERENCE.md) - Every setting the server reads. - [**Debugging**](docs/DEBUGGING.md) / [**Logging**](docs/LOGGING.md) / [**Inspector**](docs/INSPECTOR.md) - Capture traffic, read logs, and test with the MCP Inspector. ### MCP Apps (UI) - [**MCP Apps concepts**](docs/concepts/mcp-apps.md) - How UI resources work and what clients support. - [**UI resources guide**](docs/guides/ui-resources.md) - Adding `ui://` views to a server. - [**UI SDK reference**](docs/reference/ui-sdk.md) and [**UI templates reference**](docs/reference/ui-templates.md). ### Deep Dive - [**Architecture Guide**](docs/ARCHITECTURE.md) - Internal architecture, lifecycle loop, concurrency model. - [**Protocol Compliance**](SPEC-COMPLIANCE.md) - Detailed MCP protocol support breakdown. - [**Performance Guide**](docs/PERFORMANCE.md) - Tuning concurrency, timeouts, and registry scans. - [**Security Policy**](docs/SECURITY.md) - Input validation and execution safety. - [**Minimal Mode**](docs/MINIMAL-MODE.md) - Behavior when jq/gojq is missing or minimal mode is forced. - [**Changelog**](CHANGELOG.md) - Notable changes between releases. - [**Windows Support**](docs/WINDOWS.md) - Running on Git Bash/WSL. - [**Remote Connectivity**](docs/REMOTE.md) - Exposing mcp-bash over HTTP/SSE via external gateways. ### Scope and Goals - Bash-only Model Context Protocol server verified on macOS Bash 3.2, Linux Bash ≥3.2 (including Alpine/BusyBox, CI-tested), and Windows (Git Bash is CI-tested; WSL behaves like Linux). - Targets MCP protocol version `2025-11-25` while supporting negotiated downgrades. - Transport support is limited to stdio; HTTP/SSE/OAuth transports remain out of scope (see [Remote Connectivity](docs/REMOTE.md) for gateway options). ## Built with mcp-bash | Project | Description | |---------|-------------| | [affinity-sdk](https://github.com/yaniv-golan/affinity-sdk) | Affinity CRM SDK with MCP server support | | [claude-familiar](https://github.com/yaniv-golan/claude-familiar) | Companion personality and MCP tools for Claude Code | | [git-hex](https://github.com/yaniv-golan/git-hex) | Safe, powerful git refactoring tools for AI assistants | If you've built an MCP server using this framework, show it off! Add this badge to your project's README: [![MCP Bash Framework](https://img.shields.io/badge/MCP-MCP_Bash_Framework-green?logo=modelcontextprotocol)](https://github.com/yaniv-golan/mcp-bash-framework) ```markdown [![MCP Bash Framework](https://img.shields.io/badge/MCP-MCP_Bash_Framework-green?logo=modelcontextprotocol)](https://github.com/yaniv-golan/mcp-bash-framework) ``` We'd love to see what you build—consider [opening a discussion](https://github.com/yaniv-golan/mcp-bash-framework/discussions) to share your project with the community. ## FAQ ### Why is the repository named `mcp-bash-framework` but the CLI is `mcp-bash`? The repository name `mcp-bash-framework` reflects what this project is: a framework you install once and use to create multiple MCP server projects. The CLI/binary is named `mcp-bash` because that's what you invoke—short and memorable. The name `mcp-bash` was already taken on GitHub, so we chose `mcp-bash-framework` to accurately describe the architecture while avoiding namespace conflicts. ### Do I need jq? What about gojq? You need one of them for tools, resources and prompts; without either, the server runs in [minimal mode](docs/MINIMAL-MODE.md) (lifecycle and ping only). Prefer `jq` (`brew install jq`, `apt install jq`); the server looks for it first. `gojq` works too. To choose explicitly, set `MCPBASH_JSON_TOOL=jq` or `gojq`. --- mcp-bash is intentionally small. It gives you control, clarity, and a predictable surface for AI systems. **Build tools, not infrastructure.**