Jeremiah Lowin commited on
Commit
12d4a94
·
1 Parent(s): dc1643f

Update readmeUpdate README

Browse files
README.md CHANGED
@@ -1,187 +1,362 @@
1
- # FastMCP
 
2
 
3
- > **Note**: This is experimental software. The Model Context Protocol itself is only a few days old and the specification is still evolving.
4
 
5
- A fast, pythonic way to build Model Context Protocol (MCP) servers.
 
 
6
 
7
- 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.
8
 
 
 
 
 
 
 
 
 
 
 
 
9
  ## Table of Contents
10
 
11
- - [FastMCP](#fastmcp)
12
- - [Table of Contents](#table-of-contents)
13
- - [Installation](#installation)
14
- - [Quick Start](#quick-start)
15
- - [Core Concepts](#core-concepts)
16
- - [Resources](#resources)
17
- - [Tools](#tools)
 
 
 
 
18
  - [Development](#development)
19
- - [Running the Dev Inspector](#running-the-dev-inspector)
20
- - [Installing in Claude](#installing-in-claude)
21
- - [License](#license)
 
22
 
23
  ## Installation
24
 
25
- MCP servers require you to use [uv](https://github.com/astral-sh/uv) as your dependency manager.
26
-
27
- Install uv with brew:
28
  ```bash
29
- brew install uv
 
 
30
  ```
31
- *(Editor's note: I was unable to get MCP servers working unless uv was installed with brew.)*
32
 
33
- Install FastMCP:
34
  ```bash
35
- uv pip install fastmcp
36
  ```
37
 
38
- ## Quick Start
39
 
40
- Here's a simple example that exposes your desktop directory as a resource and provides a basic addition tool:
41
 
42
  ```python
43
- from pathlib import Path
44
  from fastmcp import FastMCP
45
 
46
- # Create server
 
47
  mcp = FastMCP("Demo")
48
 
49
- @mcp.resource("dir://desktop")
50
- def desktop() -> list[str]:
51
- """List the files in the user's desktop"""
52
- desktop = Path.home() / "Desktop"
53
- return [str(f) for f in desktop.iterdir()]
54
 
 
55
  @mcp.tool()
56
  def add(a: int, b: int) -> int:
57
  """Add two numbers"""
58
  return a + b
59
 
60
- if __name__ == "__main__":
61
- mcp.run()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  ```
63
 
 
 
 
 
 
 
 
 
 
 
64
  ## Core Concepts
65
 
66
- FastMCP makes it easy to expose two types of functionality to LLMs: Resources and Tools.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
  ### Resources
69
 
70
- Resources are data sources that can be accessed by the LLM. They're perfect for providing context like files, API responses, or database queries.
71
 
72
- 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:
 
 
 
73
 
 
74
  ```python
75
- # Static resource
76
- @mcp.resource("resource://static")
77
- def get_static() -> str:
78
- """Return static content"""
79
- return "Static content"
80
-
81
- # Dynamic resource
82
- @mcp.resource("resource://{city}/weather")
83
- def get_weather(city: str) -> str:
84
- """Get weather for a city"""
85
- return f"Weather for {city}"
86
-
87
- # Multiple parameters are supported
88
- @mcp.resource("db://users/{user_id}/posts/{post_id}")
89
- def get_user_post(user_id: int, post_id: int) -> dict:
90
- """Get a specific post by a user"""
91
- return {
92
- "user_id": user_id,
93
- "post_id": post_id,
94
- "content": "Post content..."
95
- }
96
-
97
- # File resources
98
- @mcp.resource("file://config.json")
99
  def get_config() -> str:
100
- """Read the config file"""
101
- return Path("config.json").read_text()
102
  ```
103
 
104
- Resources can return:
105
- - Strings for text content
106
- - Bytes for binary content
107
- - Other types will be converted to JSON
 
 
 
108
 
109
- 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.
110
 
111
- > **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.
112
 
 
 
 
 
 
 
 
113
 
114
- ### Tools
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
- 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:
117
 
118
  ```python
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  @mcp.tool()
120
- def search_docs(query: str, max_results: int = 5) -> list[dict]:
121
- """Search documentation for relevant entries"""
122
- results = perform_search(query, limit=max_results)
123
- return [{"title": r.title, "excerpt": r.excerpt} for r in results]
 
 
 
124
 
125
  @mcp.tool()
126
- def analyze_image(image_path: str) -> dict:
127
- """Analyze an image and return metadata"""
128
- from PIL import Image
129
- img = Image.open(image_path)
130
- return {
131
- "size": img.size,
132
- "mode": img.mode,
133
- "format": img.format
134
- }
135
  ```
136
 
137
- Tools support:
138
- - Type hints for parameters
139
- - Default values
140
- - Async functions
141
- - Return value conversion to JSON
142
 
143
- ## Development
144
 
145
- FastMCP includes developer tools to make testing and debugging easier.
146
 
147
- ### Running the Dev Inspector
 
148
 
149
- The MCP Inspector helps you test your server during development:
 
 
 
 
 
 
 
 
 
 
 
150
 
151
- ```bash
152
- # Basic usage
153
- fastmcp dev your_server.py
 
 
 
 
154
 
155
- # Install package in editable mode from current directory
156
- fastmcp dev your_server.py --with-editable .
157
 
158
- # Install additional packages
159
- fastmcp dev your_server.py --with pandas --with numpy
160
 
161
- # Combine both
162
- fastmcp dev your_server.py --with-editable . --with pandas --with numpy
 
 
 
 
 
 
 
 
 
163
  ```
164
 
165
- 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.
166
 
167
- ### Installing in Claude
 
 
168
 
169
- To use your server with Claude Desktop:
 
 
 
 
170
 
 
171
  ```bash
172
- # Basic usage
173
- fastmcp install your_server.py --name "My Server"
174
 
175
- # Install package in editable mode
176
- fastmcp install your_server.py --with-editable .
177
 
178
- # Install additional packages
179
- fastmcp install your_server.py --with pandas --with numpy
180
 
181
- # Combine options
182
- fastmcp install your_server.py --with-editable . --with pandas --with numpy
183
  ```
184
 
185
- ## License
 
 
 
 
 
 
 
 
 
 
 
 
 
186
 
187
- Apache 2.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!-- omit in toc -->
2
+ # FastMCP
3
 
4
+ <div align="center">
5
 
6
+ [![PyPI - Version](https://img.shields.io/pypi/v/fastmcp.svg)](https://pypi.org/project/fastmcp)
7
+ [![Tests](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml/badge.svg)](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml)
8
+ [![License](https://img.shields.io/github/license/jlowin/fastmcp.svg)](https://github.com/jlowin/fastmcp/blob/main/LICENSE)
9
 
10
+ </div>
11
 
12
+ 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.
13
+
14
+ 🚧 *Note: FastMCP is under active development, as is the low-level MCP Python SDK* 🏗️
15
+
16
+ Key features:
17
+ * **Intuitive**: Designed to feel familiar to Python developers, with powerful type hints and editor support
18
+ * **Simple**: Build compliant MCP servers with minimal boilerplate
19
+ * **Fast**: High-performance async implementation
20
+ * **Full-featured**: Complete implementation of the MCP specification
21
+
22
+ <!-- omit in toc -->
23
  ## Table of Contents
24
 
25
+ - [Installation](#installation)
26
+ - [Quickstart](#quickstart)
27
+ - [What is MCP?](#what-is-mcp)
28
+ - [Core Concepts](#core-concepts)
29
+ - [Server](#server)
30
+ - [Resources](#resources)
31
+ - [Tools](#tools)
32
+ - [Prompts](#prompts)
33
+ - [Images](#images)
34
+ - [Context](#context)
35
+ - [Deployment](#deployment)
36
  - [Development](#development)
37
+ - [Claude Desktop](#claude-desktop)
38
+ - [Examples](#examples)
39
+ - [Echo Server](#echo-server)
40
+ - [SQLite Explorer](#sqlite-explorer)
41
 
42
  ## Installation
43
 
 
 
 
44
  ```bash
45
+ # We strongly recommend installing with uv
46
+ brew install uv # on macOS
47
+ uv pip install fastmcp
48
  ```
 
49
 
50
+ Or with pip:
51
  ```bash
52
+ pip install fastmcp
53
  ```
54
 
55
+ ## Quickstart
56
 
57
+ Let's create a simple MCP server that exposes a calculator tool and some data:
58
 
59
  ```python
 
60
  from fastmcp import FastMCP
61
 
62
+
63
+ # Create an MCP server
64
  mcp = FastMCP("Demo")
65
 
 
 
 
 
 
66
 
67
+ # Add an addition tool
68
  @mcp.tool()
69
  def add(a: int, b: int) -> int:
70
  """Add two numbers"""
71
  return a + b
72
 
73
+
74
+ # Add a dynamic greeting resource
75
+ @mcp.resource("greeting://{name}")
76
+ def get_greeting(name: str) -> str:
77
+ """Get a personalized greeting"""
78
+ return f"Hello, {name}!"
79
+ ```
80
+
81
+ To use this server, you have two options:
82
+
83
+ 1. Install it in Claude Desktop:
84
+ ```bash
85
+ fastmcp install server.py
86
+ ```
87
+
88
+ 2. Test it with the MCP Inspector:
89
+ ```bash
90
+ fastmcp dev server.py
91
  ```
92
 
93
+ ![MCP Inspector](docs/images/mcp-inspector.png)
94
+
95
+ ## What is MCP?
96
+
97
+ 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:
98
+
99
+ - Expose data through **Resources** (like GET endpoints)
100
+ - Provide functionality through **Tools** (like POST endpoints)
101
+ - Define interaction patterns through **Prompts** (reusable templates for LLM interactions)
102
+
103
  ## Core Concepts
104
 
105
+ *Note: All code examples below assume you've created a FastMCP server instance called `mcp`.*
106
+
107
+ ### Server
108
+
109
+ The FastMCP server is your core interface to the MCP protocol. It handles connection management, protocol compliance, and message routing:
110
+
111
+ ```python
112
+ from fastmcp import FastMCP
113
+
114
+ # Create a named server
115
+ mcp = FastMCP("My App")
116
+
117
+ # Configure host/port for HTTP transport (optional)
118
+ mcp = FastMCP("My App", host="localhost", port=8000)
119
+ ```
120
 
121
  ### Resources
122
 
123
+ 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:
124
 
125
+ - File contents
126
+ - Database schemas
127
+ - API responses
128
+ - System information
129
 
130
+ Resources can be static:
131
  ```python
132
+ @mcp.resource("config://app")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  def get_config() -> str:
134
+ """Static configuration data"""
135
+ return "App configuration here"
136
  ```
137
 
138
+ Or dynamic with parameters (FastMCP automatically handles these as MCP templates):
139
+ ```python
140
+ @mcp.resource("users://{user_id}/profile")
141
+ def get_user_profile(user_id: str) -> str:
142
+ """Dynamic user data"""
143
+ return f"Profile data for user {user_id}"
144
+ ```
145
 
146
+ ### Tools
147
 
148
+ 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.
149
 
150
+ Simple calculation example:
151
+ ```python
152
+ @mcp.tool()
153
+ def calculate_bmi(weight_kg: float, height_m: float) -> float:
154
+ """Calculate BMI given weight in kg and height in meters"""
155
+ return weight_kg / (height_m ** 2)
156
+ ```
157
 
158
+ HTTP request example:
159
+ ```python
160
+ import httpx
161
+
162
+ @mcp.tool()
163
+ async def fetch_weather(city: str) -> str:
164
+ """Fetch current weather for a city"""
165
+ async with httpx.AsyncClient() as client:
166
+ response = await client.get(
167
+ f"https://api.weather.com/{city}"
168
+ )
169
+ return response.text
170
+ ```
171
+
172
+ ### Prompts
173
 
174
+ 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:
175
 
176
  ```python
177
+ @mcp.prompt()
178
+ def review_code(code: str) -> str:
179
+ return f"Please review this code:\n\n{code}"
180
+ ```
181
+
182
+ Or a more structured sequence of messages:
183
+ ```python
184
+ from fastmcp.prompts.base import UserMessage, AssistantMessage
185
+
186
+ @mcp.prompt()
187
+ def debug_error(error: str) -> list[Message]:
188
+ return [
189
+ UserMessage("I'm seeing this error:"),
190
+ UserMessage(error),
191
+ AssistantMessage("I'll help debug that. What have you tried so far?")
192
+ ]
193
+ ```
194
+
195
+
196
+ ### Images
197
+
198
+ FastMCP provides an `Image` class that automatically handles image data in your server:
199
+
200
+ ```python
201
+ from fastmcp import FastMCP, Image
202
+ from PIL import Image as PILImage
203
+
204
  @mcp.tool()
205
+ def create_thumbnail(image_path: str) -> Image:
206
+ """Create a thumbnail from an image"""
207
+ img = PILImage.open(image_path)
208
+ img.thumbnail((100, 100))
209
+
210
+ # FastMCP automatically handles conversion and MIME types
211
+ return Image(data=img.tobytes(), format="png")
212
 
213
  @mcp.tool()
214
+ def load_image(path: str) -> Image:
215
+ """Load an image from disk"""
216
+ # FastMCP handles reading and format detection
217
+ return Image(path=path)
 
 
 
 
 
218
  ```
219
 
220
+ Images can be used as the result of both tools and resources.
 
 
 
 
221
 
222
+ ### Context
223
 
224
+ The Context object gives your tools and resources access to MCP capabilities. To use it, add a parameter annotated with `fastmcp.Context`:
225
 
226
+ ```python
227
+ from fastmcp import FastMCP, Context
228
 
229
+ @mcp.tool()
230
+ async def long_task(files: list[str], ctx: Context) -> str:
231
+ """Process multiple files with progress tracking"""
232
+ for i, file in enumerate(files):
233
+ ctx.info(f"Processing {file}")
234
+ await ctx.report_progress(i, len(files))
235
+
236
+ # Read another resource if needed
237
+ data = await ctx.read_resource(f"file://{file}")
238
+
239
+ return "Processing complete"
240
+ ```
241
 
242
+ The Context object provides:
243
+ - Progress reporting through `report_progress()`
244
+ - Logging via `debug()`, `info()`, `warning()`, and `error()`
245
+ - Resource access through `read_resource()`
246
+ - Request metadata via `request_id` and `client_id`
247
+
248
+ ## Deployment
249
 
250
+ The FastMCP CLI helps you develop and deploy MCP servers.
 
251
 
252
+ 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`.
 
253
 
254
+ 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`.
255
+
256
+ ### Development
257
+
258
+ Test and debug your server with the MCP Inspector:
259
+ ```bash
260
+ # Provide the fully qualified path to your server
261
+ fastmcp dev server.py:my_mcp_server
262
+
263
+ # Or just the file if your server is named 'mcp', 'server', or 'app'
264
+ fastmcp dev server.py
265
  ```
266
 
267
+ 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:
268
 
269
+ ```bash
270
+ # With additional packages
271
+ fastmcp dev server.py --with pandas --with numpy
272
 
273
+ # Using your project's dependencies and up-to-date code
274
+ fastmcp dev server.py --with-editable .
275
+ ```
276
+
277
+ ### Claude Desktop
278
 
279
+ Install your server in Claude Desktop:
280
  ```bash
281
+ # Basic usage (name is taken from your FastMCP instance)
282
+ fastmcp install server.py
283
 
284
+ # With a custom name
285
+ fastmcp install server.py --name "My Server"
286
 
287
+ # With dependencies
288
+ fastmcp install server.py --with pandas --with numpy
289
 
290
+ # Replace an existing server
291
+ fastmcp install server.py --force
292
  ```
293
 
294
+ The server name in Claude will be:
295
+ 1. The `--name` parameter if provided
296
+ 2. The `name` from your FastMCP instance
297
+ 3. The filename if the server can't be imported
298
+
299
+ ## Examples
300
+
301
+ ### Echo Server
302
+ A simple server demonstrating resources, tools, and prompts:
303
+
304
+ ```python
305
+ from fastmcp import FastMCP
306
+
307
+ mcp = FastMCP("Echo")
308
 
309
+ @mcp.resource("echo://{message}")
310
+ def echo_resource(message: str) -> str:
311
+ """Echo a message as a resource"""
312
+ return f"Resource echo: {message}"
313
+
314
+ @mcp.tool()
315
+ def echo_tool(message: str) -> str:
316
+ """Echo a message as a tool"""
317
+ return f"Tool echo: {message}"
318
+
319
+ @mcp.prompt()
320
+ def echo_prompt(message: str) -> str:
321
+ """Create an echo prompt"""
322
+ return f"Please process this message: {message}"
323
+ ```
324
+
325
+ ### SQLite Explorer
326
+ A more complex example showing database integration:
327
+
328
+ ```python
329
+ from fastmcp import FastMCP
330
+ import sqlite3
331
+
332
+ mcp = FastMCP("SQLite Explorer")
333
+
334
+ @mcp.resource("schema://main")
335
+ def get_schema() -> str:
336
+ """Provide the database schema as a resource"""
337
+ conn = sqlite3.connect("database.db")
338
+ schema = conn.execute(
339
+ "SELECT sql FROM sqlite_master WHERE type='table'"
340
+ ).fetchall()
341
+ return "\n".join(sql[0] for sql in schema if sql[0])
342
+
343
+ @mcp.tool()
344
+ def query_data(sql: str) -> str:
345
+ """Execute SQL queries safely"""
346
+ conn = sqlite3.connect("database.db")
347
+ try:
348
+ result = conn.execute(sql).fetchall()
349
+ return "\n".join(str(row) for row in result)
350
+ except Exception as e:
351
+ return f"Error: {str(e)}"
352
+
353
+ @mcp.prompt()
354
+ def analyze_table(table: str) -> str:
355
+ """Create a prompt template for analyzing tables"""
356
+ return f"""Please analyze this database table:
357
+ Table: {table}
358
+ Schema:
359
+ {get_schema()}
360
+
361
+ What insights can you provide about the structure and relationships?"""
362
+ ```
docs/assets/demo-inspector.png ADDED

Git LFS Details

  • SHA256: f090afaf6d72fc42ee1ec073298fba541a69c7ea105f5dc33edb6bb60dabc218
  • Pointer size: 131 Bytes
  • Size of remote file: 813 kB
examples/readme-quickstart.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastmcp import FastMCP
2
+
3
+
4
+ # Create an MCP server
5
+ mcp = FastMCP("Demo")
6
+
7
+
8
+ # Add an addition tool
9
+ @mcp.tool()
10
+ def add(a: int, b: int) -> int:
11
+ """Add two numbers"""
12
+ return a + b
13
+
14
+
15
+ # Add a dynamic greeting resource
16
+ @mcp.resource("greeting://{name}")
17
+ def get_greeting(name: str) -> str:
18
+ """Get a personalized greeting"""
19
+ return f"Hello, {name}!"
src/fastmcp/resources/base.py CHANGED
@@ -27,14 +27,8 @@ class Resource(BaseModel, abc.ABC):
27
  """Set default name from URI if not provided."""
28
  if name:
29
  return name
30
- # Extract everything after the protocol (e.g., "desktop" from "resource://desktop")
31
- uri = info.data.get("uri")
32
- if uri:
33
- uri_str = str(uri)
34
- if "://" in uri_str:
35
- name = uri_str.split("://", 1)[1]
36
- if name:
37
- return name
38
  raise ValueError("Either name or uri must be provided")
39
 
40
  @abc.abstractmethod
 
27
  """Set default name from URI if not provided."""
28
  if name:
29
  return name
30
+ if uri := info.data.get("uri"):
31
+ return str(uri)
 
 
 
 
 
 
32
  raise ValueError("Either name or uri must be provided")
33
 
34
  @abc.abstractmethod
tests/resources/test_resources.py CHANGED
@@ -45,7 +45,7 @@ class TestResourceValidation:
45
  uri="resource://my-resource",
46
  fn=dummy_func,
47
  )
48
- assert resource.name == "my-resource"
49
 
50
  def test_resource_name_validation(self):
51
  """Test name validation."""
 
45
  uri="resource://my-resource",
46
  fn=dummy_func,
47
  )
48
+ assert resource.name == "resource://my-resource"
49
 
50
  def test_resource_name_validation(self):
51
  """Test name validation."""