1. Conduid
  2. Developer Tools
  3. Bash SDK
MCP server · Developer Tools

Bash SDK

MCP server SDK/example implemented entirely in bash — build a Model Context Protocol server with no runtime dependency beyond a POSIX shell.

Unclaimed MIT devtools
81Excellent

Scored 22 hours ago · breakdown

About Bash SDK

Bash SDK is an MCP server published by muthuishere in the Developer Tools category: mCP server SDK/example implemented entirely in bash — build a Model Context Protocol server with no runtime dependency beyond a POSIX shell.

The repository has 515 stars and 51 forks, with the last commit a month ago. Recent commit activity is the strongest signal that reported issues will get attention.

Install

From source
git clone https://github.com/muthuishere/mcp-server-bash-sdk

This server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.

Ask AI

✦

Ask AI about Bash SDK

Powered by Claude · Grounded in docs

I know everything about Bash SDK. Ask me about installation, configuration, usage, or troubleshooting.

Security checks

Static checks derived from the repository 22 hours ago. Heuristics on the source, not an audit.

  • ✓README presentThe repository ships documentation.
  • ✓License declaredUsable under a stated license.
  • ✓Tests presentA test suite exists in the repository.
  • !Dependencies pinnedNo lockfile. Installs can pull different code over time.
  • !Scoped permissionsDoesn't declare a permission scope. Assume it can do anything its process can.

1 check has not been run yet.

Releases

v0.4.1v0.4.1 — MCP 2026-07-28, both transports, verified on Linux · 4 Aug 2026The 2026-07-28 revision made MCP stateless — no `initialize` handshake, no sessions, no server-initiated requests. That is a far better fit for a shell script than what came before, so this release brings the SDK up to that revision…

README

🐚 MCP Server in Bash

MCP Release Platforms License Discord

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)
  • jq for JSON processing (brew install jq / apt install jq / apk add jq)
  • (HTTP transport only) socat preferred, or netcat — 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

  1. Clone the repo
git clone https://github.com/muthuishere/mcp-server-bash-sdk
cd mcp-server-bash-sdk
  1. Make scripts executable
chmod +x mcpserver_core.sh moviemcpserver.sh
  1. 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
  1. Or run it as a local HTTP endpoint
./moviemcpserver.sh --http        # http://127.0.0.1:3000/mcp
  1. 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

  1. Naming Convention: prefix every tool function with tool_, matching the name in your tools JSON
  2. Parameters: each function takes a single parameter $1 containing the arguments as JSON
  3. Success: echo the result, return 0
  4. Failure: echo an explanatory message, return 1 — the caller receives a successful response with isError: true, so the model can read the reason and retry. Tool failures are not transport errors.
  5. Automatic Discovery: tools are dispatched by function name; the tools JSON controls what clients are told exists

Implementation Steps

  1. 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
  1. 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.

  1. Create assets/weatherserver_config.json — this is now the server/discover body, not an initialize result:
{
  "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."
}
  1. 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 the netcat fallback 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 explains the trade-off). Editors still on 2025-06-18 / 2025-11-25 will need a shim until they upgrade.


🚫 Limitations

  • HTTP is a local endpoint: no TLS, no auth, and without socat it serves one connection at a time
  • No SSE response mode, so no notifications/progress streaming and no subscriptions/listen
  • No concurrency on stdio: one client, one process, ~30 requests/second
  • No resources, prompts, MRTR input requests, or the tasks extension
  • Clients older than 2026-07-28 are rejected

For AI assistants and local tool execution, these aren't blocking issues.


🧭 Design notes

This SDK is documented as much by why as by how:

  • Architecture decisions — seven ADRs, each recording what was rejected and why. ADR-0001 chose stdio-only and is now superseded in scope by ADR-0006; the reversal is recorded rather than edited away, because the protocol changed underneath it.
  • Spikes — runnable experiments behind every number here. They are how the two platform bugs were found: read -N missing from macOS's bash 3.2, and Linux capping a single argv entry at 128 KB where macOS does not.

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

Blog : https://medium.com/@muthuishere/why-i-built-an-mcp-server-sdk-in-shell-yes-bash-6f2192072279

Community

Questions, ideas, or built something with this? Join AgentNexus — a Discord for people building with AI agents and open tools. This project lives in #mcp-bash-sdk.


Built and maintained by deemwar — we build messaging and browser automation, and the plumbing that keeps it delivering when it silently stops.

Need help with this, or something like it in production? io@deemwar.com

README fetched from the repository 22 hours ago. The original is authoritative.

Questions

About Bash SDK

How do I install Bash SDK?

No package has been published for this server, so install it from source: clone the repository and follow its README.

Is Bash SDK safe to use with an AI agent?

Its trust score is 81 out of 100 (excellent). It passes 3 of the 5 static security checks run so far; the failures are listed above. It has no ConduID identity yet, so agent calls to it are not receipted.

Is Bash SDK still maintained?

Yes — the latest release is v0.4.1 (4 Aug 2026), and the last commit was a month ago. The repository has 515 stars and 5 open issues.