py-http-mcp-server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@py-http-mcp-serveradd an MCP endpoint to my FastAPI app that exposes a get_order tool"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
py-http-mcp-server
A zero-dependency Python library for building Model Context Protocol (MCP) servers over the
Streamable HTTP transport. It's a Python port of the .NET McpHttpServer library and uses only the standard library.
Supports MCP revisions
2024-11-05,2025-03-26,2025-06-18,2025-11-25and2026-07-28Requires Python 3.9+, with no runtime dependencies (
dependencies = [])Tested against the official MCP Python SDK client; should work with MCP Inspector, Claude, Cursor and Devin
Features
Area | What you get |
Transport | Streamable HTTP |
Versioning | Negotiation via |
2026-07-28 |
|
Tasks (SEP-2663) | Background tool execution through |
Tools | JSON Schema built from type hints, annotations ( |
Resources | Static resources plus URI templates ( |
Prompts | Typed arguments; return a string, an |
Async | Handlers can be |
Security |
|
Hosting | Built-in threaded |
Framework integration |
|
Server push |
|
Related MCP server: Python MCP Server Kit
Installation
pip install py-http-mcp-server # no dependencies are pulled inMinimal code: three ways to host
1. Add MCP to an existing FastAPI app (one line)
from fastapi import FastAPI
from py_http_mcp_server import McpServer
app = FastAPI()
mcp = McpServer(server_name="orders-mcp")
@mcp.tool()
def get_order(order_id: int) -> dict:
"""Returns an order by id."""
return {"id": order_id, "status": "shipped"}
mcp.mount(app) # ← serves /mcp alongside your API (like app.MapMcpHttpServer() in .NET)Run it with uvicorn main:app; the MCP URL is http://127.0.0.1:8000/mcp.
async deftools run on FastAPI's own event loop, so they can share your async DB pools and HTTP clients.Sync tools run on worker threads.
/mcpis left out of the OpenAPI docs.
2. Add MCP to Flask / Django / any ASGI or WSGI app
mcp.mount(flask_app) # Flask: wraps flask_app.wsgi_app
application = mcp.wsgi_middleware(get_wsgi_application()) # Django (wsgi.py)
application = mcp.asgi_middleware(get_asgi_application()) # Django (asgi.py), Quart, ...
app.add_middleware(McpAsgiMiddleware, server=mcp) # Starlette/FastAPI alternative to mount()The middleware handles only /mcp (plus /.well-known/oauth-protected-resource when OAuth is configured). Every other request goes to your app.
3. Standalone (no framework at all)
mcp.run(port=8000) # stdlib threaded HTTP server
# or: uvicorn main:mcp.asgi_app / waitress-serve main:mcp.wsgi_appOrganizing tools across modules (like .NET assembly scanning)
# myapp/tools/orders.py
from py_http_mcp_server import mcp_tool
@mcp_tool
def cancel_order(order_id: int) -> str:
"""Cancels an order."""
...
# main.py
mcp = McpServer(server_name="orders-mcp").discover("myapp.tools") # scans the package recursively
mcp.mount(app)discover() registers every module-level @mcp_tool, @mcp_resource and @mcp_prompt function and every @mcp_handler class it finds.
Quick start (full feature tour)
from dataclasses import dataclass
from typing import Annotated
from py_http_mcp_server import McpServer, Param, CancellationToken
server = McpServer(
server_name="MyMcpServer",
server_version="1.0.0",
instructions="Use these tools to query system metrics and data.",
use_default_cors=True,
list_cache_ttl_ms=60000,
)
@server.tool("calculate_sum", "Calculates the sum of two numbers.", read_only_hint=True, idempotent_hint=True)
def calculate_sum(a: Annotated[int, Param("First number")], b: Annotated[int, Param("Second number")]) -> int:
return a + b
@dataclass
class WeatherReport:
city: str
temperature_c: float
condition: str
@server.tool("get_weather", "Returns structured weather data.", use_structured_output=True)
async def get_weather(city: Annotated[str, Param("City", enum=["Seattle", "London"])], ct: CancellationToken) -> WeatherReport:
return WeatherReport(city, 24.5, "Sunny")
@server.resource("system://status", "System Status", "Current status", "application/json")
def status() -> dict:
return {"status": "online"}
@server.resource_template("users://{id}/profile", "User Profile", "Profile by ID", "application/json")
def profile(id: int) -> dict:
return {"id": id, "role": "developer"}
@server.prompt("greet_user", "Generates a greeting prompt.")
def greet(username: str) -> str:
return f"Hello {username}, how can I help you today?"
server.run(host="127.0.0.1", port=5000) # → http://127.0.0.1:5000/mcpIf you leave out name or description, the function name and docstring are used.
Handler classes (like [McpHandler] in .NET)
from py_http_mcp_server import mcp_handler, mcp_tool, mcp_icon, mcp_resource, mcp_prompt
@mcp_handler
class MyAiTools:
def __init__(self): # register(MyAiTools) instantiates with no args;
self.db = ... # use register(MyAiTools(db)) to inject dependencies
@mcp_tool("lookup", "Looks up a record.", title="Lookup", read_only_hint=True, icon_src="/icons/lookup.svg")
@mcp_icon("/icons/lookup-dark.png", "image/png", "dark", "48x48")
def lookup(self, key: str) -> dict: ...
server.register(MyAiTools) # also accepts instances, modules, or decorated functions.NET → Python mapping
.NET | Python |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Supported parameter types
str, int, float, bool, Decimal, datetime/date/time, UUID, bytes (base64), Enum (by name, case-insensitive),
Literal[...], Optional[T] / T | None, list[T] / tuple / set, dict[str, T], dataclasses, TypedDict, NamedTuple.
Argument names match case-insensitively, and numeric or boolean strings are converted to the declared type (as in .NET).
Advanced
Rich content
from py_http_mcp_server import McpContent, McpToolResult
@server.tool("generate_chart", "Returns a chart image and resource link.")
def generate_chart(metric: str) -> list:
return [
McpContent.create_text(f"Generated chart for {metric}"),
McpContent.create_image("iVBORw0KGgo...", "image/png"),
McpContent.create_resource_link("system://status", "System Status", "Live status JSON", "application/json"),
]Return values are mapped like this:
McpToolResult→ sent as-isMcpContentor a list of them →contentdict, dataclass, NamedTuple or other objects → JSON text plus
structuredContentanything else → text
Exceptions become isError: true tool results, so the LLM can self-correct.
Authorization & OAuth discovery
from py_http_mcp_server import McpServer, OAuthProtectedResourceOptions, bearer_token_authenticator
server = McpServer(
require_authorization=True,
authenticator=bearer_token_authenticator({"secret-token": {"name": "McpUser"}}), # or any callable(request) -> principal
allowed_origins={"https://app.devin.ai"},
oauth_protected_resource=OAuthProtectedResourceOptions(
authorization_servers=["https://login.microsoftonline.com/<tenant-id>/v2.0"],
scopes_supported=["api://my-mcp-server/mcp.tools"],
),
)An unauthenticated request gets 401 with WWW-Authenticate: Bearer resource_metadata=".../.well-known/oauth-protected-resource", scope="...".
CORS preflight and the well-known endpoint skip authentication.
Async tasks
A client asks for background execution with {"name": "...", "arguments": {...}, "task": {"ttl": 30000}}. The server replies at once
with resultType: "task" and a taskId. The client then polls tasks/get and can stop the work with tasks/cancel, which fires the tool's CancellationToken.
Finished tasks are evicted after their ttl.
Hosting options
server.run(port=5000) # blocking, Ctrl+C to stop
srv = server.start(port=0); print(srv.url); server.stop() # background thread (tests)
server.run(port=443, ssl_context=my_ssl_context) # HTTPS
application = server.wsgi_app # e.g. `waitress-serve module:application`
application = server.asgi_app # e.g. `uvicorn module:application`When the app is mounted under a prefix (ASGI root_path or WSGI SCRIPT_NAME), the prefix is respected for generated URLs.
Under uvicorn, open MCP GET streams are closed when the server shuts down, so Ctrl+C doesn't hang on "Waiting for connections to close".
Server → client notifications
server.notify_tools_list_changed()
server.broadcast("notifications/message", {"level": "info", "data": "hello"})Web app + MCP server on one port
The same server can host a regular web application next to the MCP endpoint, so a browser UI and AI agents can share
state. Routes are matched after /mcp and the OAuth well-known path, and static files are served last.
from py_http_mcp_server import HttpError, HttpRequest, SessionManager, sse_response
ui_events = SessionManager()
@server.route("/api/notes/{id}", methods=["GET", "DELETE"])
def note(request: HttpRequest):
note_id = int(request.path_params["id"]) # also: request.query, request.json(), request.headers
if note_id not in notes:
raise HttpError(404, "Note not found") # → 404 {"error": "Note not found"}
return notes[note_id] # dict/list → JSON, str → HTML, None → 204, (body, 201)
@server.route("/api/events") # browser EventSource stream
def events(request: HttpRequest):
return sse_response(ui_events) # push with ui_events.broadcast({...})
server.static("/", "web") # index.html, JS, CSS (path traversal is blocked)Use require_auth=True on a route to apply the server's authenticator. Web routes don't require auth by default, even when the MCP endpoint does.
Demo: examples/sample_server.py serves a single-page UI at http://127.0.0.1:5000/ with three parts:
a notes board, shared between the REST API and the MCP tools
add_note,list_notes,complete_note,update_noteanddelete_notean MCP playground that calls
/mcpfrom the browsera live activity feed
Ask your agent to "add a note to review the release checklist" and the note appears in the browser right away.
Custom JSON-RPC methods
@server.method("completion/complete") # override a built-in or add your own
def complete(params: dict) -> dict:
return {"completion": {"values": ["Seattle"], "total": 1, "hasMore": False}}Options
All the McpServerOptions fields from .NET are present in snake_case (server_name, server_title, website_url, icons,
protocol_version, supported_protocol_versions, instructions, enable_logging, enable_http_logging, endpoint,
require_authorization, use_default_cors, stateless_mode, validate_protocol_version_header, validate_routing_headers,
list_cache_ttl_ms, cache_scope, enable_tasks_extension, allowed_origins, oauth_protected_resource). Python adds:
Option | Default | Purpose |
|
|
|
| 4 MiB | Larger bodies get |
| 15 s | Keep-alive comments on |
|
| Send the legacy |
| 8 | Thread pool size for background tasks |
Intentional differences from the .NET library
outputSchemais only emitted for object-shaped return types, as the MCP spec requires, and its property names matchstructuredContent.Lists of plain values come back as JSON text without
structuredContent, because the spec requiresstructuredContentto be an object.Malformed JSON gets HTTP
400, and an empty batch[]getsInvalid Request.A task cancelled while running stays
cancelled; a late result never overwrites it.prompts/getreturns the prompt's description rather than its name.Resources may return
bytes, which are sent as a base64blob.
Running the tests
python -m unittest discover -s tests -t .Connecting
MCP Inspector: run
npx @modelcontextprotocol/inspector, choose Streamable HTTP, and connect tohttp://localhost:5000/mcpClaude Code:
claude mcp add --transport http my-server http://localhost:5000/mcpDevin / Cursor: set the MCP server URL to
http://<host>:<port>/mcp
This server cannot be deployed
Maintenance
Related MCP Connectors
Model Context Protocol server for the Apideck Unified API. Connect any MCP-compatible agent framework to 100+ accounting systems, HRIS platforms, file storage providers, and more through one integration. More information https://www.apideck.com/mcp-server
Remote streamable-HTTP MCP server running on a single Cloudflare Worker. Your assistant gets live Airbnb, Amazon, Booking.com, Google Flights, Maps and Reddit data, social search on X, Instagram and TikTok, the Meta Ad Library, and image/video generation without any keys. Connect your own accounts to let it send WhatsApp or Telegram messages, work an IMAP inbox, manage Meta Ads campaigns and publish to X and LinkedIn. OAuth 2.1 with PKCE; stored credentials are AES-256-GCM encrypted.
MCP server for secureFlows: token-free URL builders and integration-linting tools for AI agents.
Remote MCP server exposing SMI Aware tools, resources, and skills over Streamable HTTP.
Related MCP Servers
AlicenseNot gradedqualityBmaintenanceEnables serving a remote Model Context Protocol endpoint by exposing tools, prompts, and resources with customizable authorization and hooks, allowing AI models to interact with your application through a standardized JSON-RPC interface.342 npmBusiness Source 1.1- AlicenseNot gradedqualityCmaintenanceEnables developers to build and deploy secure, typed Model Context Protocol servers with tools, resources, prompts, bearer-token authentication, per-tool scopes, rate limiting, and health probes over Streamable HTTP.MIT
- AlicenseNot gradedqualityCmaintenanceEnables creating minimal Model Context Protocol servers with tools, resources, and prompts over stdio or streamable HTTP using only Python's standard library.MIT
- AlicenseNot gradedqualityCmaintenanceEnables running a stateless Model Context Protocol server over Streamable HTTP on Cloudflare Workers or any fetch-capable runtime, with signed sessions, origin checks, and JSON-RPC request handling. It lets developers define tools with JSON Schema validation and per-request context.MIT