Skip to content

Commit 3701594

Browse files
committed
Split ServerAsyncOperationManager into AsyncOperationStore and AsyncOperationBroker components
1 parent 272b238 commit 3701594

File tree

18 files changed

+1000
-281
lines changed

18 files changed

+1000
-281
lines changed

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1661,6 +1661,40 @@ For more information on mounting applications in Starlette, see the [Starlette d
16611661

16621662
## Advanced Usage
16631663

1664+
### Persistent Async Operations
1665+
1666+
For production deployments, you may want async operations to survive server restarts. The `ServerAsyncOperationManager` uses pluggable `AsyncOperationStore` and `AsyncOperationBroker` components to handle operation persistence and task queuing.
1667+
1668+
#### Operation Lifecycle
1669+
1670+
Async operations follow this lifecycle:
1671+
1672+
1. **Submitted** - Operation token generated and stored
1673+
2. **Working** - Task begins execution
1674+
3. **Completed/Failed/Cancelled** - Operation reaches terminal state with results
1675+
1676+
#### Custom Store and Broker
1677+
1678+
```python
1679+
from mcp.server.fastmcp import FastMCP
1680+
from mcp.shared.async_operations import ServerAsyncOperationManager
1681+
1682+
# Create custom store and broker implementations
1683+
custom_store = MyAsyncOperationStore()
1684+
custom_broker = MyAsyncOperationBroker()
1685+
1686+
# Create operation manager with custom components
1687+
operation_manager = ServerAsyncOperationManager(
1688+
store=custom_store,
1689+
broker=custom_broker
1690+
)
1691+
1692+
# Use with FastMCP
1693+
mcp = FastMCP("My Server", async_operations=operation_manager)
1694+
```
1695+
1696+
For a complete SQLite-based implementation example, see [`examples/servers/sqlite-async-operations/`](examples/servers/sqlite-async-operations/).
1697+
16641698
### Low-Level Server
16651699

16661700
For more control, you can use the low-level server implementation directly. This gives you full access to the protocol and allows you to customize every aspect of your server, including lifecycle management through the lifespan API:

examples/clients/async-reconnect-client/mcp_async_reconnect_client/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ async def call_async_tool(session: ClientSession, token: str | None):
1010

1111
if not token:
1212
result = await session.call_tool("fetch_website", arguments={"url": "https://modelcontextprotocol.io"})
13+
if result.isError:
14+
raise RuntimeError(f"Error calling tool: {result}")
1315
assert result.operation
1416
token = result.operation.token
1517
print(f"Operation started with token: {token}")
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.db
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# SQLite Async Operations Example
2+
3+
This example demonstrates how to implement custom async operations storage and task queuing using SQLite with the MCP Python SDK.
4+
5+
## Architecture
6+
7+
The example showcases the pluggable architecture of the async operations system:
8+
9+
- `SQLiteAsyncOperationStore`: Custom implementation that persists operations to SQLite
10+
- `SQLiteAsyncOperationBroker`: Custom implementation that persists pending tasks to SQLite
11+
- `ServerAsyncOperationManager`: Uses both custom store and broker for full persistence
12+
- `FastMCP`: Configured with the custom async operations manager
13+
14+
## Usage
15+
16+
Install and run the server:
17+
18+
```bash
19+
# Using stdio transport (default)
20+
# Run with default SQLite database
21+
uv run mcp-sqlite-async-operations
22+
23+
# Run with custom database path
24+
uv run mcp-sqlite-async-operations --db-path /path/to/custom.db
25+
26+
# Using streamable-http transport on custom port
27+
uv run mcp-sqlite-async-operations --transport streamable-http --port 8000
28+
```
29+
30+
## Testing Persistent Async Operations
31+
32+
1. Start the server
33+
2. Call one of the async tools (`long_computation` or `fetch_data`)
34+
3. **Restart the server while the operation is running**
35+
4. The operation will automatically resume and complete
36+
5. Use the operation token to check status and retrieve results
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .server import main
2+
3+
if __name__ == "__main__":
4+
main()

0 commit comments

Comments
 (0)