mcp-server-bash-sdk
MCP server SDK/example implemented entirely in bash — build a Model Context Protocol server with no runtime dependency beyond a POSIX shell.
Install / Use
claude mcp add muthuishere -- npx -y github:muthuishere/mcp-server-bash-sdkIf the server publishes to npm under a different name, use that package instead — check the repo README.
MCP Server
Model Context Protocol server
Quality Score
Category
Development & EngineeringSupported Platforms
Tags
Skill content
View source on GitHub🐚 MCP Server in Bash
A lightweight, zero-overhead implementation of the Model Context Protocol (MCP) server in pure Bash — targeting the current 2026-07-28 revision.
📖 Documentation site · 🧩 Examples · 🧠 Architecture decisions · 🔬 Spikes
Why? Most MCP servers are just API wrappers with schema conversion. This implementation provides a zero-overhead alternative to Node.js, Python, or other heavy runtimes.
Why now? The 2026-07-28 revision made MCP stateless: no initialize handshake, no sessions, no server-initiated requests. One line in, one line out — which is exactly the shape of a shell read loop. Bash went from an awkward fit to a natural one.
📋 Features
- ✅ MCP 2026-07-28 — stateless, per-request version negotiation
- ✅ Both standard transports: stdio and Streamable HTTP, from the same server file
- ✅
server/discover,tools/list,tools/call - ✅ Dynamic tool discovery via function naming convention
- ✅ External configuration via JSON files
- ✅ Output validated against the official published JSON Schema
🔧 Requirements
- Bash 3.2 or newer (3.2 is what macOS ships as
/bin/bash) jqfor JSON processing (brew install jq/apt install jq/apk add jq)- (HTTP transport only)
socatpreferred, ornetcat— see the note under Transports - (optional) Python with
jsonschema, only to run the schema conformance test
Supported platforms
Verified by running the real test suites on each, not by inspection —
./scripts/test-linux.sh all reproduces the Linux rows on any machine with Docker.
| Platform | Shell | Unit | HTTP |
| --- | --- | --- | --- |
| macOS | bash 3.2 (/bin/bash) and 5.x | 31/31 | 19/19 |
| Debian / Ubuntu | bash 5.2 | 31/31 | 19/19 |
| Alpine (musl + busybox) | bash 5.3 | 31/31 | 19/19 |
Docker is needed only to verify other platforms — never to run a server.
🚀 Quick Start
- Clone the repo
git clone https://github.com/muthuishere/mcp-server-bash-sdk
cd mcp-server-bash-sdk
- Make scripts executable
chmod +x mcpserver_core.sh moviemcpserver.sh
- Try it out — every request carries its protocol version in
params._meta:
echo '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}' | ./moviemcpserver.sh
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_movies","arguments":{},"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}' | ./moviemcpserver.sh
- Or run it as a local HTTP endpoint
./moviemcpserver.sh --http # http://127.0.0.1:3000/mcp
- Run the tests
./test_mcpserver_core.sh # 31 unit tests
./test_mcpserver_core.sh test_discover_shape # a single test
./test_conformance.sh # validate against the official schema
./test_http_transport.sh # 19 HTTP transport tests
./scripts/test-linux.sh all # run everything on Debian, Alpine and Ubuntu
🏗️ Architecture
┌─────────────┐ stdio ┌──────────────────────────────────────┐
│ MCP Host │◄─────────►│ Your server (moviemcpserver.sh) │
│ (AI System) │ │ │
│ │ HTTP │ ┌────────────────────────────────┐ │
│ │◄─────────►│ │ Transport │ │
└─────────────┘ │ │ run_mcp_server (stdio) │ │
│ │ mcpserver_http.sh (--http) │ │
│ └───────────────┬────────────────┘ │
│ ▼ │
│ ┌────────────────────────────────┐ │
│ │ Protocol mcpserver_core.sh │ │
│ │ process_request() │ │
│ └───────────────┬────────────────┘ │
│ ▼ │
│ ┌────────────────────────────────┐ │
│ │ Business logic tool_* funcs │ │
│ └───────────────┬────────────────┘ │
└──────────────────┼──────────────────-┘
▼
┌──────────────────────────────┐
│ Config JSON · external APIs │
└──────────────────────────────┘
Both transports call the same process_request, so they cannot drift in protocol behaviour.
- mcpserver_core.sh: JSON-RPC framing, MCP dispatch, version negotiation, result envelopes
- mcpserver_http.sh: the Streamable HTTP binding — headers, status codes,
Origin, listener - moviemcpserver.sh / examples/gitserver.sh: business logic — your
tool_*functions - assets/: discovery document and tool list
- spec/: the vendored official schema the conformance test validates against
- examples/: four runnable servers and a build-your-own walkthrough
- docs/adr/: why the SDK is shaped this way · docs/spikes/: the experiments behind those decisions
🔌 Creating Your Own MCP Server
Tool Function Guidelines
- Naming Convention: prefix every tool function with
tool_, matching the name in your tools JSON - Parameters: each function takes a single parameter
$1containing the arguments as JSON - Success: echo the result,
return 0 - Failure: echo an explanatory message,
return 1— the caller receives a successful response withisError: true, so the model can read the reason and retry. Tool failures are not transport errors. - Automatic Discovery: tools are dispatched by function name; the tools JSON controls what clients are told exists
Implementation Steps
- Create your business logic file (e.g.,
weatherserver.sh)
#!/bin/bash
# Weather API implementation
# Override configuration paths BEFORE sourcing the core
MCP_CONFIG_FILE="$(dirname "${BASH_SOURCE[0]}")/assets/weatherserver_config.json"
MCP_TOOLS_LIST_FILE="$(dirname "${BASH_SOURCE[0]}")/assets/weatherserver_tools.json"
MCP_LOG_FILE="$(dirname "${BASH_SOURCE[0]}")/logs/weatherserver.log"
source "$(dirname "${BASH_SOURCE[0]}")/mcpserver_core.sh"
API_KEY="${MCP_API_KEY:-default_key}"
# Tool: Get current weather for a location
tool_get_weather() {
local args="$1"
local location=$(echo "$args" | jq -r '.location')
if [[ -z "$location" || "$location" == "null" ]]; then
echo "Missing required parameter: location" # reaches the model as isError
return 1
fi
curl -s "https://api.example.com/weather?location=$location&apikey=$API_KEY"
return 0
}
# stdio by default; --http serves the same tools over Streamable HTTP.
case "${1:-}" in
--http) shift; run_mcp_http_server "$@" ;;
handle-connection) run_mcp_http_server handle-connection ;;
*) run_mcp_server "$@" ;;
esac
For the --http mode also source "$(dirname "${BASH_SOURCE[0]}")/mcpserver_http.sh" next to the core.
Four runnable examples are in examples/, each covering a different problem:
| Example | What it shows |
| --- | --- |
| gitserver.sh | Shelling out safely — argument validation, structured output |
| weatherserver.sh | Wrapping a third-party API — secrets in env, network failures, trimming the response |
| fileserver.sh | Saying no — read-only filesystem access with a real path-traversal boundary |
| moviemcpserver.sh | The minimum, over canned data |
- Create
assets/weatherserver_tools.json
{
"tools": [
{
"name": "get_weather",
"description": "Get current weather for a location",
"inputSchema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name or coordinates"
}
},
"required": ["location"]
}
}
]
}
Keep the array order stable — the spec asks servers to return tools deterministically so clients (and LLM prompt caches) can cache them.
- Create
assets/weatherserver_config.json— this is now theserver/discoverbody, not aninitializeresult:
{
"supportedVersions": ["2026-07-28"],
"serverInfo": {
"name": "WeatherServer",
"version": "1.0.0"
},
"capabilities": {
"tools": {
"listChanged": false
}
},
"ttlMs": 3600000,
"cacheScope": "public",
"instructions": "This server provides weather information."
}
- Make your file executable
chmod +x weatherserver.sh
🔀 Transports
Both are the spec's standard bindings, and both run from the same server file.
stdio
What an editor or agent launches as a subprocess. This is the default and the one to use unless you specifically need HTTP.
./moviemcpserver.sh
Streamable HTTP
2026-07-28 removed sessions, the GET stream and SSE resumability, so this binding is now
just POST a message, get JSON back — which is why it fits in a shell script at all.
./moviemcpserver.sh --http # http://127.0.0.1:3000/mcp
MCP_HTTP_PORT=8080 ./moviemcpserver.sh --http
| Variable | Default | Purpose |
| --- | --- | --- |
| MCP_HTTP_PORT | 3000 | listening port |
| MCP_HTTP_BIND | 127.0.0.1 | interface — leave it on loopback |
| MCP_HTTP_PATH | /mcp | endpoint path |
| MCP_ALLOWED_ORIGINS | http://localhost,http://127.0.0.1 | Origin allowlist; anything else gets 403 |
Every POST must carry MCP-Protocol-Version and Mcp-Method, plus Mcp-Name for
tools/call / resources/read / prompts/get, and each must match the request body —
a mismatch is 400 with -32020. That is a security control, not ceremony: an
intermediary may route on the header while the server executes the body.
⚠️ This is a local endpoint, not a web server. It binds loopback, has no TLS and no auth. Install
socat(brew install socat) and it forks per connection; without it thenetcatfallback serves one connection at a time, with a brief window between connections where the port is refused. To expose it beyond localhost, put a real reverse proxy in front. See ADR-0006 and spike 02.
🖥️ Using with an MCP client
"mcp": {
"servers": {
"my-weather-server": {
"type": "stdio",
"command": "/path/to/your/weatherserver.sh",
"args": [],
"env": {
"MCP_API_KEY": "your-api-key"
}
}
}
}
⚠️ The client must speak MCP 2026-07-28. This SDK implements that revision only and rejects older ones with -32022 ([ADR-0002](docs/
Truncated for display — read the full file on GitHub.
Related Skills
Agent-Reach
84.4kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
headroom
73.4kCompress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.
ruflo
73.0k🌊 The original agent harness. Deploy intelligent multi-player swarms, coordinate autonomous workflows, and build conversational AI systems. Features adaptive memory, self-learning intelligence, federation, vector RAG integration, and native Claude Code / Codex / Hermes and many more Integrated
career-ops
72.4kOpen-source AI job search: scan job portals, evaluate listings into a structured A-H report with a global 1-5 score, tailor your CV, track applications — runs locally in your AI coding CLI (Claude Code, Codex, OpenCode, Antigravity…)
