fastmcp/examples/modular_app.py
2025-04-07 17:54:55 -04:00

150 lines
4.2 KiB
Python

"""
Modular FastMCP Application Example
This example demonstrates building a modular application with FastMCP
by separating functionality into domain-specific modules.
"""
import asyncio
from pathlib import Path
from typing import Any, Dict, List, Optional
from fastmcp import Context, FastMCP
# ----- DATA MODULE -----
data_app = FastMCP("Data Module")
# Simulated database
users_db = [
{"id": 1, "name": "Alice", "email": "alice@example.com"},
{"id": 2, "name": "Bob", "email": "bob@example.com"},
{"id": 3, "name": "Charlie", "email": "charlie@example.com"},
]
@data_app.resource("users://all")
def get_all_users() -> List[Dict[str, Any]]:
"""Get all users in the database"""
return users_db
@data_app.resource("users://{user_id}")
def get_user_by_id(user_id: str) -> Optional[Dict[str, Any]]:
"""Get a specific user by ID"""
user_id_int = int(user_id)
for user in users_db:
if user["id"] == user_id_int:
return user
return None
@data_app.tool()
async def create_user(name: str, email: str, ctx: Context) -> Dict[str, Any]:
"""Add a new user to the database"""
# Simulate a slow operation
await ctx.info(f"Creating user {name}...")
await asyncio.sleep(1)
# Create user
new_id = max(user["id"] for user in users_db) + 1
new_user = {"id": new_id, "name": name, "email": email}
users_db.append(new_user)
await ctx.info(f"User created with ID {new_id}")
return new_user
# ----- ANALYTICS MODULE -----
analytics_app = FastMCP("Analytics Module")
@analytics_app.tool()
async def analyze_users(ctx: Context) -> Dict[str, Any]:
"""Run analytics on user data"""
# Get user data from the data module
users = await ctx.read_resource("data:users://all")
# Perform analytics
await ctx.info("Analyzing user data...")
await asyncio.sleep(1)
# Return analytics results
return {
"total_users": len(users),
"domains": {user["email"].split("@")[1] for user in users},
}
@analytics_app.resource("analytics://summary")
def get_analytics_summary() -> Dict[str, Any]:
"""Get a summary of analytics data"""
return {"active_users": len(users_db), "last_updated": "2023-06-01"}
# ----- FILESYSTEM MODULE -----
files_app = FastMCP("Filesystem Module")
@files_app.resource("files://desktop")
def list_desktop_files() -> List[str]:
"""List files on the user's desktop"""
desktop = Path.home() / "Desktop"
return [f.name for f in desktop.iterdir() if f.is_file()]
@files_app.tool()
async def search_files(query: str, ctx: Context) -> List[str]:
"""Search for files matching a query"""
await ctx.info(f"Searching for files matching '{query}'...")
# Simulate a file search
desktop = Path.home() / "Desktop"
files = [
f.name
for f in desktop.iterdir()
if f.is_file() and query.lower() in f.name.lower()
]
await ctx.info(f"Found {len(files)} matching files")
return files
# ----- MAIN APPLICATION -----
# Create the main application that combines all modules
main_app = FastMCP("Modular FastMCP Demo")
@main_app.tool()
async def get_system_info(ctx: Context) -> Dict[str, Any]:
"""Get comprehensive system information"""
await ctx.info("Gathering system information...")
# Use the mounted modules to gather info
users = await ctx.read_resource("data:users://all")
analytics = await ctx.read_resource("analytics:analytics://summary")
desktop_files = await ctx.read_resource("files:files://desktop")
return {
"users": {"count": len(users), "names": [user["name"] for user in users]},
"analytics": analytics,
"files": {"desktop_count": len(desktop_files)},
}
# Mount all modules to the main app
main_app.mount("data", data_app)
main_app.mount("analytics", analytics_app)
main_app.mount("files", files_app)
if __name__ == "__main__":
# Now register resources (which requires async)
async def initialize_resources():
await main_app.register_all_mounted_resources()
print("Resources registered successfully!")
# Initialize resources
asyncio.run(initialize_resources())
# Start the server
print("Starting modular FastMCP application...")
main_app.run()