Building an MCP Server for Claude Desktop

  • In this demo, we’ll turn a pandas query into a tool that Claude Desktop can call. We’ll use a small sample dataset so you can follow along on its own, but the exact same pattern works with any DataFrame you’re already working with in a notebook.
  • 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.
  • Create a new project folder for this demo, open a terminal there, and set it up with uv:
bash
1uv init --no-package
2uv add fastmcp pandas
  • This adds fastmcp and pandas to your pyproject.toml and installs them into your .venv.
  • uv init already created a main.py file for you, with a couple of starter lines in it — delete those, we’ll build in main.py for the rest of this demo.
  • Note: if you’re following along from inside a Jupyter notebook instead and don’t want to create main.py by hand, you can use the %%writefile main.py cell magic in a notebook cell to save code to the file instead. Just remember an MCP server has to run as its own separate program — it can’t run as a notebook cell itself, since Claude Desktop needs to launch it directly and mcp.run() blocks forever once it starts.

Set Up a Small Sample Dataset

  • Any DataFrame works here — this is just a stand-in for whatever data you’re actually working with. Run the following once (in a notebook cell or from a .py file) to create a tiny sample_weather.csv file in your project folder:
python
1import pandas as pd
2
3sample_data = pd.DataFrame({
4    "Date": ["2026-01-01", "2026-01-02", "2026-01-03", "2026-01-04", "2026-01-05"],
5    "Average Temperature": [28.0, 30.0, 32.0, 26.0, 34.0],
6})
7sample_data.to_csv("sample_weather.csv", index=False)

Turn a Pandas Query into a Tool

  • Any function that reads from a DataFrame and returns a value can become a tool — the only new part is the @mcp.tool() decorator.
  • Here, we’ll load the sample data once when the server starts, and expose a tool that reports the average temperature between two dates.

main.py

python
1import pandas as pd
2from fastmcp import FastMCP
3
4# Create the MCP server
5mcp = FastMCP("ClimateTools")
6
7# Load the data once, when the server starts
8df = pd.read_csv("sample_weather.csv")
9
10@mcp.tool()
11def average_temperature(start_date: str, end_date: str) -> float:
12    """Return the average temperature in the weather data between two dates."""
13    subset = df.query("Date >= @start_date and Date <= @end_date")
14    return subset["Average Temperature"].mean()
15
16if __name__ == "__main__":
17    mcp.run()
  • On line 8, we load the CSV into a DataFrame named df once, at the top level of the file — outside of any function. Just like the persistent lists you’ve seen in other MCP examples, every tool call shares this same df, so it’s only read from disk one time, not on every question Claude asks.
  • On line 11, average_temperature() takes two parameters, start_date and end_date, both strings.
  • On line 13, we use .query() to filter df down to just the rows between the two dates. The @ in front of start_date and end_date tells pandas these are Python variables — the function’s parameters — not column names. Without the @, pandas would look for columns called start_date and end_date and raise an error.
  • On line 14, .mean() averages the “Average Temperature” column of that filtered subset and returns it. Claude receives this as a single number.
  • Swap sample_weather.csv and the column names for whatever DataFrame you’re actually working with — the pattern stays the same.

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.json

On Windows, this file lives at:

1%APPDATA%\Claude\claude_desktop_config.json
  • If the file doesn’t already exist, create it. Add an entry that tells Claude Desktop to run your server with uv:
json
1{
2  "mcpServers": {
3    "climate-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 and sample_weather.csv (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 climate-tools listed. Make sure it’s enabled.

Testing the Tool

  • Start a new conversation in Claude Desktop and ask it something that requires your tool, for example:
1What was the average temperature between January 1st and January 5th, 2026?
  • Claude Desktop should show that it wants to use the average_temperature tool before running it — this is Claude asking your permission to call code on your computer. Approve it, and Claude should use the returned value to answer in plain English:
1Claude: [calls average_temperature("2026-01-01", "2026-01-05")]
2The average temperature between January 1 and January 5, 2026 was 30.0°F.