Skip to main content

Zero-config MCP server. Drop a .py file in a folder, it's a tool.

Project description

ZeroMCP — Python

Drop a .py file in a folder, get a sandboxed MCP server. Stdio out of the box, zero dependencies.

Getting started

# tools/hello.py — this is a complete MCP server
tool = {
    "description": "Say hello to someone",
    "input": {"name": "string"},
}

async def execute(args, ctx):
    return f"Hello, {args['name']}!"
python3 -m zeromcp serve ./tools

That's it. Stdio transport works immediately. Drop another .py file to add another tool. Delete a file to remove one. No server object, no decorators, no main block.

vs. the official SDK

The official Python SDK (FastMCP) requires a server object, decorators, and a __main__ block. Adding a tool means editing server code and restarting. ZeroMCP is file-based — each tool is its own file, discovered automatically.

In benchmarks, ZeroMCP Python handles 12,936 requests/second over stdio versus the official SDK's 1,018 — 12.7x faster with 59% less memory. Over HTTP (Starlette), ZeroMCP serves 2,623 rps at 27 MB versus the official SDK's 635 rps at 80-87 MB. ZeroMCP uses only the standard library. The official SDK pulls in pydantic, httpx, uvicorn, and starlette just for stdio.

Python passes all 10 conformance suites and survives 21/22 chaos monkey attacks.

The official SDK has no sandbox. ZeroMCP enforces per-tool network allowlists, credential isolation, and filesystem controls at runtime.

HTTP / Streamable HTTP

ZeroMCP doesn't own the HTTP layer. You bring your own framework; ZeroMCP gives you an async handler that takes a JSON-RPC dict and returns a response dict (or None for notifications).

from zeromcp import create_handler

handler = await create_handler("./tools")
# handler(request: dict) -> dict | None

Flask

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/mcp", methods=["POST"])
async def mcp():
    response = await handler(request.get_json())
    if response is None:
        return "", 204
    return jsonify(response)

FastAPI

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI()

@app.post("/mcp")
async def mcp(req: Request):
    response = await handler(await req.json())
    if response is None:
        return JSONResponse(status_code=204, content=None)
    return response

Requirements

  • Python 3.10+
  • No external dependencies (stdlib only)

Install

pip install -e .

Defining tools

# tools/add.py
tool = {
    "description": "Add two numbers together",
    "input": {"a": "number", "b": "number"},
}

async def execute(args, ctx):
    return {"sum": args["a"] + args["b"]}

Input types

Shorthand strings: "string", "number", "boolean", "object", "array".

Returning values

Return a string or a dict. ZeroMCP wraps it in the MCP content envelope for you.

Sandbox

The Python implementation has full runtime sandboxing.

Network allowlists

tool = {
    "description": "Fetch from our API",
    "input": {"endpoint": "string"},
    "permissions": {
        "network": ["api.example.com", "*.internal.dev"],
    },
}

async def execute(args, ctx):
    res = await ctx.fetch(f"https://api.example.com/{args['endpoint']}")
    return res["body"]

ctx.fetch validates the hostname against the allowlist. Unlisted domains are blocked and logged.

Credential injection

Tools receive secrets via ctx.credentials, configured per namespace. Tools never call os.environ directly.

Filesystem and exec control

Tools must declare fs: 'read' or fs: 'write' for filesystem access. Static auditing and proxy objects enforce the restrictions.

Directory structure

Tools are discovered recursively. Subdirectory names become namespace prefixes:

tools/
  hello.py          -> tool "hello"
  math/
    add.py          -> tool "math_add"

Configuration

Optional zeromcp.config.json in the working directory. See the root README for the full schema.

Testing

python3 -m pytest

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

antidrift_zeromcp-0.2.2.tar.gz (22.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

antidrift_zeromcp-0.2.2-py3-none-any.whl (16.9 kB view details)

Uploaded Python 3

File details

Details for the file antidrift_zeromcp-0.2.2.tar.gz.

File metadata

  • Download URL: antidrift_zeromcp-0.2.2.tar.gz
  • Upload date:
  • Size: 22.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for antidrift_zeromcp-0.2.2.tar.gz
Algorithm Hash digest
SHA256 3c6c364dec9d5a59568299382cae87f37961b245a186ee87026826d8e67cf2c9
MD5 682af23915ae53ae57f47317bec14f15
BLAKE2b-256 529b72c2682295977bbeb9d108493323b8a757cd9c044362d8b97e9ad8b21398

See more details on using hashes here.

Provenance

The following attestation bundles were made for antidrift_zeromcp-0.2.2.tar.gz:

Publisher: publish-python.yml on antidrift-dev/zeromcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file antidrift_zeromcp-0.2.2-py3-none-any.whl.

File metadata

File hashes

Hashes for antidrift_zeromcp-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 2d79bf71ede8e26a45d78123ea278750f6d897b1904db967572153d26b6b94a8
MD5 ba622aa028dde4bcc7388079c0bfb440
BLAKE2b-256 2cc3efb155c1203c3e43526859aeb751b8f40f0ebca81299b219ecffa03a1a65

See more details on using hashes here.

Provenance

The following attestation bundles were made for antidrift_zeromcp-0.2.2-py3-none-any.whl:

Publisher: publish-python.yml on antidrift-dev/zeromcp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page