435 lines
No EOL
11 KiB
Text
435 lines
No EOL
11 KiB
Text
---
|
|
title: "Quickstart"
|
|
description: "Get started with building your first MCP server and connecting it to an MCP host"
|
|
---
|
|
|
|
In this tutorial, we'll build a simple MCP server with a `get_weather` tool and connect it to a popular MCP host, Claude for Desktop. We'll start with a basic setup, and then progress to more complex use cases.
|
|
|
|
You can think of MCP as a bridge between your LLMs and your data and tools. Model Context Protocol was developed by Anthropic (the makers of [Claude](https://claude.ai)), but MCP can work with any Large Language Model provider that supports tool calling.
|
|
|
|
### What is MCP?
|
|
|
|
MCP is an open protocol that standardizes how applications provide context to LLMs. Think of it as the "Language Server Protocol for AI" - it separates the concerns of providing context from the LLM interaction itself. This separation enables:
|
|
|
|
1. **Modularity**: Each MCP server focuses on one specific integration or capability
|
|
2. **Reusability**: The same server can be used with any MCP-compatible client
|
|
3. **Composability**: Multiple servers can be combined to create powerful workflows
|
|
4. **Standardization**: A common protocol for all integrations reduces complexity
|
|
|
|
### What we'll be building
|
|
|
|
Many LLMs (including Claude) do not currently have the ability to fetch the current temperature and weather conditions. Let's use MCP to solve that!
|
|
|
|
<Frame>
|
|
<img src="/images/quickstart-weather.png" />
|
|
</Frame>
|
|
|
|
Servers can connect to any host. We'll walk through using Claude Desktop as the host and eventually show you how to build your own.
|
|
|
|
<Accordion title="Why Claude Desktop and not Claude.ai?">
|
|
Because servers are locally run, MCP currently only supports desktop hosts. Remote hosts are in active development.
|
|
</Accordion>
|
|
|
|
### Core MCP Concepts
|
|
|
|
MCP servers can provide three main types of capabilities:
|
|
|
|
1. **Resources**: File-like data that can be read by clients (like API responses or file contents)
|
|
2. **Tools**: Functions that can be called by the LLM (with user approval)
|
|
3. **Prompts**: Pre-written templates that help users accomplish specific tasks
|
|
|
|
This tutorial focuses on tools, but we have intermediate tutorials if you'd like to learn more about Resources and Prompts.
|
|
|
|
### Prerequisite knowledge
|
|
|
|
This quickstart assumes you have familiarity with:
|
|
- Python or TypeScript
|
|
- LLMs like Claude
|
|
|
|
It also helps to be familiar with [tool use (aka function calling)](https://docs.anthropic.com/en/docs/build-with-claude/tool-use), but it's not required.
|
|
|
|
### System requirements
|
|
|
|
For Python, make sure you have the latest version of Python installed. For TypeScript, make sure you have the latest version of Node installed.
|
|
|
|
### Set up your environment
|
|
|
|
<Tabs>
|
|
<Tab title="Python">
|
|
First, let's install `uv` and set up our Python project and environment:
|
|
|
|
<CodeGroup>
|
|
|
|
```bash MacOS/Linux
|
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
```
|
|
|
|
```bash Windows
|
|
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
```
|
|
|
|
</CodeGroup>
|
|
</Tab>
|
|
<Tab title="Node">
|
|
First, let's install Node.js and npm if you haven't already. You can download them from [nodejs.org](https://nodejs.org/).
|
|
Verify your Node.js installation:
|
|
```bash
|
|
node --version
|
|
npm --version
|
|
```
|
|
For this tutorial, you'll need Node.js version 16 or higher.
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
Now, let's create and set up our project:
|
|
|
|
<Tabs>
|
|
<Tab title='Python'>
|
|
<CodeGroup>
|
|
```bash MacOS/Linux
|
|
# Create a new directory for our project
|
|
uv init mcp-quickstart
|
|
cd mcp-quickstart
|
|
|
|
# Create virtual environment and activate it
|
|
uv venv
|
|
source .venv/bin/activate
|
|
|
|
# Install dependencies
|
|
uv pip install mcp httpx
|
|
|
|
# Clean up boilerplate code
|
|
rm -rf hello.py
|
|
|
|
# Create our files
|
|
mkdir src
|
|
mkdir src/weather
|
|
touch src/__init__.py
|
|
touch src/server.py
|
|
```
|
|
|
|
```bash Windows
|
|
# Create a new directory for our project
|
|
uv init mcp-quickstart
|
|
cd mcp-quickstart
|
|
|
|
# Create virtual environment and activate it
|
|
uv venv
|
|
.venv\Scripts\activate
|
|
|
|
# Install dependencies
|
|
uv pip install mcp httpx
|
|
|
|
# Clean up boilerplate code
|
|
del /f /q hello.py
|
|
|
|
# Create our files
|
|
md src
|
|
md src\weather
|
|
echo.> src\__init__.py
|
|
echo.> src\server.py
|
|
```
|
|
</CodeGroup>
|
|
|
|
Add this code to __init__.py:
|
|
|
|
```python src/weather/__init__.py
|
|
from . import server
|
|
import asyncio
|
|
|
|
def main():
|
|
"""Main entry point for the package."""
|
|
asyncio.run(server.main())
|
|
|
|
# Optionally expose other important items at package level
|
|
__all__ = ['main', 'server']
|
|
```
|
|
</Tab>
|
|
|
|
<Tab title="Node">
|
|
```bash
|
|
# Create a new directory for our project
|
|
mkdir mcp-quickstart
|
|
cd mcp-quickstart
|
|
|
|
# Initialize a new npm project
|
|
npm init -y
|
|
|
|
# Install dependencies
|
|
npm install @modelcontextprotocol/sdk node-fetch
|
|
npm install -D @types/node typescript
|
|
```
|
|
|
|
Then create a new file called `src/index.ts` in your project directory:
|
|
|
|
```bash
|
|
mkdir src
|
|
touch src/index.ts
|
|
```
|
|
Update your package.json to add type: "module" and a build script:
|
|
|
|
```json package.json
|
|
{
|
|
"type": "module",
|
|
"bin": {
|
|
"mcp-quickstart": "./build/index.js"
|
|
},
|
|
"scripts": {
|
|
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
|
|
},
|
|
"files": [
|
|
"build"
|
|
],
|
|
}
|
|
```
|
|
|
|
Create a tsconfig.json:
|
|
|
|
```json tsconfig.json
|
|
{
|
|
"compilerOptions": {
|
|
"target": "ES2022",
|
|
"module": "Node16",
|
|
"moduleResolution": "Node16",
|
|
"outDir": "./build",
|
|
"rootDir": "./src",
|
|
"strict": true,
|
|
"esModuleInterop": true,
|
|
"skipLibCheck": true,
|
|
"forceConsistentCasingInFileNames": true
|
|
},
|
|
"include": ["src/**/*"],
|
|
"exclude": ["node_modules"]
|
|
}
|
|
```
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
Now let's dive into building your server.
|
|
|
|
## Building your server
|
|
|
|
### Importing packages
|
|
|
|
Add these to your server.py:
|
|
```python
|
|
from typing import Any
|
|
import httpx
|
|
from mcp.server.models import InitializationOptions
|
|
import mcp.types as types
|
|
from mcp.server import NotificationOptions, Server
|
|
import mcp.server.stdio
|
|
```
|
|
|
|
### Setting up the instance
|
|
|
|
Then add the cache, server instance and the base URL for the NWS API:
|
|
|
|
```python
|
|
# Cache weather data
|
|
weather_cache: dict[str, Any] = {}
|
|
NWS_API_BASE = "https://api.weather.gov"
|
|
|
|
server = Server("weather")
|
|
```
|
|
|
|
### Implementing tool listing
|
|
|
|
We need to tell clients what tools are available. The `list_tools()` decorator registers this handler:
|
|
|
|
```python
|
|
@server.list_tools()
|
|
async def handle_list_tools() -> list[types.Tool]:
|
|
return [
|
|
types.Tool(
|
|
name="get-weather",
|
|
description="Get weather data for a location",
|
|
inputSchema={
|
|
"type": "object",
|
|
"properties": {
|
|
"latitude": {"type": "number"},
|
|
"longitude": {"type": "number"},
|
|
},
|
|
"required": ["latitude", "longitude"],
|
|
},
|
|
)
|
|
]
|
|
```
|
|
|
|
This defines a single tool that requires latitude and longitude parameters.
|
|
|
|
### Implementing tool execution
|
|
|
|
The tool execution handler processes requests to get weather data:
|
|
|
|
```python
|
|
@server.call_tool()
|
|
async def handle_call_tool(
|
|
name: str,
|
|
arguments: dict | None
|
|
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
|
|
# Validate tool name and arguments
|
|
if name != "get-weather":
|
|
raise ValueError(f"Unknown tool: {name}")
|
|
if not arguments:
|
|
raise ValueError("Missing arguments")
|
|
|
|
lat = arguments.get("latitude")
|
|
lon = arguments.get("longitude")
|
|
if lat is None or lon is None:
|
|
raise ValueError("Missing latitude or longitude")
|
|
|
|
# Fetch weather data
|
|
async with httpx.AsyncClient() as client:
|
|
# Get grid point
|
|
points_url = f"{NWS_API_BASE}/points/{lat},{lon}"
|
|
points_response = await client.get(
|
|
points_url,
|
|
headers={"Accept": "application/json"}
|
|
)
|
|
points_data = points_response.json()
|
|
|
|
# Get forecast
|
|
forecast_url = points_data["properties"]["forecast"]
|
|
forecast_response = await client.get(
|
|
forecast_url,
|
|
headers={"Accept": "application/json"}
|
|
)
|
|
forecast_data = forecast_response.json()
|
|
|
|
# Cache the forecast data
|
|
location_key = f"{lat},{lon}"
|
|
weather_cache[location_key] = forecast_data
|
|
|
|
# Notify clients of resource changes
|
|
await server.request_context.session.send_resource_list_changed()
|
|
|
|
return [
|
|
types.TextContent(
|
|
type="text",
|
|
text=f"Retrieved weather data for location ({lat}, {lon})",
|
|
)
|
|
]
|
|
```
|
|
|
|
### Running the server
|
|
|
|
Finally, implement the main function to run the server:
|
|
|
|
```python
|
|
async def main():
|
|
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
|
|
await server.run(
|
|
read_stream,
|
|
write_stream,
|
|
InitializationOptions(
|
|
server_name="weather",
|
|
server_version="0.1.0",
|
|
capabilities=server.get_capabilities(
|
|
notification_options=NotificationOptions(),
|
|
experimental_capabilities={},
|
|
),
|
|
),
|
|
)
|
|
```
|
|
|
|
Your server is complete! Let's now test your server from an existing MCP host, Claude for Desktop.
|
|
|
|
## Testing your server with Claude for Desktop
|
|
|
|
<Note>
|
|
Unfortunately, Claude for Desktop is not yet available on Linux. Linux users can proceed to the [Building a client](/tutorials/building-a-client) tutorial for a workaround.
|
|
</Note>
|
|
|
|
First, make sure you have Claude for Desktop installed. [You can install the latest version here.](https://claude.ai/download)
|
|
|
|
Open your Claude Desktop App configuration at `~/Library/Application Support/Claude/claude_desktop_config.json` in a text editor.
|
|
|
|
For example, if you have [VS Code](https://code.visualstudio.com/) installed:
|
|
|
|
<Tabs>
|
|
<Tab title="MacOS/Linux">
|
|
```bash
|
|
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
|
|
```
|
|
</Tab>
|
|
<Tab title="Windows">
|
|
```powershell
|
|
code $env:AppData\Claude\claude_desktop_config.json
|
|
```
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
|
|
Add this configuration (replace the parent folder path):
|
|
|
|
<Tabs>
|
|
<Tab title="MacOS/Linux">
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"weather": {
|
|
"command": "uv",
|
|
"args": [
|
|
"--directory",
|
|
"/PATH/TO/PARENT/FOLDER/mcp-quickstart",
|
|
"run",
|
|
"weather"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
</Tab>
|
|
<Tab title="Windows">
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"weather": {
|
|
"command": "uv",
|
|
"args": [
|
|
"--directory",
|
|
"C:\\PATH\TO\PARENT\FOLDER\mcp-quickstart",
|
|
"run",
|
|
"weather"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
</Tab>
|
|
</Tabs>
|
|
|
|
This tells Claude Desktop:
|
|
1. There's an MCP server named "weather"
|
|
2. Launch it by running `uv --directory /PATH/TO/PARENT/FOLDER/mcp-quickstart run weather`
|
|
|
|
Save the file, and restart **Claude Desktop**.
|
|
|
|
### Test with commands
|
|
|
|
You can now test your server by running the following commands in Claude Desktop:
|
|
|
|
- What's the weather in Brooklyn right now?
|
|
- What's the weather in San Francisco?
|
|
|
|
<Frame>
|
|
<img src="/images/quickstart-weather.png" />
|
|
</Frame>
|
|
|
|
<Note>
|
|
Since this is the US National Weather service, the queries will only work for US locations.
|
|
</Note>
|
|
|
|
## What's happening under the hood
|
|
|
|
When you ask a question:
|
|
|
|
1. The client sends your question to Claude
|
|
2. Claude analyzes the available tools and decides which one(s) to use
|
|
3. The client executes the chosen tool(s) through the MCP server
|
|
4. The results are sent back to Claude
|
|
5. Claude formulates a natural language response
|
|
6. The response is displayed to you!
|
|
|
|
## Next steps |