MiniMCP is designed with simplicity at its core, it exposes a single asynchronous function to handle MCP messages—Pass in a request, and it returns the response ⭐ While MiniMCP supports bidirectional messaging, it’s not a mandatory requirement—So you can use plain HTTP for communication ⭐ MiniMCP is primarily built for remote MCP servers but works just as well for local servers ⭐ MiniMCP ships with built-in transport mechanisms (stdio, HTTP via Starlette, and Streamable HTTP via Starlette)—You’re free to use them directly or implement your own ⭐ MiniMCP is built on the official MCP Python SDK, ensuring standardized context and resource sharing.
The Model Context Protocol (MCP) is a powerful, standardized way for AI applications to connect with external data sources and tools. It follows a client–server architecture, where communication happens through well-defined MCP messages in the JSON-RPC 2.0 format. The key advantage of MCP is interoperability: once a server supports MCP, any MCP-compatible AI client can connect to it without custom integration code. The official MCP Python SDK provides a low-level implementation of the protocol, while FastMCP offers a higher-level, Pythonic interface.
MiniMCP rethinks the MCP server from the ground up, keeping the core functionality lightweight and independent of the transport layer, bidirectional communication, session management, and auth mechanisms. Additionally, instead of a stream-based interface, MiniMCP exposes a simple asynchronous handle function that takes a JSON-RPC 2.0 message string as input and returns a JSON-RPC 2.0 message string as output.
- Stateless: Scalability, simplicity, and reliability are crucial for remote MCP servers. MiniMCP is stateless at its core, making it robust, easy to scale, and straightforward to maintain.
- Bidirectional is optional: Many use cases work perfectly with a simple request–response channel without needing bidirectional communication. MiniMCP was built with this in mind and provides a simple HTTP transport while adhering to the specification.
- Embeddable: Already have an application built with FastAPI (or another framework)? You can embed a MiniMCP server under a single endpoint—or multiple servers under multiple endpoints—Unlike with mounting, you can use your existing dependency injection system.
- Scope and Context: MiniMCP provides a type-checked scope object that travels with each message. This allows you to pass extra details such as authentication, user info, session data, or database handles. Inside the handler, the scope is available in the context—so you’re free to use your preferred session or user management mechanisms.
- Security: MiniMCP encourages you to use your existing battle-tested security mechanism instead of enforcing one - A MiniMCP server built with FastAPI can be as secure as any FastAPI application!
- Stream on Demand: MiniMCP comes with a smart Streamable HTTP implementation. If the handler just returns a response, the server replies with a normal JSON HTTP response. An event stream is only opened when the server actually needs to push notifications to the client.
- Separation of Concerns: The transport layer is fully decoupled from message handling. This makes it easy to adapt MiniMCP to different environments and protocols without rewriting your core business logic.
- Minimal Dependencies: MiniMCP keeps its footprint small, depending only on the official MCP SDK. This makes it lightweight, easy to maintain, and less prone to dependency conflicts.
The following features are already available in MiniMCP.
- 🧩 Server primitives - Tools, Prompts and Resources
- 🔗 Transports - stdio, HTTP, Streamable HTTP
- 🔄 Server to client messages - Progress notification
- 🛠 Typed scope and handler context
- ⚡ Asynchronous - stateless message processing
- 📝 Easy handler registration for different MCP message types
- ⏱️ Enforces idle time and concurrency limits
- 📦 Web frameworks - In-built support for Starlette/FastAPI
These features may be added in the future if the need arises.
⚠️ Server-initiated messaging⚠️ Built-in support for more frameworks—Flask, Django etc.⚠️ Client primitives - Sampling, Elicitation, Logging⚠️ Pagination⚠️ Resumable Streamable HTTP with GET method support⚠️ MCP Client (As shown in the integration tests, MiniMCP works seamlessly with existing MCP clients, and there’s currently no need for a custom client)
These features are not expected to be built into MiniMCP in the foreseeable future.
- 🚫 Session management
- 🚫 Authentication
The snippets below provide a quick overview of how to use MiniMCP. Check out the examples for more.
pip install minimcp
The following example demonstrates simple registration and basic message processing using the handle function.
mcp = MiniMCP(name="MathServer")
# Tool
@mcp.tool()
def add(a:int, b:int) -> int:
"Add two numbers"
return a + b
# Prompt
@mcp.prompt()
def problem_solving(problem_description: str) -> str:
"Prompt to systematically solve math problems."
return f"""You are a math problem solver. Solve the following problem step by step.
Problem: {problem_description}
"""
# Resource
@mcp.resource("math://constants/pi")
def pi_value() -> float:
"""Value of π (pi) to be used"""
return 3.14
request_msg = '{"jsonrpc": "2.0", "id": "1", "method": "ping"}'
response_msg = await mcp.handle(request_msg, scope={...})
# response_msg = '{"jsonrpc": "2.0", "id": "1", "result": {}}'
This minimal example shows how to expose an MCP tool over HTTP using FastAPI.
from fastapi import FastAPI, Request
from minimcp import MiniMCP, starlette
# This can be an existing FastAPI/Starlette app (with authentication, middleware, etc.)
app = FastAPI()
# Create an MCP instance
mcp = MiniMCP(name="MathServer")
# Register a simple tool
@mcp.tool(description="Add two numbers")
def add(a:int, b:int) -> int:
return a + b
# Define the MCP endpoint
@app.post("/mcp")
async def handle_mcp_request(request: Request):
return await starlette.http_transport(mcp.handle, request)
This section provides an overview of the key classes, their functions, and the arguments they accept.
As the name suggests, MiniMCP is the core class for creating a server. It requires a server name as its only mandatory argument; all other arguments are optional. You can also specify the type of the scope object, which is passed through the system.
MiniMCP provides:
- Tool, Prompt, and Resource managers — used to register handlers.
- A Context manager — accessible inside handlers.
The handle function processes incoming messages. It accepts a JSON-RPC 2.0 message string and two optional parameters: a send function and a scope object.
MiniMCP controls how many handlers can run at the same time and how long each handler can remain idle. By default, idle_timeout is set to 30 seconds and max_concurrency to 100.
# Instantiation
mcp = MiniMCP[ScopeT](name, [version, instructions, idle_timeout, max_concurrency, raise_exceptions])
# Managers
mcp.tool
mcp.prompt
mcp.resource
mcp.context
# Message handling
response = await mcp.handle(message, [send, scope])
MiniMCP supports three server primitives, each managed by its own primitive manager. These managers are implemented as callable classes that can be used as decorators for registering handler functions.
When called, a manager accepts a primitive definition. If none is provided, the definition is automatically inferred from the handler function.
In addition to decorator usage, all three primitive managers also expose methods to add, list, remove, and invoke handlers programmatically.
# As a decorator
@mcp.tool([name, title, description, annotations, meta])
def handler_func(...):...
# Methods for programmatic access
mcp.tool.add(func, [name, title, description, annotations, meta]) # Register a tool
mcp.tool.remove(name) # Remove a tool by name
mcp.tool.list() # List all registered tools
mcp.tool.call(name, args) # Invoke a tool by name
# As a decorator
@mcp.prompt([name, title, description, meta])
def handler_func(...):...
# Methods for programmatic access
mcp.prompt.add(func, [name, title, description, meta])
mcp.prompt.remove(name)
mcp.prompt.list()
mcp.prompt.get(name, args)
# As a decorator
@mcp.resource(url, [name, title, description, mime_type, annotations, meta])
def handler_func(...):...
# Methods for programmatic access
mcp.resource.add(func, url, [name, title, description, annotations, meta])
mcp.resource.remove(name)
mcp.resource.list()
mcp.resource.list_templates()
mcp.resource.read(uri)
mcp.resource.read_by_name(name, args)
The Context Manager provides access to request metadata (such as the message, scope, responder, and timeout) directly inside handlers. It tracks the currently active handler context, which you can retrieve using mcp_instance.context.get()
. If called outside of a handler, this method raises a ContextError
.
# Context structure
Context(Generic[ScopeT]):
message: JSONRPCMessage # The parsed request message
time_limiter: TimeLimiter # time_limiter.reset() resets the handler idle timeout
scope: ScopeT | None # Scope object passed when calling handle()
responder: Responder | None # Allows sending notifications back to the client
# Accessing context
mcp.context.get() -> Context[ScopeT]
# For common use cases, the following helpers are provided to avoid null checks
mcp.context.get_scope() -> ScopeT
mcp.context.get_responder() -> Responder
The official MCP specification currently defines two standard transport mechanisms: stdio and Streamable HTTP. It also provides flexibility in implementations and also permits custom transports. MiniMCP uses this flexibility to introduce a third option: HTTP transport.
Transport | Directionality | Use Case |
---|---|---|
stdio | Bidirectional | Local integration (e.g., Claude desktop) |
HTTP | Request–response | Simple REST-like message handling |
Streamable HTTP | Bidirectional | Advanced message handling with notifications, progress updates etc. |
HTTP is a subset of Streamable HTTP and doesn't support bidirectional communication. However, as shown in the integration example, it can be added as a RESTful API endpoint in any Python application to host remote MCP servers. Importantly, it remains compatible with Streamable HTTP MCP clients.
MiniMCP also provides a smart Streamable HTTP implementation. It adapts to usage patterns: if the handler simply returns a response, the server replies with a normal JSON HTTP response. An event stream is opened only when the server needs to push notifications to the client. To keep things simple and stateless, this is currently implemented using polling to keep the stream alive, with the option to support fully resumable Streamable HTTP in the future.
You can use the transports as shown below, or wrap mcp.handle
in a custom function to pass a scope or manage lifecycle:
# Stdio
anyio.run(stdio_transport, mcp.handle)
# HTTP
await starlette.http_transport(mcp.handle, request)
# Streamable HTTP
await starlette.streamable_http_transport(mcp.handle, request)
For more details on supported transports, please check the specification compliance document.
The examples include a Math MCP server with four tools—add, subtract, multiply, and divide—demonstrating how MiniMCP works with different transport mechanisms and frameworks.
To run the examples, you’ll need a MiniMCP development setup. After cloning this repository, run the following command from the project root to set up the environment:
uv sync --frozen --all-extras --dev
The table below lists the available examples along with the commands to run them.
# | Transport | Command |
---|---|---|
1 | Stdio | uv run -m examples.math_mcp_server.stdio |
2 | HTTP with FastAPI | uv run uvicorn examples.math_mcp_server.fastapi_http:app --reload |
3 | Streamable HTTP with FastAPI | uv run uvicorn examples.math_mcp_server.fastapi_streamable_http:app --reload |
The Sample MCP Messages document provides example input messages along with their corresponding responses.
Claude desktop can be configured to run the stdio example as follows.
{
"mcpServers":
{
"math-server":
{
"command": "uv",
"args":
[
"--directory",
"/path/to/minimcp",
"run",
"-m",
"examples.math_mcp_server.stdio"
]
}
}
}