Skip to main content

py2mcp

Quick MCP (Model Context Protocol) server creation from Python functions.

Installation

pip install py2mcp

Quick Start

from py2mcp import mk_mcp_server


def add(a: int, b: int) -> int:
    """Add two numbers"""
    return a + b


def greet(name: str = "world") -> str:
    """Greet someone"""
    return f"Hello, {name}!"


# Create and run MCP server
mcp = mk_mcp_server([add, greet])

if __name__ == "__main__":
    mcp.run()

That's it! Your functions are now available as MCP tools.

Features

  • Simple: Just pass functions to mk_mcp_server()
  • Flexible: Supports input/output transformations
  • Pythonic: Clean, decorator-free function definitions
  • Powerful: Built on FastMCP for production-ready servers

Input Transformations

Transform inputs before they reach your functions:

from py2mcp import mk_mcp_server, mk_input_trans
import numpy as np


def add_arrays(a, b):
    """Add two numpy arrays"""
    return (a + b).tolist()


# Convert list inputs to numpy arrays
input_trans = mk_input_trans({"a": np.array, "b": np.array})
mcp = mk_mcp_server([add_arrays], input_trans=input_trans)

From Stores (MutableMapping)

Automatically expose CRUD operations from any mapping:

from py2mcp import mk_mcp_from_store

projects = {"proj1": {"name": "Project 1"}, "proj2": {"name": "Project 2"}}
mcp = mk_mcp_from_store(projects, name="project")

# Automatically creates: list_projects, get_project, set_project, delete_project

Serving: local (stdio) and remote (HTTP + OAuth)

mk_mcp_* build a server object; py2mcp also gives you two ways to run one.

Local (stdio) — for a one-click bundle (e.g. a Claude Desktop .mcpb):

from py2mcp import serve_stdio

serve_stdio(["mypkg.tools:summarize", "mypkg.tools:translate"], name="My Tools")
# or:  python -m py2mcp --config py2mcp_config.json

Remote (Streamable HTTP + OAuth 2.1) — for a hosted MCP server reached from a vendor's cloud (e.g. a claude.ai custom connector). The server is an OAuth 2.1 resource server: it validates a managed IdP's JWTs (audience-bound per RFC 8707) and never issues tokens itself.

from py2mcp.http import mk_http_app

AUTH = {
    "type": "jwt",  # resource-server: validate the IdP's JWTs
    "jwks_uri": "https://idp.example.com/.well-known/jwks.json",
    "issuer": "https://idp.example.com",
    "audience": "https://my-connector.example.com/mcp",  # THIS server (RFC 8707)
    "authorization_servers": ["https://idp.example.com"],
    "base_url": "https://my-connector.example.com",
    "required_scopes": ["mcp:read"],
}

# An ASGI app you run under any ASGI server (uvicorn, gunicorn, serverless):
app = mk_http_app(["mypkg.tools:summarize"], name="My Connector", auth=AUTH)
#   uvicorn server.app:app --host 0.0.0.0 --port 8000   (behind TLS)

serve_http(...) builds and runs it in-process (FastMCP/uvicorn). Both wrap FastMCP's native transports/OAuth — py2mcp does not reinvent them.

Middleware (metering, logging, rate-limiting)

Every builder accepts middleware= — a single FastMCP middleware or an iterable of them — attached at construction, exactly as auth= is. It's the one clean seam for cross-cutting concerns that must wrap every tool call (usage metering, cost logging, audit trails, rate limiting), so you don't decorate each function individually — and can't forget one (a missed decorator on a paid tool means untracked cost):

from fastmcp.server.middleware import Middleware


class UsageMeter(Middleware):
    async def on_call_tool(self, context, call_next):
        result = await call_next(context)  # the tool runs here
        record(context.message.name)  # ... then meter it
        return result


mcp = mk_mcp_server([render, estimate], middleware=[UsageMeter()])
# same on mk_mcp_from_refs(...), mk_mcp_from_store(...), mk_http_app(...),
#         serve_http(...), serve_stdio(...)

On the remote path auth= (transport-level) runs first, so a middleware can read the authenticated caller via fastmcp.server.dependencies.get_access_token(). Middleware is a programmatic hook — it takes Python objects, so it isn't wired through the python -m py2mcp CLI / JSON-config path (unlike refs/name/auth).

License

MIT

Download files

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

Source Distribution

py2mcp-0.1.8.tar.gz (34.2 kB view details)

Uploaded Source

Built Distribution

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

py2mcp-0.1.8-py3-none-any.whl (19.2 kB view details)

Uploaded Python 3

File details

Details for the file py2mcp-0.1.8.tar.gz.

File metadata

  • Download URL: py2mcp-0.1.8.tar.gz
  • Upload date:
  • Size: 34.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for py2mcp-0.1.8.tar.gz
Algorithm Hash digest
SHA256 c7ad6311b963bab5f60d5110c950adeb78a1a38522866be00d3e46ddac6271bd
MD5 dc1194e017338e3c5296d445e1c36888
BLAKE2b-256 df0feba96a639f9cc3f761148fd315ffa470a789ad4fecda7595859cf3909005

See more details on using hashes here.

File details

Details for the file py2mcp-0.1.8-py3-none-any.whl.

File metadata

  • Download URL: py2mcp-0.1.8-py3-none-any.whl
  • Upload date:
  • Size: 19.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for py2mcp-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 9835d52aaa73bdbb658cae5809fbd207d555ec2046fa75c507451d7e44435585
MD5 9d5e06de26ba01151afd03cac40baef4
BLAKE2b-256 9d949c601fcfc1661c96bdcc1b64e8b1e66dcf23e43500259c4d63a28599ae61

See more details on using hashes here.

Release history Release notifications | RSS feed

0.1.9

2 files

This release

0.1.8 This release

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

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