Building an MCP Server for Claude Desktop
- Weâll take two functions (below) you already know how to write and turn each into a tool that Claude Desktop can call:
- one that does some math
- one that works with a Python list
- By the end, youâll have a small MCP server running on your own computer, connected to Claude Desktop as a host.
Install FastMCP
- FastMCP is a Python framework for building MCP servers.
- uv can be used to setup your MCP server and install FastMCP into your environment.
bash
1uv init --no-package
2uv add fastmcp- This adds the fastmcp package to your pyproject.toml and installs it into your .venv.
Turn a Python Function into a Tool
- Remember the sphere calculator from the Functions and Modules lesson? It took a radius and computed the diameter, circumference, and surface area.
- We can expose that same logic to Claude as a tool by wrapping it with the
@mcp.tool()decorator.
python
1import math
2from fastmcp import FastMCP
3
4# Create the MCP server
5mcp = FastMCP("CourseTools")
6
7@mcp.tool()
8def sphere_measurements(radius: float) -> list:
9 """Return sphere measurements as a list."""
10 diameter = 2 * radius
11 circumference = diameter * math.pi
12 surface_area = 4 * math.pi * radius * radius
13 return [diameter, circumference, surface_area]- On line 5, we create an instance of FastMCP and give our server a name, âCourseToolsâ. This is our server.
- On line 7, the
@mcp.tool()decorator registers the function below it as something Claude is allowed to call. - On line 8, notice the function looks like the ones youâve already written. The only new part is the decorator above it.
- The functionâs type hints (the
: floatand-> list) are important â they tell Claude what kind of input to give the function and what kind of output to expect.
- The functionâs type hints (the
- On line 9, the docstring (the text in triple quotes) tells Claude what the tool does and when to use it.
Turn a List-Mutating Function into a Tool
- Tools can do more than just calculate and return something. Tools can also change a list over time, the same way insert() and append() changed your lists in Ex4.
- This time, our list will live outside any function, at the top level of the file. That means every tool call shares and can modify the exact same list, instead of rebuilding it from scratch each time.
python
15# A persistent to-do list, shared across every tool call
16tasks = ["Finish CIS240 homework", "Study for math quiz", "Buy groceries"]
17
18@mcp.tool()
19def add_task(task: str, add_to_beginning: bool = False) -> list:
20 """Add a task to the to-do list. Use add_to_beginning=True to add it to the front"""
21 if add_to_beginning:
22 tasks.insert(0, task)
23 else:
24 tasks.append(task)
25 return tasks
26
27if __name__ == "__main__":
28 mcp.run()- On line 16, we define tasks outside of any function. This list is initialized once, when the server starts, and every tool call after that shares and can change that same list.
- On line 19,
add_task()takes two parameters: task (a string) and add_to_beginning (a boolean that defaults to False). Claude only needs to pass add_to_beginning if the user actually wants the task added to the front of the list. - On lines 21-25, add_task() uses an if/else conditional test to decide how to add the new task.
insert(0, task)puts it at the very front of the list, while the default,append(task), puts it at the end. - Right now thereâs no way to mark a task as done however. Youâll build that yourself in the correspondingexercise, using two more list methods: index() and pop().
Connecting the Server to Claude Desktop
- Claude Desktop needs to know where your server is and how to start it. This is done through a configuration file called claude_desktop_config.json.
On Mac, this file lives at:
1~/Library/Application Support/Claude/claude_desktop_config.jsonOn Windows, this file lives at:
1%APPDATA%\Claude\claude_desktop_config.json- If the file doesnât already exist (it isnât created automatically when you install Claude Desktop), create it via Claude menu â Settings â Developer â Edit Config. Then, add an entry that tells Claude Desktop to run your server with uv, the same way youâve been running your own scripts with uv run:
json
1{
2 "mcpServers": {
3 "course-tools": {
4 "command": "uv",
5 "args": ["--directory", "/absolute/path/to/your/project", "run", "main.py"]
6 }
7 }
8}- Replace â/absolute/path/to/your/projectâ with the full path to the folder containing main.py (forward slashes work fine in this file on both Mac and Windows).
- Completely quit and reopen Claude Desktop so it picks up the change. Note: you may have to stop the service with ctrl+alt+del on Windows and then reopen Claude Desktop.
- Open Settings > Connectors in Claude Desktop. You should see course-tools listed. Make sure itâs enabled.
Testing the Tools
- Start a new conversation in Claude Desktop and ask it something that requires one of your tools, for example:
1What are the diameter, circumference, and surface area of a sphere with a radius of 2.5?- Claude Desktop should show that it wants to use the sphere_measurements tool before running it. This is Claude asking your permission to call code on your computer. Approve it, and Claude should use the returned values to answer in plain English.
- Next, try the list-mutating tool:
1Add "Fold laundry" to the beginning of my to-do list.- Claude will call add_task with task=âFold laundryâ and add_to_beginning=True, and report back the updated list.
- Try asking Claude to mark a task as done â nothing will happen yet, because complete_task doesnât exist. Thatâs what youâll build in the exercise below.
Looking Ahead
- Later this semester, once weâve built the Code Journals Django application, weâll use this same pattern to let Claude query and summarize our own database.