diff --git a/README.md b/README.md
index fa9f48e22..83d50a97c 100644
--- a/README.md
+++ b/README.md
@@ -1,187 +1,362 @@
-# FastMCP
+
+# FastMCP
-> **Note**: This is experimental software. The Model Context Protocol itself is only a few days old and the specification is still evolving.
+
-A fast, pythonic way to build Model Context Protocol (MCP) servers.
+[](https://pypi.org/project/fastmcp)
+[](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml)
+[](https://github.com/jlowin/fastmcp/blob/main/LICENSE)
-Anthropic's new [Model Context Protocol](https://modelcontextprotocol.io) is a powerful way to give broadcast new functionality and context to LLMs. However, developing MCP servers can be cumbersome. FastMCP provides a simple, intuitive interface for creating MCP servers in Python.
+
+FastMCP is a high-level, intuitive framework for building [Model Context Protocol (MCP)](https://modelcontextprotocol.io) servers with Python. While MCP is a powerful protocol that enables LLMs to interact with local data and tools in a secure, standardized way, the specification can be cumbersome to implement directly. FastMCP lets you build fully compliant MCP servers in the most Pythonic way possible - in many cases, simply decorating a function is all that's required.
+
+🚧 *Note: FastMCP is under active development, as is the low-level MCP Python SDK* 🏗️
+
+Key features:
+* **Intuitive**: Designed to feel familiar to Python developers, with powerful type hints and editor support
+* **Simple**: Build compliant MCP servers with minimal boilerplate
+* **Fast**: High-performance async implementation
+* **Full-featured**: Complete implementation of the MCP specification
+
+
## Table of Contents
-- [FastMCP](#fastmcp)
- - [Table of Contents](#table-of-contents)
- - [Installation](#installation)
- - [Quick Start](#quick-start)
- - [Core Concepts](#core-concepts)
- - [Resources](#resources)
- - [Tools](#tools)
+- [Installation](#installation)
+- [Quickstart](#quickstart)
+- [What is MCP?](#what-is-mcp)
+- [Core Concepts](#core-concepts)
+ - [Server](#server)
+ - [Resources](#resources)
+ - [Tools](#tools)
+ - [Prompts](#prompts)
+ - [Images](#images)
+ - [Context](#context)
+- [Deployment](#deployment)
- [Development](#development)
- - [Running the Dev Inspector](#running-the-dev-inspector)
- - [Installing in Claude](#installing-in-claude)
- - [License](#license)
+ - [Claude Desktop](#claude-desktop)
+- [Examples](#examples)
+ - [Echo Server](#echo-server)
+ - [SQLite Explorer](#sqlite-explorer)
## Installation
-MCP servers require you to use [uv](https://github.com/astral-sh/uv) as your dependency manager.
-
-Install uv with brew:
-```bash
-brew install uv
-```
-*(Editor's note: I was unable to get MCP servers working unless uv was installed with brew.)*
-
-Install FastMCP:
```bash
+# We strongly recommend installing with uv
+brew install uv # on macOS
uv pip install fastmcp
```
-## Quick Start
+Or with pip:
+```bash
+pip install fastmcp
+```
-Here's a simple example that exposes your desktop directory as a resource and provides a basic addition tool:
+## Quickstart
+
+Let's create a simple MCP server that exposes a calculator tool and some data:
```python
-from pathlib import Path
from fastmcp import FastMCP
-# Create server
+
+# Create an MCP server
mcp = FastMCP("Demo")
-@mcp.resource("dir://desktop")
-def desktop() -> list[str]:
- """List the files in the user's desktop"""
- desktop = Path.home() / "Desktop"
- return [str(f) for f in desktop.iterdir()]
+# Add an addition tool
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
-if __name__ == "__main__":
- mcp.run()
+
+# Add a dynamic greeting resource
+@mcp.resource("greeting://{name}")
+def get_greeting(name: str) -> str:
+ """Get a personalized greeting"""
+ return f"Hello, {name}!"
```
+To use this server, you have two options:
+
+1. Install it in Claude Desktop:
+```bash
+fastmcp install server.py
+```
+
+2. Test it with the MCP Inspector:
+```bash
+fastmcp dev server.py
+```
+
+
+
+## What is MCP?
+
+The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. Think of it like a web API, but specifically designed for LLM interactions. MCP servers can:
+
+- Expose data through **Resources** (like GET endpoints)
+- Provide functionality through **Tools** (like POST endpoints)
+- Define interaction patterns through **Prompts** (reusable templates for LLM interactions)
+
## Core Concepts
-FastMCP makes it easy to expose two types of functionality to LLMs: Resources and Tools.
+*Note: All code examples below assume you've created a FastMCP server instance called `mcp`.*
+
+### Server
+
+The FastMCP server is your core interface to the MCP protocol. It handles connection management, protocol compliance, and message routing:
+
+```python
+from fastmcp import FastMCP
+
+# Create a named server
+mcp = FastMCP("My App")
+
+# Configure host/port for HTTP transport (optional)
+mcp = FastMCP("My App", host="localhost", port=8000)
+```
### Resources
-Resources are data sources that can be accessed by the LLM. They're perfect for providing context like files, API responses, or database queries.
+Resources are how you expose data to LLMs. They're similar to GET endpoints in a REST API - they provide data but shouldn't perform significant computation or have side effects. Some examples:
-FastMCP provides a simple `@resource` decorator that handles both static and dynamic resources. While the MCP spec distinguishes between resources and templates, FastMCP automatically handles this distinction based on your function signature:
+- File contents
+- Database schemas
+- API responses
+- System information
+Resources can be static:
```python
-# Static resource
-@mcp.resource("resource://static")
-def get_static() -> str:
- """Return static content"""
- return "Static content"
-
-# Dynamic resource
-@mcp.resource("resource://{city}/weather")
-def get_weather(city: str) -> str:
- """Get weather for a city"""
- return f"Weather for {city}"
-
-# Multiple parameters are supported
-@mcp.resource("db://users/{user_id}/posts/{post_id}")
-def get_user_post(user_id: int, post_id: int) -> dict:
- """Get a specific post by a user"""
- return {
- "user_id": user_id,
- "post_id": post_id,
- "content": "Post content..."
- }
-
-# File resources
-@mcp.resource("file://config.json")
+@mcp.resource("config://app")
def get_config() -> str:
- """Read the config file"""
- return Path("config.json").read_text()
+ """Static configuration data"""
+ return "App configuration here"
```
-Resources can return:
-- Strings for text content
-- Bytes for binary content
-- Other types will be converted to JSON
-
-When your resource URI includes parameters in curly braces (like `{city}`) and your function accepts matching arguments, FastMCP automatically sets up a template resource behind the scenes. This means you don't need to worry about the distinction between resources and templates in the MCP spec - just write your function, and FastMCP handles the rest.
-
-> **Note**: If you're familiar with the MCP spec, you might notice that dynamic resources are implemented as templates under the hood. FastMCP simplifies this by providing a unified interface through the `@resource` decorator. This is similar to how web frameworks often unify GET and POST handlers under a single route decorator.
-
+Or dynamic with parameters (FastMCP automatically handles these as MCP templates):
+```python
+@mcp.resource("users://{user_id}/profile")
+def get_user_profile(user_id: str) -> str:
+ """Dynamic user data"""
+ return f"Profile data for user {user_id}"
+```
### Tools
-Tools are functions that can be called by the LLM to perform actions. They're great for calculations, API calls, or any interactive functionality. Tools are defined using the `@tool` decorator:
+Tools let LLMs take actions through your server. Unlike resources, tools are expected to perform computation and have side effects. They're similar to POST endpoints in a REST API.
+Simple calculation example:
```python
@mcp.tool()
-def search_docs(query: str, max_results: int = 5) -> list[dict]:
- """Search documentation for relevant entries"""
- results = perform_search(query, limit=max_results)
- return [{"title": r.title, "excerpt": r.excerpt} for r in results]
+def calculate_bmi(weight_kg: float, height_m: float) -> float:
+ """Calculate BMI given weight in kg and height in meters"""
+ return weight_kg / (height_m ** 2)
+```
+
+HTTP request example:
+```python
+import httpx
@mcp.tool()
-def analyze_image(image_path: str) -> dict:
- """Analyze an image and return metadata"""
- from PIL import Image
- img = Image.open(image_path)
- return {
- "size": img.size,
- "mode": img.mode,
- "format": img.format
- }
+async def fetch_weather(city: str) -> str:
+ """Fetch current weather for a city"""
+ async with httpx.AsyncClient() as client:
+ response = await client.get(
+ f"https://api.weather.com/{city}"
+ )
+ return response.text
```
-Tools support:
-- Type hints for parameters
-- Default values
-- Async functions
-- Return value conversion to JSON
+### Prompts
-## Development
+Prompts are reusable templates that help LLMs interact with your server effectively. They're like "best practices" encoded into your server. A prompt can be as simple as a string:
-FastMCP includes developer tools to make testing and debugging easier.
+```python
+@mcp.prompt()
+def review_code(code: str) -> str:
+ return f"Please review this code:\n\n{code}"
+```
-### Running the Dev Inspector
+Or a more structured sequence of messages:
+```python
+from fastmcp.prompts.base import UserMessage, AssistantMessage
-The MCP Inspector helps you test your server during development:
+@mcp.prompt()
+def debug_error(error: str) -> list[Message]:
+ return [
+ UserMessage("I'm seeing this error:"),
+ UserMessage(error),
+ AssistantMessage("I'll help debug that. What have you tried so far?")
+ ]
+```
+
+
+### Images
+
+FastMCP provides an `Image` class that automatically handles image data in your server:
+
+```python
+from fastmcp import FastMCP, Image
+from PIL import Image as PILImage
+
+@mcp.tool()
+def create_thumbnail(image_path: str) -> Image:
+ """Create a thumbnail from an image"""
+ img = PILImage.open(image_path)
+ img.thumbnail((100, 100))
+
+ # FastMCP automatically handles conversion and MIME types
+ return Image(data=img.tobytes(), format="png")
+
+@mcp.tool()
+def load_image(path: str) -> Image:
+ """Load an image from disk"""
+ # FastMCP handles reading and format detection
+ return Image(path=path)
+```
+
+Images can be used as the result of both tools and resources.
+
+### Context
+
+The Context object gives your tools and resources access to MCP capabilities. To use it, add a parameter annotated with `fastmcp.Context`:
+
+```python
+from fastmcp import FastMCP, Context
+
+@mcp.tool()
+async def long_task(files: list[str], ctx: Context) -> str:
+ """Process multiple files with progress tracking"""
+ for i, file in enumerate(files):
+ ctx.info(f"Processing {file}")
+ await ctx.report_progress(i, len(files))
+
+ # Read another resource if needed
+ data = await ctx.read_resource(f"file://{file}")
+
+ return "Processing complete"
+```
+
+The Context object provides:
+- Progress reporting through `report_progress()`
+- Logging via `debug()`, `info()`, `warning()`, and `error()`
+- Resource access through `read_resource()`
+- Request metadata via `request_id` and `client_id`
+
+## Deployment
+
+The FastMCP CLI helps you develop and deploy MCP servers.
+
+Note that for all deployment commands, you are expected to provide the fully qualified path to your server object. For example, if you have a file `server.py` that contains a FastMCP server named `my_server`, you would provide `path/to/server.py:my_server`.
+
+If your server variable has one of the standard names (`mcp`, `server`, or `app`), you can omit the server name from the path and just provide the file: `path/to/server.py`.
+
+### Development
+
+Test and debug your server with the MCP Inspector:
+```bash
+# Provide the fully qualified path to your server
+fastmcp dev server.py:my_mcp_server
+
+# Or just the file if your server is named 'mcp', 'server', or 'app'
+fastmcp dev server.py
+```
+
+Your server is run in an isolated environment, so you'll need to indicate any dependencies with the `--with` flag. FastMCP is automatically included. If you are working on a uv project, you can use the `--with-editable` flag to mount your current directory:
```bash
-# Basic usage
-fastmcp dev your_server.py
+# With additional packages
+fastmcp dev server.py --with pandas --with numpy
-# Install package in editable mode from current directory
-fastmcp dev your_server.py --with-editable .
-
-# Install additional packages
-fastmcp dev your_server.py --with pandas --with numpy
-
-# Combine both
-fastmcp dev your_server.py --with-editable . --with pandas --with numpy
+# Using your project's dependencies and up-to-date code
+fastmcp dev server.py --with-editable .
```
-The `--with` flag automatically includes `fastmcp` and any additional packages you specify. The `--with-editable` flag installs the package from the specified directory in editable mode, which is useful during development.
-
-### Installing in Claude
-
-To use your server with Claude Desktop:
+### Claude Desktop
+Install your server in Claude Desktop:
```bash
-# Basic usage
-fastmcp install your_server.py --name "My Server"
+# Basic usage (name is taken from your FastMCP instance)
+fastmcp install server.py
-# Install package in editable mode
-fastmcp install your_server.py --with-editable .
+# With a custom name
+fastmcp install server.py --name "My Server"
-# Install additional packages
-fastmcp install your_server.py --with pandas --with numpy
+# With dependencies
+fastmcp install server.py --with pandas --with numpy
-# Combine options
-fastmcp install your_server.py --with-editable . --with pandas --with numpy
+# Replace an existing server
+fastmcp install server.py --force
```
-## License
+The server name in Claude will be:
+1. The `--name` parameter if provided
+2. The `name` from your FastMCP instance
+3. The filename if the server can't be imported
-Apache 2.0
\ No newline at end of file
+## Examples
+
+### Echo Server
+A simple server demonstrating resources, tools, and prompts:
+
+```python
+from fastmcp import FastMCP
+
+mcp = FastMCP("Echo")
+
+@mcp.resource("echo://{message}")
+def echo_resource(message: str) -> str:
+ """Echo a message as a resource"""
+ return f"Resource echo: {message}"
+
+@mcp.tool()
+def echo_tool(message: str) -> str:
+ """Echo a message as a tool"""
+ return f"Tool echo: {message}"
+
+@mcp.prompt()
+def echo_prompt(message: str) -> str:
+ """Create an echo prompt"""
+ return f"Please process this message: {message}"
+```
+
+### SQLite Explorer
+A more complex example showing database integration:
+
+```python
+from fastmcp import FastMCP
+import sqlite3
+
+mcp = FastMCP("SQLite Explorer")
+
+@mcp.resource("schema://main")
+def get_schema() -> str:
+ """Provide the database schema as a resource"""
+ conn = sqlite3.connect("database.db")
+ schema = conn.execute(
+ "SELECT sql FROM sqlite_master WHERE type='table'"
+ ).fetchall()
+ return "\n".join(sql[0] for sql in schema if sql[0])
+
+@mcp.tool()
+def query_data(sql: str) -> str:
+ """Execute SQL queries safely"""
+ conn = sqlite3.connect("database.db")
+ try:
+ result = conn.execute(sql).fetchall()
+ return "\n".join(str(row) for row in result)
+ except Exception as e:
+ return f"Error: {str(e)}"
+
+@mcp.prompt()
+def analyze_table(table: str) -> str:
+ """Create a prompt template for analyzing tables"""
+ return f"""Please analyze this database table:
+Table: {table}
+Schema:
+{get_schema()}
+
+What insights can you provide about the structure and relationships?"""
+```
\ No newline at end of file
diff --git a/docs/assets/demo-inspector.png b/docs/assets/demo-inspector.png
new file mode 100644
index 000000000..5d7715387
Binary files /dev/null and b/docs/assets/demo-inspector.png differ
diff --git a/examples/readme-quickstart.py b/examples/readme-quickstart.py
new file mode 100644
index 000000000..26a0cc138
--- /dev/null
+++ b/examples/readme-quickstart.py
@@ -0,0 +1,19 @@
+from fastmcp import FastMCP
+
+
+# Create an MCP server
+mcp = FastMCP("Demo")
+
+
+# Add an addition tool
+@mcp.tool()
+def add(a: int, b: int) -> int:
+ """Add two numbers"""
+ return a + b
+
+
+# Add a dynamic greeting resource
+@mcp.resource("greeting://{name}")
+def get_greeting(name: str) -> str:
+ """Get a personalized greeting"""
+ return f"Hello, {name}!"
diff --git a/src/fastmcp/resources/base.py b/src/fastmcp/resources/base.py
index 5238dab43..eee7f1d24 100644
--- a/src/fastmcp/resources/base.py
+++ b/src/fastmcp/resources/base.py
@@ -27,14 +27,8 @@ class Resource(BaseModel, abc.ABC):
"""Set default name from URI if not provided."""
if name:
return name
- # Extract everything after the protocol (e.g., "desktop" from "resource://desktop")
- uri = info.data.get("uri")
- if uri:
- uri_str = str(uri)
- if "://" in uri_str:
- name = uri_str.split("://", 1)[1]
- if name:
- return name
+ if uri := info.data.get("uri"):
+ return str(uri)
raise ValueError("Either name or uri must be provided")
@abc.abstractmethod
diff --git a/tests/resources/test_resources.py b/tests/resources/test_resources.py
index 658a1f1d9..8bab8804f 100644
--- a/tests/resources/test_resources.py
+++ b/tests/resources/test_resources.py
@@ -45,7 +45,7 @@ class TestResourceValidation:
uri="resource://my-resource",
fn=dummy_func,
)
- assert resource.name == "my-resource"
+ assert resource.name == "resource://my-resource"
def test_resource_name_validation(self):
"""Test name validation."""