Flask-based Model Context Protocol (MCP) server for Python. Drop it into any Flask app or run it standalone. Ships with security and ops features out of the box.
Spec target: MCP 2025-06-18 — Streamable HTTP transport with a unified /mcp
endpoint (POST for JSON-RPC; GET for SSE). Back-compat routes retained.
Package version: 0.6.1
- Requirements
- Install
- Quick Start
- What’s new in 0.6.1
- Configuration (env vars)
- Core Concepts
- Examples
- FAQ
- Troubleshooting
- License
- Python 3.9+
- Flask 3.x (auto-installed)
pip install flask-mcp-server
Add to requirements.txt
from a file path:
flask-mcp-server==0.6.1
Integrated into an existing Flask app (integrated HTTP):
from flask import Flask
from flask_mcp_server import mount_mcp, Mcp
from flask_mcp_server.http_integrated import mw_auth, mw_ratelimit, mw_cors
app = Flask(__name__)
@Mcp.tool(name="sum")
def sum_(a: int, b: int) -> int:
return a + b
# Mount at /mcp with useful middlewares
mount_mcp(app, url_prefix="/mcp", middlewares=[mw_auth, mw_ratelimit, mw_cors])
if __name__ == "__main__":
app.run(port=8765)
Dedicated server app (ships with docs endpoints):
flask-mcp serve-http
# Swagger: http://127.0.0.1:8765/swagger
# OpenAPI: /docs.json
STDIO transport:
flask-mcp serve-stdio
- Unified MCP endpoint (
/mcp
):- POST a single JSON-RPC message (request/notification/response). If the
Accept
header includestext/event-stream
, the server streams the response via SSE; otherwise returns JSON. - GET to open an SSE stream for server messages (optional). Supports
Last-Event-ID
for resumability in future minor releases.
- POST a single JSON-RPC message (request/notification/response). If the
- Protocol header: Accepts
MCP-Protocol-Version: 2025-06-18
(or2025-03-26
for back-compat); others =>400 Bad Request
. - Sessions (minimal): If the POSTed method is
"initialize"
, response includesMcp-Session-Id
header. Clients can include this header on subsequent requests. - Origin validation: Optional
FLASK_MCP_ALLOWED_ORIGINS
(comma-separated). If set, only requests with matchingOrigin
are allowed at the unified MCP endpoint. - Backwards compatibility: legacy endpoints
/mcp/list
,/mcp/call
,/mcp/batch
remain.
export FLASK_MCP_AUTH_MODE=apikey # none|apikey|hmac
export FLASK_MCP_API_KEYS="k1,k2"
export FLASK_MCP_API_KEYS_MAP="k1:admin|user;k2:user" # per-key roles
export FLASK_MCP_HMAC_SECRET="supersecret" # if using HMAC
export FLASK_MCP_RATE_LIMIT=60/m # format: <N>/<s|m|h|d>
export FLASK_MCP_RATE_SCOPE=key # ip|key
export FLASK_MCP_ALLOWED_ORIGINS="http://localhost:3000,https://your.app"
export FLASK_MCP_CORS_ORIGIN="*"
export FLASK_MCP_LOG_FORMAT=json
export FLASK_MCP_PROVIDERS="my_pkg.mcp_provider:Provider,another.mod:Provider"
- Registry — stores tools, resources, prompts, completions.
- Decorators —
@tool
,@resource
,@prompt
,@completion_provider
register elements. - Facade —
Mcp.tool(...)
etc. (fluent, same params as decorators). - Roles/ACL — per-element role lists checked at call time.
- TTL cache — per-tool/resource memoization via
ttl=int_seconds
(memory backend). - Middleware — pluggable pipeline for integrated HTTP routes.
- Service Providers —
register(container, registry)
andboot(app, registry)
to wire services/routes. - Transports — Streamable HTTP unified endpoint, STDIO, and SSE notifications (hello + registry change).
# List registry (JSON)
curl -s -X POST http://127.0.0.1:8765/mcp \
-H "Content-Type: application/json" \
-H "MCP-Protocol-Version: 2025-06-18" \
-d '{"jsonrpc":"2.0","id":1,"method":"mcp.list"}' | jq .
# Call a tool over SSE
curl -N -X POST http://127.0.0.1:8765/mcp \
-H "Accept: text/event-stream, application/json" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":"2","method":"mcp.call","params":{"kind":"tool","name":"sum","args":{"a":5,"b":7}}}'
from flask_mcp_server import Mcp
@Mcp.tool(name="math.add", roles=["user","admin"], ttl=30)
def add(a: int, b: int) -> int:
return a + b
from flask_mcp_server import resource, ResourceTemplate
@resource(name="profile", ttl=15)
def profile(user_id: int) -> dict:
return {"id": user_id, "name": "Alice"}
tpl = ResourceTemplate("https://api.example.com/items/{id}")
url = tpl.expand(id=42) # "https://api.example.com/items/42"
from flask_mcp_server import prompt, completion_provider
from typing import List
@prompt(name="greet")
def greet(name: str) -> str:
return f"Write a warm one-line greeting for {name}."
@completion_provider(name="cities")
def cities(prefix: str="") -> List[str]:
base = ["Dhaka","Chittagong","Khulna","Rajshahi"]
return [c for c in base if c.lower().startswith(prefix.lower())]
curl -s -X POST http://127.0.0.1:8765/mcp/batch \
-H "Content-Type: application/json" \
-d '[
{"kind":"tool","name":"math.add","args":{"a":1,"b":2}},
{"kind":"prompt","name":"greet","args":{"name":"Bashar"}}
]' | jq .
# GET /mcp opens an SSE stream and emits a hello event and future registry change events.
curl -N http://127.0.0.1:8765/mcp
export FLASK_MCP_AUTH_MODE=apikey
export FLASK_MCP_API_KEYS="secret123"
curl -s http://127.0.0.1:8765/mcp/list -H "X-API-Key: secret123"
# HMAC
python - <<'PY'
import hmac, hashlib, json, requests
secret = b"supersecret"
body = json.dumps({"jsonrpc":"2.0","id":1,"method":"mcp.list"}).encode()
sig = hmac.new(secret, body, hashlib.sha256).hexdigest()
print(requests.post("http://127.0.0.1:8765/mcp",
headers={"X-Signature":"sha256="+sig,"Content-Type":"application/json"},
data=body).text)
PY
export FLASK_MCP_RATE_LIMIT=100/m
export FLASK_MCP_RATE_SCOPE=key # or 'ip'
# my_pkg/mcp_provider.py
from flask_mcp_server import ServiceProvider, Mcp
class Provider(ServiceProvider):
def register(self, container, registry):
@Mcp.tool(name="time.now")
def now() -> str:
import time; return str(int(time.time()))
# Suppose your tools live in package 'my_tools'
python -c "from flask_mcp_server.discovery import discover_package; discover_package('my_tools')"
flask-mcp serve-http # unified /mcp endpoint
flask-mcp serve-stdio # stdio transport
flask-mcp list # print registry
/docs.json
— OpenAPI 3.1 JSON/swagger
— Swagger UI
Is Redis required? No. 0.6.1 uses in-memory backends by default.
Can I mount it into any Flask project? Yes—mount_mcp(app, url_prefix="/mcp")
.
Does it fully implement all MCP JSON-RPC methods? It implements the transport semantics and common operations (mcp.list
, mcp.call
, compat endpoints). If you need additional method names or shapes to match a specific client, open an issue or extend in a Service Provider.
- 400 unsupported_protocol_version — set
MCP-Protocol-Version: 2025-06-18
. - 403 origin_not_allowed — configure
FLASK_MCP_ALLOWED_ORIGINS
or remove it. - 401 invalid_api_key / invalid_signature — check headers and env vars.
- 429 rate_limited — adjust
FLASK_MCP_RATE_LIMIT
or scope.
We welcome contributions to flask-mcp-server
! Whether it's bug fixes, new features, improved documentation, or ideas—you're invited to collaborate.
- Fork this repository and clone it locally.
- Set up a virtual environment and install the package in editable mode:
python -m venv .venv && source .venv/bin/activate pip install -e .[dev,redis,async]
- Run the test suite:
pytest
- Create a new branch for your contribution:
git checkout -b my-feature-branch
- Make your changes and commit:
git commit -m "Add: description of the change"
- Push your branch and open a Pull Request (PR) on GitHub.
- Follow consistent code style (Black, PEP8).
- Write or update tests for any new logic.
- Keep commits focused and descriptive.
- Document any new features in the README if needed.
- Open an issue if you'd like to discuss a large idea before coding.
Use the GitHub Issues page to report bugs, request features, or ask questions.
We appreciate every contribution, big or small. Thank you for helping improve flask-mcp-server
!
MIT