Spec Registry
Help your agents use open-source better. Learn more.
Find usage specs for your project’s dependencies
- Author
- tessl
- Last updated
- Spec files
pypi-fastmcp
Describes: pypi/fastmcp
- Description
- The fast, Pythonic way to build MCP servers and clients with minimal boilerplate code.
- Author
- tessl
- Last updated
resources.md docs/
1# Resources System23Resources expose read-only data sources with support for static resources and dynamic templates with URI parameters. The FastMCP resources system provides a clean way to expose data that LLMs can access for context and information retrieval.45## Capabilities67### Resource Classes89Base classes for creating and managing resources with different content types and access patterns.1011```python { .api }12class Resource:13def __init__(14self,15uri: str,16name: str,17description: str,18mime_type: str | None = None19):20"""21Base resource class for static resources.2223Parameters:24- uri: Resource URI for client access25- name: Human-readable resource name26- description: Resource description for LLM understanding27- mime_type: MIME type of resource content28"""2930class FunctionResource(Resource):31"""Resource implementation for function-based resources."""3233class TextResource:34"""Resource for text-based content."""3536class BinaryResource:37"""Resource for binary content (images, files, etc.)."""3839class FileResource:40"""Resource that serves file content from filesystem."""4142class HttpResource:43"""Resource that proxies HTTP requests to external URLs."""4445class DirectoryResource:46"""Resource that serves directory listings or file content."""47```4849### Resource Templates5051Dynamic resources that accept URI parameters for flexible data access.5253```python { .api }54class ResourceTemplate:55def __init__(56self,57uri_template: str,58name: str,59description: str,60mime_type: str | None = None61):62"""63Resource template for dynamic resources with URI parameters.6465Parameters:66- uri_template: URI template with {parameters} placeholders67- name: Human-readable template name68- description: Template description for LLM understanding69- mime_type: MIME type of resource content70"""71```7273### Resource Manager7475Manages resource registration, templates, and resolution within a FastMCP server.7677```python { .api }78class ResourceManager:79def add_resource(self, resource: Resource) -> None:80"""81Add a static resource to the manager.8283Parameters:84- resource: Resource instance to add85"""8687def add_template(self, template: ResourceTemplate) -> None:88"""89Add a resource template for dynamic resources.9091Parameters:92- template: ResourceTemplate instance to add93"""9495def get_resource(self, uri: str) -> Resource | None:96"""97Get a resource by URI.9899Parameters:100- uri: Resource URI to retrieve101102Returns:103Resource instance or None if not found104"""105106def list_resources(self) -> list[Resource]:107"""108List all registered static resources.109110Returns:111List of all resource instances112"""113114def list_templates(self) -> list[ResourceTemplate]:115"""116List all registered resource templates.117118Returns:119List of all resource template instances120"""121```122123## Usage Examples124125### Basic Static Resources126127```python128from fastmcp import FastMCP129130mcp = FastMCP("Info Server")131132@mcp.resource("config://version")133def get_version():134"""Get the current server version."""135return "1.0.0"136137@mcp.resource("config://settings")138def get_settings():139"""Get server configuration settings."""140return {141"debug": True,142"max_connections": 100,143"timeout": 30144}145146@mcp.resource("info://status")147def get_status():148"""Get current server status."""149import psutil150import time151152return {153"uptime": time.time() - mcp.start_time,154"memory_usage": psutil.virtual_memory().percent,155"cpu_usage": psutil.cpu_percent(),156"active_connections": len(mcp.connections)157}158```159160### Dynamic Resource Templates161162```python163from fastmcp import FastMCP164165mcp = FastMCP("Data Server")166167@mcp.resource("users://{user_id}/profile")168def get_user_profile(user_id: int):169"""170Get user profile information by ID.171172Parameters:173- user_id: Unique user identifier174175Returns:176User profile data as JSON177"""178# This would typically query a database179users_db = {1801: {"name": "Alice", "email": "alice@example.com", "status": "active"},1812: {"name": "Bob", "email": "bob@example.com", "status": "inactive"},1823: {"name": "Charlie", "email": "charlie@example.com", "status": "active"}183}184185user = users_db.get(user_id)186if not user:187raise ValueError(f"User {user_id} not found")188189return {190"user_id": user_id,191**user,192"last_updated": "2024-01-01T00:00:00Z"193}194195@mcp.resource("data://{dataset}/{table}")196def get_table_data(dataset: str, table: str):197"""198Get data from a specific dataset table.199200Parameters:201- dataset: Dataset name202- table: Table name within dataset203204Returns:205Table data as JSON206"""207# Validate dataset and table208valid_datasets = ["sales", "marketing", "finance"]209if dataset not in valid_datasets:210raise ValueError(f"Invalid dataset: {dataset}")211212# Mock data structure213data = {214"sales": {215"orders": [216{"id": 1, "amount": 100.0, "date": "2024-01-01"},217{"id": 2, "amount": 250.0, "date": "2024-01-02"}218],219"customers": [220{"id": 1, "name": "Customer A", "region": "North"},221{"id": 2, "name": "Customer B", "region": "South"}222]223},224"marketing": {225"campaigns": [226{"id": 1, "name": "Q1 Campaign", "budget": 10000},227{"id": 2, "name": "Q2 Campaign", "budget": 15000}228]229}230}231232if table not in data.get(dataset, {}):233raise ValueError(f"Table {table} not found in dataset {dataset}")234235return {236"dataset": dataset,237"table": table,238"data": data[dataset][table],239"record_count": len(data[dataset][table])240}241242@mcp.resource("files://{path}")243def get_file_content(path: str):244"""245Get file content by path (with security restrictions).246247Parameters:248- path: File path relative to allowed directory249250Returns:251File content as text or binary data252"""253import os254from pathlib import Path255256# Security: restrict to allowed directory257allowed_dir = Path("./data")258safe_path = allowed_dir / path259260# Prevent directory traversal261if not safe_path.resolve().is_relative_to(allowed_dir.resolve()):262raise ValueError("Access denied: path outside allowed directory")263264if not safe_path.exists():265raise ValueError(f"File not found: {path}")266267if safe_path.is_dir():268# Return directory listing269files = [f.name for f in safe_path.iterdir() if f.is_file()]270dirs = [d.name for d in safe_path.iterdir() if d.is_dir()]271return {272"type": "directory",273"path": path,274"files": files,275"directories": dirs276}277278# Return file content279try:280with open(safe_path, 'r', encoding='utf-8') as f:281content = f.read()282return {283"type": "text",284"path": path,285"content": content,286"size": len(content)287}288except UnicodeDecodeError:289# Binary file290with open(safe_path, 'rb') as f:291content = f.read()292return {293"type": "binary",294"path": path,295"size": len(content),296"content_base64": content.hex() # Or base64.b64encode(content).decode()297}298```299300### Complex Resource with Context301302```python303from fastmcp import FastMCP, Context304import json305306mcp = FastMCP("API Proxy Server")307308@mcp.resource("api://{service}/{endpoint}")309async def proxy_api_request(service: str, endpoint: str, ctx: Context):310"""311Proxy requests to external APIs with authentication and logging.312313Parameters:314- service: Service name (maps to API base URL)315- endpoint: API endpoint path316- ctx: Execution context for HTTP requests and logging317318Returns:319API response data320"""321# Service URL mapping322service_urls = {323"weather": "https://api.weather.com/v1",324"news": "https://api.news.com/v2",325"stocks": "https://api.stocks.com/v1"326}327328if service not in service_urls:329await ctx.error(f"Unknown service: {service}")330raise ValueError(f"Service {service} not supported")331332base_url = service_urls[service]333full_url = f"{base_url}/{endpoint}"334335await ctx.info(f"Proxying request to {service}: {endpoint}")336337try:338# Make HTTP request with authentication339headers = {340"Authorization": f"Bearer {get_api_key(service)}",341"User-Agent": "FastMCP-Proxy/1.0"342}343344response = await ctx.http_request(345method="GET",346url=full_url,347headers=headers348)349350if response.status_code == 200:351await ctx.info(f"Successfully fetched data from {service}")352data = response.json()353354return {355"service": service,356"endpoint": endpoint,357"data": data,358"timestamp": "2024-01-01T00:00:00Z",359"status": "success"360}361else:362await ctx.error(f"API request failed: {response.status_code}")363return {364"service": service,365"endpoint": endpoint,366"error": f"HTTP {response.status_code}",367"status": "error"368}369370except Exception as e:371await ctx.error(f"Request failed: {str(e)}")372return {373"service": service,374"endpoint": endpoint,375"error": str(e),376"status": "error"377}378379def get_api_key(service: str) -> str:380"""Get API key for service (would load from secure config)."""381api_keys = {382"weather": "weather_api_key_here",383"news": "news_api_key_here",384"stocks": "stocks_api_key_here"385}386return api_keys.get(service, "")387```388389### File-Based Resources390391```python392from fastmcp import FastMCP393from fastmcp.utilities.types import Image, File394import mimetypes395from pathlib import Path396397mcp = FastMCP("File Server")398399@mcp.resource("docs://{filename}")400def get_document(filename: str):401"""402Serve documentation files.403404Parameters:405- filename: Name of documentation file406407Returns:408Document content with appropriate MIME type409"""410docs_dir = Path("./docs")411file_path = docs_dir / filename412413# Security check414if not file_path.resolve().is_relative_to(docs_dir.resolve()):415raise ValueError("Access denied")416417if not file_path.exists():418raise ValueError(f"Document not found: {filename}")419420mime_type, _ = mimetypes.guess_type(str(file_path))421422with open(file_path, 'r', encoding='utf-8') as f:423content = f.read()424425return {426"filename": filename,427"content": content,428"mime_type": mime_type or "text/plain",429"size": len(content)430}431432@mcp.resource("images://{image_name}")433def get_image(image_name: str):434"""435Serve image files as Image objects.436437Parameters:438- image_name: Name of image file439440Returns:441Image object with binary data442"""443images_dir = Path("./images")444image_path = images_dir / image_name445446if not image_path.exists():447raise ValueError(f"Image not found: {image_name}")448449with open(image_path, 'rb') as f:450image_data = f.read()451452mime_type, _ = mimetypes.guess_type(str(image_path))453454return Image(455data=image_data,456mime_type=mime_type or "application/octet-stream"457)458459@mcp.resource("downloads://{file_name}")460def get_download_file(file_name: str):461"""462Serve downloadable files as File objects.463464Parameters:465- file_name: Name of file to download466467Returns:468File object with metadata469"""470downloads_dir = Path("./downloads")471file_path = downloads_dir / file_name472473if not file_path.exists():474raise ValueError(f"File not found: {file_name}")475476mime_type, _ = mimetypes.guess_type(str(file_path))477478if mime_type and mime_type.startswith('text/'):479# Text file480with open(file_path, 'r', encoding='utf-8') as f:481content = f.read()482else:483# Binary file484with open(file_path, 'rb') as f:485content = f.read()486487return File(488data=content,489name=file_name,490mime_type=mime_type491)492```493494### Database Resources495496```python497from fastmcp import FastMCP498import sqlite3499import json500501mcp = FastMCP("Database Server")502503@mcp.resource("db://{table}")504def get_table_contents(table: str):505"""506Get contents of a database table.507508Parameters:509- table: Name of database table510511Returns:512Table data as JSON with metadata513"""514# Whitelist allowed tables for security515allowed_tables = ["users", "orders", "products", "categories"]516if table not in allowed_tables:517raise ValueError(f"Access to table '{table}' not allowed")518519conn = sqlite3.connect("database.db")520conn.row_factory = sqlite3.Row # Enable column access by name521cursor = conn.cursor()522523try:524# Get table schema525cursor.execute(f"PRAGMA table_info({table})")526columns = [row['name'] for row in cursor.fetchall()]527528# Get table data529cursor.execute(f"SELECT * FROM {table} LIMIT 100") # Limit for safety530rows = cursor.fetchall()531532# Convert to list of dictionaries533data = [dict(row) for row in rows]534535return {536"table": table,537"columns": columns,538"row_count": len(data),539"data": data,540"query_timestamp": "2024-01-01T00:00:00Z"541}542543except sqlite3.Error as e:544raise ValueError(f"Database error: {str(e)}")545finally:546conn.close()547548@mcp.resource("db://query/{query_name}")549def get_predefined_query(query_name: str):550"""551Execute predefined database queries.552553Parameters:554- query_name: Name of predefined query555556Returns:557Query results as JSON558"""559# Predefined queries for security560queries = {561"user_stats": "SELECT COUNT(*) as total_users, COUNT(CASE WHEN status='active' THEN 1 END) as active_users FROM users",562"recent_orders": "SELECT * FROM orders WHERE created_at > datetime('now', '-7 days') ORDER BY created_at DESC",563"top_products": "SELECT p.*, COUNT(o.product_id) as order_count FROM products p LEFT JOIN orders o ON p.id = o.product_id GROUP BY p.id ORDER BY order_count DESC LIMIT 10"564}565566if query_name not in queries:567raise ValueError(f"Query '{query_name}' not found")568569conn = sqlite3.connect("database.db")570conn.row_factory = sqlite3.Row571cursor = conn.cursor()572573try:574cursor.execute(queries[query_name])575rows = cursor.fetchall()576data = [dict(row) for row in rows]577578return {579"query_name": query_name,580"sql": queries[query_name],581"result_count": len(data),582"data": data,583"executed_at": "2024-01-01T00:00:00Z"584}585586except sqlite3.Error as e:587raise ValueError(f"Query error: {str(e)}")588finally:589conn.close()590```591592## Resource Content Types593594Resources can return various content types:595596```python597# Text content598@mcp.resource("text://example")599def text_resource():600return "Plain text content"601602# JSON data603@mcp.resource("json://example")604def json_resource():605return {"key": "value", "numbers": [1, 2, 3]}606607# Binary data as Image608@mcp.resource("image://example")609def image_resource():610with open("example.png", "rb") as f:611return Image(f.read(), mime_type="image/png")612613# Binary data as File614@mcp.resource("file://example")615def file_resource():616return File(617data=b"binary content",618name="example.bin",619mime_type="application/octet-stream"620)621```