Merge pull request #783 from jlowin/tutorials

Add tutorials
This commit is contained in:
Jeremiah Lowin 2025-06-10 12:32:54 -04:00 committed by GitHub
commit c1e8d0c3a4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 270 additions and 59 deletions

View file

@ -52,84 +52,97 @@
}
},
"navigation": {
"groups": [
"anchors": [
{
"group": "Get Started",
"pages": [
"getting-started/welcome",
"getting-started/installation",
"getting-started/quickstart",
"updates"
]
},
{
"group": "Servers",
"pages": [
"servers/fastmcp",
"anchor": "Documentation",
"groups": [
{
"group": "Core Components",
"icon": "toolbox",
"group": "Get Started",
"pages": [
"servers/tools",
"servers/resources",
"servers/prompts",
"servers/context"
"getting-started/welcome",
"getting-started/installation",
"getting-started/quickstart",
"updates"
]
},
{
"group": "Authentication",
"icon": "shield-check",
"group": "Servers",
"pages": [
"servers/auth/bearer"
"servers/fastmcp",
{
"group": "Core Components",
"icon": "toolbox",
"pages": [
"servers/tools",
"servers/resources",
"servers/prompts",
"servers/context"
]
},
{
"group": "Authentication",
"icon": "shield-check",
"pages": [
"servers/auth/bearer"
]
},
"servers/openapi",
"servers/proxy",
"servers/composition",
{
"group": "Deployment",
"icon": "upload",
"pages": [
"deployment/running-server",
"deployment/asgi"
]
}
]
},
"servers/openapi",
"servers/proxy",
"servers/composition",
{
"group": "Deployment",
"icon": "upload",
"group": "Clients",
"pages": [
"deployment/running-server",
"deployment/asgi"
"clients/client",
"clients/transports",
{
"group": "Authentication",
"icon": "user-shield",
"pages": [
"clients/auth/oauth",
"clients/auth/bearer"
]
},
"clients/advanced-features"
]
},
{
"group": "Integrations",
"pages": [
"integrations/anthropic",
"integrations/claude-desktop",
"integrations/openai",
"integrations/gemini",
"integrations/contrib"
]
},
{
"group": "Patterns",
"pages": [
"patterns/tool-transformation",
"patterns/decorating-methods",
"patterns/http-requests",
"patterns/testing",
"patterns/cli"
]
}
]
],
"icon": "book"
},
{
"group": "Clients",
"anchor": "Tutorials",
"icon": "graduation-cap",
"pages": [
"clients/client",
"clients/transports",
{
"group": "Authentication",
"icon": "user-shield",
"pages": [
"clients/auth/oauth",
"clients/auth/bearer"
]
},
"clients/advanced-features"
]
},
{
"group": "Integrations",
"pages": [
"integrations/anthropic",
"integrations/claude-desktop",
"integrations/openai",
"integrations/gemini",
"integrations/contrib"
]
},
{
"group": "Patterns",
"pages": [
"patterns/tool-transformation",
"patterns/decorating-methods",
"patterns/http-requests",
"patterns/testing",
"patterns/cli"
"tutorials/create-mcp-server"
]
}
]

View file

@ -0,0 +1,198 @@
---
title: "How to Create an MCP Server in Python"
sidebarTitle: "Creating an MCP Server"
description: "A step-by-step guide to building a Model Context Protocol (MCP) server using Python and FastMCP, from basic tools to dynamic resources."
icon: server
---
So you want to build a Model Context Protocol (MCP) server in Python. The goal is to create a service that can provide tools and data to AI models like Claude, Gemini, or others that support the protocol. While the [MCP specification](https://modelcontextprotocol.io/specification/) is powerful, implementing it from scratch involves a lot of boilerplate: handling JSON-RPC, managing session state, and correctly formatting requests and responses.
This is where **FastMCP** comes in. It's a high-level framework that handles all the protocol complexities for you, letting you focus on what matters: writing the Python functions that power your server.
This guide will walk you through creating a fully-featured MCP server from scratch using FastMCP.
<Tip>
Every code block in this tutorial is a complete, runnable example. You can copy and paste it into a file and run it, or paste it directly into a Python REPL like IPython to try it out.
</Tip>
### Prerequisites
Make sure you have FastMCP installed. If not, follow the [installation guide](/getting-started/installation).
```bash
pip install fastmcp
```
## Step 1: Create the Basic Server
Every FastMCP application starts with an instance of the `FastMCP` class. This object acts as the container for all your tools and resources.
Create a new file called `my_mcp_server.py`:
```python my_mcp_server.py
from fastmcp import FastMCP
# Create a server instance with a descriptive name
mcp = FastMCP(name="My First MCP Server")
```
That's it! You have a valid (though empty) MCP server. Now, let's add some functionality.
## Step 2: Add a Tool
Tools are functions that an LLM can execute. Let's create a simple tool that adds two numbers.
To do this, simply write a standard Python function and decorate it with `@mcp.tool`.
```python my_mcp_server.py {5-8}
from fastmcp import FastMCP
mcp = FastMCP(name="My First MCP Server")
@mcp.tool
def add(a: int, b: int) -> int:
"""Adds two integer numbers together."""
return a + b
```
FastMCP automatically handles the rest:
- **Tool Name:** It uses the function name (`add`) as the tool's name.
- **Description:** It uses the function's docstring as the tool's description for the LLM.
- **Schema:** It inspects the type hints (`a: int`, `b: int`) to generate a JSON schema for the inputs.
This is the core philosophy of FastMCP: **write Python, not protocol boilerplate.**
## Step 3: Expose Data with Resources
Resources provide read-only data to the LLM. You can define a resource by decorating a function with `@mcp.resource`, providing a unique URI.
Let's expose a simple configuration dictionary as a resource.
```python my_mcp_server.py {10-13}
from fastmcp import FastMCP
mcp = FastMCP(name="My First MCP Server")
@mcp.tool
def add(a: int, b: int) -> int:
"""Adds two integer numbers together."""
return a + b
@mcp.resource("resource://config")
def get_config() -> dict:
"""Provides the application's configuration."""
return {"version": "1.0", "author": "MyTeam"}
```
When a client requests the URI `resource://config`, FastMCP will execute the `get_config` function and return its output (serialized as JSON) to the client. The function is only called when the resource is requested, enabling lazy-loading of data.
## Step 4: Generate Dynamic Content with Resource Templates
Sometimes, you need to generate resources based on parameters. This is what **Resource Templates** are for. You define them using the same `@mcp.resource` decorator but with placeholders in the URI.
Let's create a template that provides a personalized greeting.
```python my_mcp_server.py {15-17}
from fastmcp import FastMCP
mcp = FastMCP(name="My First MCP Server")
@mcp.tool
def add(a: int, b: int) -> int:
"""Adds two integer numbers together."""
return a + b
@mcp.resource("resource://config")
def get_config() -> dict:
"""Provides the application's configuration."""
return {"version": "1.0", "author": "MyTeam"}
@mcp.resource("greetings://{name}")
def personalized_greeting(name: str) -> str:
"""Generates a personalized greeting for the given name."""
return f"Hello, {name}! Welcome to the MCP server."
```
Now, clients can request dynamic URIs:
- `greetings://Ford` will call `personalized_greeting(name="Ford")`.
- `greetings://Marvin` will call `personalized_greeting(name="Marvin")`.
FastMCP automatically maps the `{name}` placeholder in the URI to the `name` parameter in your function.
## Step 5: Run the Server
To make your server executable, add a `__main__` block to your script that calls `mcp.run()`.
```python my_mcp_server.py {19-20}
from fastmcp import FastMCP
mcp = FastMCP(name="My First MCP Server")
@mcp.tool
def add(a: int, b: int) -> int:
"""Adds two integer numbers together."""
return a + b
@mcp.resource("resource://config")
def get_config() -> dict:
"""Provides the application's configuration."""
return {"version": "1.0", "author": "MyTeam"}
@mcp.resource("greetings://{name}")
def personalized_greeting(name: str) -> str:
"""Generates a personalized greeting for the given name."""
return f"Hello, {name}! Welcome to the MCP server."
if __name__ == "__main__":
mcp.run()
```
Now you can run your server from the command line:
```bash
python my_mcp_server.py
```
This starts the server using the default **STDIO transport**, which is how clients like Claude Desktop communicate with local servers. To learn about other transports, like HTTP, see the [Running Your Server](/deployment/running-server) guide.
## The Complete Server
Here is the full code for `my_mcp_server.py` (click to expand):
```python my_mcp_server.py [expandable]
from fastmcp import FastMCP
# 1. Create the server
mcp = FastMCP(name="My First MCP Server")
# 2. Add a tool
@mcp.tool
def add(a: int, b: int) -> int:
"""Adds two integer numbers together."""
return a + b
# 3. Add a static resource
@mcp.resource("resource://config")
def get_config() -> dict:
"""Provides the application's configuration."""
return {"version": "1.0", "author": "MyTeam"}
# 4. Add a resource template for dynamic content
@mcp.resource("greetings://{name}")
def personalized_greeting(name: str) -> str:
"""Generates a personalized greeting for the given name."""
return f"Hello, {name}! Welcome to the MCP server."
# 5. Make the server runnable
if __name__ == "__main__":
mcp.run()
```
## Next Steps
You've successfully built an MCP server! From here, you can explore more advanced topics:
- [**Tools in Depth**](/servers/tools): Learn about asynchronous tools, error handling, and custom return types.
- [**Resources & Templates**](/servers/resources): Discover different resource types, including files and HTTP endpoints.
- [**Prompts**](/servers/prompts): Create reusable prompt templates for your LLM.
- [**Running Your Server**](/deployment/running-server): Deploy your server with different transports like HTTP.