Revamped quickstart again

This commit is contained in:
Zachary Lee 2024-12-09 22:45:23 -05:00
commit 9870471072
No known key found for this signature in database
6 changed files with 637 additions and 926 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View file

@ -3,12 +3,17 @@ title: Introduction
description: 'Get started with the Model Context Protocol (MCP)'
---
Model Context Protocol (MCP) is an open protocol that enables seamless integration between LLM applications and external data sources and tools.
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:
## Why MCP
- **Modularity**: Each MCP server focuses on one specific integration or capability
- **Reusability**: The same server can be used with any MCP-compatible client
- **Composability**: Multiple servers can be combined to create powerful workflows
- **Standardization**: A common protocol for all integrations reduces complexity
MCP is a great choice for anyone building agents or complex integrations on top of LLMs. LLMs frequently need to integrate with data and tools, and MCP provides:
- A growing list of pre-built integrations that your LLM can directly plug into.
## Why MCP?
MCP helps you build agents and complex workflows on top of LLMs. LLMs frequently need to integrate with data and tools, and MCP provides:
- A growing list of pre-built integrations that your LLM can directly plug into
- The flexibility to switch between LLM providers and vendors
- Best practices for securing your data within your infrastructure
@ -40,7 +45,7 @@ flowchart LR
- **Local Data Sources**: Your computer's files, databases, and services that MCP servers can securely access
- **Remote Services**: External systems available over the internet (e.g., through APIs) that MCP servers can connect to
## Get started with MCP
## Get started
Choose the path that best fits your needs:
@ -50,28 +55,19 @@ Choose the path that best fits your needs:
icon="bolt"
href="/quickstart"
>
Build your first MCP client and server in less than 5 minutes
Build and connect to your first MCP server
</Card>
</CardGroup>
## Tutorials
<CardGroup cols={2}>
<Card
title="Take MCP servers for a test run in Claude Desktop"
icon="desktop"
href="/tutorials/claude-desktop"
>
Give Claude Desktop the ability to query SQLite data using MCP
</Card>
<Card
title="MCP clients"
icon="plug"
href="/clients"
>
See a detailed list of MCP clients
</Card>
<Card
title="MCP servers"
title="Building a MCP client"
icon="outlet"
href="/servers"
href="/building-a-client"
>
See a detailed list of MCP servers
Learn how to build your first MCP client
</Card>
</CardGroup>
@ -81,7 +77,7 @@ Dive deeper into MCP's core concepts and capabilities:
<CardGroup cols={2}>
<Card
title="Core Architecture"
title="Core architecture"
icon="sitemap"
href="/docs/concepts/architecture"
>

View file

@ -40,11 +40,11 @@
"navigation": [
{
"group": "Get Started",
"pages": ["introduction", "quickstart", "clients", "servers"]
"pages": ["introduction", "quickstart"]
},
{
"group": "Development Tools",
"pages": ["docs/tools/debugging", "docs/tools/inspector"]
"group": "Tutorials",
"pages": ["tutorials/building-a-client"]
},
{
"group": "Concepts",

View file

@ -1,598 +1,426 @@
---
title: "Quickstart"
description: "Get started with building your first MCP server and client in just a few minutes."
description: "Get started with building your first MCP server and connecting it to an MCP host"
---
In this quickstart, you'll learn the ins and outs of MCP by building your first client and server. We'll start with a basic setup, and then progress to more complex use cases.
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 know the current time and date. Let's use MCP to fix that!
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!
In this quickstart, we'll start by building a Claude CLI chatbot which connects to a simple server that exposes two tools: `get_current_time` and `convert_time`.
<Frame>
<img src="/images/quickstart-weather.png" />
</Frame>
This will allow us to ask Claude questions like:
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.
"What time is it in NYC?"
"When it's 4 PM in New York, what time is it in London?"
<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
- 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
Before we begin, make sure your system meets the following requirements for best results:
- You're on a Mac or Windows computer
- You have the latest Python version installed
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
First, let's create and activate a virtual environment in a new Python project.
<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
# Install virtualenv if you don't have it
pip install virtualenv
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
# Create virtual environment
python -m virtualenv venv
# Initialize a new npm project
npm init -y
# Activate virtual environment
# On Windows:
venv\Scripts\activate
# On Unix or MacOS:
source venv/bin/activate
# Install mcp, anthropic, and python-dotenv
pip install mcp anthropic python-dotenv
# Install dependencies
npm install @modelcontextprotocol/sdk node-fetch
npm install -D @types/node typescript
```
### Set up Anthropic API key
You'll need an Anthropic API key, which you can get from the [Anthropic Console](https://console.anthropic.com/settings/keys).
After you've gotten your key, create a `.env` file to store it securely.
Then create a new file called `src/index.ts` in your project directory:
```bash
# Creates the .env file
touch .env
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"
],
}
```
```bash .env
ANTHROPIC_API_KEY=<your key here>
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
```
```bash .gitignore
# Make sure to add your .env key to your .gitignore to exclude it from source control!
### Setting up the instance
.env
```
### Creating the client (client.py)
Next, let's create a Python file called `client.py` in your `mcp-quickstart` directory.
```bash
touch client.py
```
Let's start by importing our libraries at the top of the file:
```python client.py
import asyncio
from typing import Optional
from contextlib import AsyncExitStack
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from anthropic import Anthropic
from dotenv import load_dotenv
load_dotenv() # load environment variables from .env.
```
Next, we'll create a `class` called `ClaudeCLIChatbot` with a single `__init__` method for now:
```python client.py
# continued from above
class ClaudeCLIChatbot:
def __init__(self):
# This is where we'll keep the MCP client session
self.session: Optional[ClientSession] = None
self.exit_stack = AsyncExitStack()
# Initialize Anthropic client
self.anthropic = Anthropic()
# rest of the class methods will go here
```
Now, lets add a `connect_to_time_server` method. We'll call this method when we first start the main process to actually initialize the connection between our client and our time server.
```python client.py
class ClaudeCLIChatbot:
# continued from above
async def connect_to_time_server(self, server_script_path: str):
"""Connect to the MCP time server"""
server_params = StdioServerParameters(
command="python",
args=[server_script_path],
env=None
)
stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
self.stdio, self.write = stdio_transport
self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))
await self.session.initialize()
response = await self.session.list_tools()
tools = response.tools
print("\nConnected to time server with tools:", [tool.name for tool in tools])
# more methods will go here
```
Let's add a `process_query` method. This is where the following happens:
- It sends the user's query to Claude
- Claude decides which tools to use based on the query
- The client executes the tools and returns the results to Claude
- Claude provides a natural language response
```python client.py
class ClaudeCLIChatbot:
# continued from above
async def process_query(self, query: str) -> str:
"""Use Claude to understand the query and call appropriate tools"""
messages = [
{
"role": "user",
"content": query
}
]
response = await self.session.list_tools()
available_tools = [{
"name": tool.name,
"description": tool.description,
"input_schema": tool.inputSchema
} for tool in response.tools]
# Make the initial call to the Anthropic API
response = self.anthropic.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1000,
messages=messages,
tools=available_tools
)
# Process each content block in the response
tool_results = []
final_text = []
for content in response.content:
if content.type == 'text':
final_text.append(content.text)
elif content.type == 'tool_use':
tool_name = content.name
tool_args = content.input
# Call the tool and get result
result = await self.session.call_tool(tool_name, tool_args)
tool_results.append({"call": tool_name, "result": result})
final_text.append(f"[Calling tool {tool_name} with args {tool_args}]")
# Continue conversation with tool results
if hasattr(content, 'text') and content.text:
messages.append({
"role": "assistant",
"content": content.text
})
messages.append({
"role": "user",
"content": result.content
})
# Get next response from Claude
response = self.anthropic.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1000,
messages=messages,
)
final_text.append(response.content[0].text)
# Return combined output
return "\n".join(final_text)
# more methods will go here
```
Now we'll add the last two methods to our class: `chat_loop` and `cleanup`. These methods are responsible for the continuous running of our chatbot and the cleanup afterwards.
```python client.py
class ClaudeCLIChatbot:
# continued from above
async def chat_loop(self):
"""Main chat loop"""
print("\nWelcome to the Time Assistant!")
print("Ask me anything about times and timezones. For example:")
print("- What time is it now?")
print("- What time is it in Tokyo?")
print("- When it's 4 PM in New York, what time is it in London?")
print("- Convert 9:30 AM Tokyo time to New York time")
print("\nType 'quit' to exit.")
while True:
try:
query = input("\nWhat would you like to know? ").strip()
if query.lower() == 'quit':
break
response = await self.process_query(query)
print("\n" + response)
except Exception as e:
print(f"\nError: {str(e)}")
async def cleanup(self):
"""Cleanup resources"""
await self.exit_stack.aclose()
```
The final step for our `client.py` file is to add and call the `main` function that will actually initialize our connection to the MCP time server (which we'll create in the next step) and begin the chat loop.
```python client.py
class ClaudeCLIChatbot:
# methods from above
async def main():
if len(sys.argv) < 2:
print("Usage: python chatbot.py <path_to_server_script>")
sys.exit(1)
chatbot = ClaudeCLIChatbot()
try:
await chatbot.connect_to_time_server(sys.argv[1])
await chatbot.chat_loop()
finally:
await chatbot.cleanup()
if __name__ == "__main__":
import sys
asyncio.run(main())
```
Putting it all together, the `client.py` file should look like this:
```python client.py
import asyncio
from typing import Optional
from contextlib import AsyncExitStack
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from anthropic import Anthropic
from dotenv import load_dotenv
load_dotenv() # load environment variables from .env.
class ClaudeCLIChatbot:
def __init__(self):
self.session: Optional[ClientSession] = None
self.exit_stack = AsyncExitStack()
# Initialize Anthropic client
self.anthropic = Anthropic()
async def connect_to_time_server(self, server_script_path: str):
"""Connect to the MCP time server"""
server_params = StdioServerParameters(
command="python",
args=[server_script_path],
env=None
)
stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
self.stdio, self.write = stdio_transport
self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))
await self.session.initialize()
response = await self.session.list_tools()
tools = response.tools
print("\nConnected to time server with tools:", [tool.name for tool in tools])
async def process_query(self, query: str) -> str:
"""Use Claude to understand the query and call appropriate tools"""
messages = [
{
"role": "user",
"content": query
}
]
response = await self.session.list_tools()
available_tools = [{
"name": tool.name,
"description": tool.description,
"input_schema": tool.inputSchema
} for tool in response.tools]
# Make the initial call to the Anthropic API
response = self.anthropic.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1000,
messages=messages,
tools=available_tools
)
# Process each content block in the response
tool_results = []
final_text = []
for content in response.content:
if content.type == 'text':
final_text.append(content.text)
elif content.type == 'tool_use':
tool_name = content.name
tool_args = content.input
# Call the tool and get result
result = await self.session.call_tool(tool_name, tool_args)
tool_results.append({"call": tool_name, "result": result})
final_text.append(f"[Calling tool {tool_name} with args {tool_args}]")
# Continue conversation with tool results
if hasattr(content, 'text') and content.text:
messages.append({
"role": "assistant",
"content": content.text
})
messages.append({
"role": "user",
"content": result.content
})
# Get next response from Claude
response = self.anthropic.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1000,
messages=messages,
)
final_text.append(response.content[0].text)
# Return combined output
return "\n".join(final_text)
async def chat_loop(self):
"""Main chat loop"""
print("\nWelcome to the Time Assistant!")
print("Ask me anything about times and timezones. For example:")
print("- What time is it now?")
print("- What time is it in Tokyo?")
print("- When it's 4 PM in New York, what time is it in London?")
print("- Convert 9:30 AM Tokyo time to New York time")
print("\nType 'quit' to exit.")
while True:
try:
query = input("\nWhat would you like to know? ").strip()
if query.lower() == 'quit':
break
response = await self.process_query(query)
print("\n" + response)
except Exception as e:
print(f"\nError: {str(e)}")
async def cleanup(self):
"""Cleanup resources"""
await self.exit_stack.aclose()
async def main():
if len(sys.argv) < 2:
print("Usage: python chatbot.py <path_to_server_script>")
sys.exit(1)
chatbot = ClaudeCLIChatbot()
try:
await chatbot.connect_to_time_server(sys.argv[1])
await chatbot.chat_loop()
finally:
await chatbot.cleanup()
if __name__ == "__main__":
import sys
asyncio.run(main())
```
### Creating the server (server.py)
Next, let's create our server file that will provide time-related tools to our client. The original source code for the MCP time server can be [found here](https://github.com/modelcontextprotocol/servers/tree/main/src/time).
Create a new file called `server.py` in your project directory.
Then add the cache, server instance and the base URL for the NWS API:
```python
touch server.py
# Cache weather data
weather_cache: dict[str, Any] = {}
NWS_API_BASE = "https://api.weather.gov"
server = Server("weather")
```
The server will expose two tools:
- `get_current_time`: Gets the current time in a specified timezone
- `convert_time`: Converts times between different timezones
### Implementing tool listing
Here's the complete server code with explanations:
We need to tell clients what tools are available. The `list_tools()` decorator registers this handler:
```python
from datetime import datetime
from zoneinfo import ZoneInfo
import json
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
@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"],
},
)
]
```
async def run_server():
# Create server instance
server = Server("time-server")
This defines a single tool that requires latitude and longitude parameters.
# Define our tools
@server.list_tools()
async def list_tools() -> list[Tool]:
### 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 [
Tool(
name="get_current_time",
description="Get current time in a specific timezone",
inputSchema={
"type": "object",
"properties": {
"timezone": {
"type": "string",
"description": "IANA timezone name (e.g., 'UTC', 'America/New_York')"
}
},
"required": ["timezone"]
}
),
Tool(
name="convert_time",
description="Convert time between timezones",
inputSchema={
"type": "object",
"properties": {
"source_timezone": {
"type": "string",
"description": "Source timezone"
},
"time": {
"type": "string",
"description": "Time in HH:MM format"
},
"target_timezone": {
"type": "string",
"description": "Target timezone"
}
},
"required": ["source_timezone", "time", "target_timezone"]
}
types.TextContent(
type="text",
text=f"Retrieved weather data for location ({lat}, {lon})",
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
try:
if name == "get_current_time":
timezone = ZoneInfo(arguments["timezone"])
current_time = datetime.now(timezone)
result = {
"time": current_time.isoformat(),
"timezone": str(timezone)
}
elif name == "convert_time":
source_tz = ZoneInfo(arguments["source_timezone"])
target_tz = ZoneInfo(arguments["target_timezone"])
# Parse time
hour, minute = map(int, arguments["time"].split(":"))
source_time = datetime.now(source_tz).replace(
hour=hour,
minute=minute
)
# Convert to target timezone
target_time = source_time.astimezone(target_tz)
result = {
"source_time": source_time.isoformat(),
"target_time": target_time.isoformat(),
"source_timezone": str(source_tz),
"target_timezone": str(target_tz)
}
else:
raise ValueError(f"Unknown tool: {name}")
return [TextContent(
type="text",
text=json.dumps(result, indent=2)
)]
except Exception as e:
raise ValueError(f"Error processing request: {str(e)}")
# Initialize and run the server
options = server.create_initialization_options()
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream, options)
if __name__ == "__main__":
import asyncio
asyncio.run(run_server())
```
### Understanding the server code
### Running the server
The server code consists of three main parts:
Finally, implement the main function to run the server:
1. **Tool Definitions**: The `list_tools()` function defines what tools are available and their input schemas. Each tool has:
- A name
- A description
- An input schema that defines what parameters it accepts
2. **Tool Implementation**: The `call_tool()` function contains the actual logic for each tool:
- `get_current_time`: Returns the current time in a specified timezone
- `convert_time`: Converts a time from one timezone to another
3. **Server Setup**: The main server setup code that initializes the MCP server and starts listening for requests
### Running the application
1. Make sure both files are in your project directory:
```
mcp-quickstart/
├── /venv
├── .env
├── client.py
└── server.py
```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={},
),
),
)
```
2. Run the client, passing the server script as an argument:
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
python client.py server.py
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
```
</Tab>
<Tab title="Windows">
```powershell
code $env:AppData\Claude\claude_desktop_config.json
```
</Tab>
</Tabs>
3. You can now ask questions like:
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"
]
}
}
}
```
What time is it in New York?
What time was it an hour ago in Sydney?
Convert 3:30 PM PST to EST
When it's 9 AM in London, what time is it in Sydney?
</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/time-server-quickstart.png" />
<img src="/images/quickstart-weather.png" />
</Frame>
### What's happening under the hood
<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:
@ -603,12 +431,4 @@ When you ask a question:
5. Claude formulates a natural language response
6. The response is displayed to you!
### Next steps
Now that you have a basic MCP application working, you can:
1. Add more tools to the server
2. Enhance the error handling
3. Add support for more time formats
4. Expand the functionality to handle more complex queries
This example demonstrates the power of MCP in connecting LLMs with external tools and data sources. You can use this pattern to build all sorts of applications that combine Claude's natural language understanding with custom tools and services.
## Next steps

View file

@ -0,0 +1,289 @@
---
title: "Building a MCP client"
description: "Learn how to build your first client in MCP"
---
In this tutorial, you'll learn how to build a client application that connects to MCP servers.
## Prerequisite Knowledge
This tutorial assumes familiarity with:
- Python
- LLMs like Claude
It helps to understand [tool use (aka function calling)](https://docs.anthropic.com/en/docs/build-with-claude/tool-use), but it's not required.
## System Requirements
Before starting, ensure your system meets these requirements:
- Mac or Windows computer
- Latest Python version installed
## Setting Up Your Environment
First, create and activate a virtual environment:
```bash
# Install virtualenv if needed
pip install virtualenv
# Create project directory
mkdir mcp-client
cd mcp-client
# Create virtual environment
python -m virtualenv venv
# Activate virtual environment
# On Windows:
venv\Scripts\activate
# On Unix or MacOS:
source venv/bin/activate
# Install required packages
pip install mcp anthropic python-dotenv
```
## Setting Up Your API Key
You'll need an Anthropic API key from the [Anthropic Console](https://console.anthropic.com/settings/keys).
Create a `.env` file to store it:
```bash
# Create .env file
touch .env
```
Add your key to the `.env` file:
```bash
ANTHROPIC_API_KEY=<your key here>
```
Add `.env` to your `.gitignore`:
```bash
echo ".env" >> .gitignore
```
## Creating the Client
Let's create a Python client that can connect to any MCP server. We'll call it `client.py`:
```python
import asyncio
from typing import Optional
from contextlib import AsyncExitStack
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from anthropic import Anthropic
from dotenv import load_dotenv
load_dotenv() # load environment variables from .env
class MCPClient:
def __init__(self):
# Initialize session and client objects
self.session: Optional[ClientSession] = None
self.exit_stack = AsyncExitStack()
self.anthropic = Anthropic()
async def connect_to_server(self, server_script_path: str):
"""Connect to an MCP server"""
server_params = StdioServerParameters(
command="python",
args=[server_script_path],
env=None
)
stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
self.stdio, self.write = stdio_transport
self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))
await self.session.initialize()
# List available tools
response = await self.session.list_tools()
tools = response.tools
print("\nConnected to server with tools:", [tool.name for tool in tools])
async def process_query(self, query: str) -> str:
"""Process a query using Claude and available tools"""
messages = [
{
"role": "user",
"content": query
}
]
response = await self.session.list_tools()
available_tools = [{
"name": tool.name,
"description": tool.description,
"input_schema": tool.inputSchema
} for tool in response.tools]
# Initial Claude API call
response = self.anthropic.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1000,
messages=messages,
tools=available_tools
)
# Process response and handle tool calls
tool_results = []
final_text = []
for content in response.content:
if content.type == 'text':
final_text.append(content.text)
elif content.type == 'tool_use':
tool_name = content.name
tool_args = content.input
# Execute tool call
result = await self.session.call_tool(tool_name, tool_args)
tool_results.append({"call": tool_name, "result": result})
final_text.append(f"[Calling tool {tool_name} with args {tool_args}]")
# Continue conversation with tool results
if hasattr(content, 'text') and content.text:
messages.append({
"role": "assistant",
"content": content.text
})
messages.append({
"role": "user",
"content": result.content
})
# Get next response from Claude
response = self.anthropic.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1000,
messages=messages,
)
final_text.append(response.content[0].text)
return "\n".join(final_text)
async def chat_loop(self):
"""Run an interactive chat loop"""
print("\nMCP Client Started!")
print("Type your queries or 'quit' to exit.")
while True:
try:
query = input("\nQuery: ").strip()
if query.lower() == 'quit':
break
response = await self.process_query(query)
print("\n" + response)
except Exception as e:
print(f"\nError: {str(e)}")
async def cleanup(self):
"""Clean up resources"""
await self.exit_stack.aclose()
async def main():
if len(sys.argv) < 2:
print("Usage: python client.py <path_to_server_script>")
sys.exit(1)
client = MCPClient()
try:
await client.connect_to_server(sys.argv[1])
await client.chat_loop()
finally:
await client.cleanup()
if __name__ == "__main__":
import sys
asyncio.run(main())
```
## Understanding the Client Code
Let's break down the key components of our MCP client:
1. **MCPClient Class**
- Manages the connection to the MCP server
- Handles communication between Claude and the server's tools
- Maintains the chat session
2. **Key Methods**
- `connect_to_server()`: Establishes connection with an MCP server
- `process_query()`: Handles user queries by:
- Getting available tools from the server
- Sending the query to Claude with tool information
- Executing any tool calls Claude requests
- Processing Claude's responses
- `chat_loop()`: Runs the interactive chat interface
- `cleanup()`: Properly closes connections and resources
## Running the Client
To run your client with any MCP server:
```bash
python client.py path/to/server.py
```
The client will:
1. Connect to the specified server
2. List available tools
3. Start an interactive chat session where you can:
- Enter queries
- See tool executions
- Get responses from Claude
## How It Works
When you submit a query:
1. The client gets the list of available tools from the server
2. Your query is sent to Claude along with tool descriptions
3. Claude decides which tools (if any) to use
4. The client executes any requested tool calls through the server
5. Results are sent back to Claude
6. Claude provides a natural language response
7. The response is displayed to you
## Best Practices
1. **Error Handling**
- Always wrap tool calls in try-catch blocks
- Provide meaningful error messages
- Gracefully handle connection issues
2. **Resource Management**
- Use `AsyncExitStack` for proper cleanup
- Close connections when done
- Handle server disconnections
3. **Security**
- Store API keys securely in `.env`
- Validate server responses
- Be cautious with tool permissions
## Next Steps
Now that you have a basic MCP client, you can:
1. Add support for different server types
2. Enhance error handling and recovery
3. Implement more sophisticated conversation handling
4. Add authentication and security features
5. Create a better user interface
## Resources
- [MCP Documentation](https://modelcontextprotocol.github.io/)
- [Claude API Documentation](https://docs.anthropic.com/claude/reference)
- [Tool Use Guide](https://docs.anthropic.com/claude/docs/function-calling)

View file

@ -1,394 +0,0 @@
---
title: "Set up MCP on Claude Desktop"
description: "How to set up MCP servers on Claude Desktop"
---
MCP is a protocol that enables secure connections between host applications, such as [Claude Desktop](https://claude.ai/download), and local services. In this short tutorial, you'll learn how to:
<Note>
While this tutorial focuses on using Claude Desktop as an example MCP client, the protocol is open and can be integrated by any application. IDEs, AI tools, and other software can all use MCP to connect to local integrations in a standardized way.
</Note>
<Warning>
Claude Desktop's MCP support is currently in developer preview and only supports connecting to local MCP servers running on your machine. Remote MCP connections are not yet supported. This integration is only available in the Claude Desktop app, not the Claude web interface (claude.ai).
</Warning>
### In This Guide
For this tutorial, we'll implement a focused example using SQLite:
```mermaid
flowchart LR
subgraph "Your Computer"
direction LR
Claude["Claude Desktop"]
MCP["SQLite MCP Server"]
DB[(SQLite Database\n~/test.db)]
Claude <-->|"MCP Protocol\n(Queries & Results)"| MCP
MCP <-->|"Local Access\n(SQL Operations)"| DB
end
```
1. Claude Desktop acts as our MCP client
2. A SQLite MCP Server provides secure database access
3. Your local SQLite database stores the actual data
The communication between the SQLite MCP server and your local SQLite database happens entirely on your machine&mdash;your SQLite database is not exposed to the internet. The Model Context Protocol ensures that Claude Desktop can only perform approved database operations through well-defined interfaces. This gives you a secure way to let Claude analyze and interact with your local data while maintaining complete control over what it can access.
## Prerequisites
- macOS or Windows
- The latest version of [Claude Desktop](https://claude.ai/download) installed
- [uv](https://docs.astral.sh/uv/) 0.4.18 or higher (`uv --version` to check)
- Git (`git --version` to check)
- SQLite (`sqlite3 --version` to check)
<AccordionGroup>
<Accordion title="Installing prerequisites (macOS)">
```bash
# Using Homebrew
brew install uv git sqlite3
# Or download directly:
# uv: https://docs.astral.sh/uv/
# Git: https://git-scm.com
# SQLite: https://www.sqlite.org/download.html
```
</Accordion>
<Accordion title="Installing prerequisites (Windows)">
```powershell
# Using winget
winget install --id=astral-sh.uv -e
winget install git.git sqlite.sqlite
# Or download directly:
# uv: https://docs.astral.sh/uv/
# Git: https://git-scm.com
# SQLite: https://www.sqlite.org/download.html
```
</Accordion>
</AccordionGroup>
## Installation
<Tabs>
<Tab title="macOS">
<Steps>
<Step title="Create a sample database">
Let's create a simple SQLite database for testing:
```bash
# Create a new SQLite database
sqlite3 ~/test.db <<EOF
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT,
price REAL
);
INSERT INTO products (name, price) VALUES
('Widget', 19.99),
('Gadget', 29.99),
('Gizmo', 39.99),
('Smart Watch', 199.99),
('Wireless Earbuds', 89.99),
('Portable Charger', 24.99),
('Bluetooth Speaker', 79.99),
('Phone Stand', 15.99),
('Laptop Sleeve', 34.99),
('Mini Drone', 299.99),
('LED Desk Lamp', 45.99),
('Keyboard', 129.99),
('Mouse Pad', 12.99),
('USB Hub', 49.99),
('Webcam', 69.99),
('Screen Protector', 9.99),
('Travel Adapter', 27.99),
('Gaming Headset', 159.99),
('Fitness Tracker', 119.99),
('Portable SSD', 179.99);
EOF
```
</Step>
<Step title="Configure Claude Desktop">
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:
```bash
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
```
Add this configuration (replace YOUR_USERNAME with your actual username):
```json
{
"mcpServers": {
"sqlite": {
"command": "uvx",
"args": ["mcp-server-sqlite", "--db-path", "/Users/YOUR_USERNAME/test.db"]
}
}
}
```
This tells Claude Desktop:
1. There's an MCP server named "sqlite"
2. Launch it by running `uvx mcp-server-sqlite`
3. Connect it to your test database
Save the file, and restart **Claude Desktop**.
</Step>
</Steps>
</Tab>
<Tab title="Windows">
<Steps>
<Step title="Create a sample database">
Let's create a simple SQLite database for testing:
```powershell
# Create a new SQLite database
$sql = @'
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT,
price REAL
);
INSERT INTO products (name, price) VALUES
('Widget', 19.99),
('Gadget', 29.99),
('Gizmo', 39.99),
('Smart Watch', 199.99),
('Wireless Earbuds', 89.99),
('Portable Charger', 24.99),
('Bluetooth Speaker', 79.99),
('Phone Stand', 15.99),
('Laptop Sleeve', 34.99),
('Mini Drone', 299.99),
('LED Desk Lamp', 45.99),
('Keyboard', 129.99),
('Mouse Pad', 12.99),
('USB Hub', 49.99),
('Webcam', 69.99),
('Screen Protector', 9.99),
('Travel Adapter', 27.99),
('Gaming Headset', 159.99),
('Fitness Tracker', 119.99),
('Portable SSD', 179.99);
'@
cd ~
& sqlite3 test.db $sql
```
</Step>
<Step title="Configure Claude Desktop">
Open your Claude Desktop App configuration at `%APPDATA%\Claude\claude_desktop_config.json` in a text editor.
For example, if you have [VS Code](https://code.visualstudio.com/) installed:
```powershell
code $env:AppData\Claude\claude_desktop_config.json
```
Add this configuration (replace YOUR_USERNAME with your actual username):
```json
{
"mcpServers": {
"sqlite": {
"command": "uvx",
"args": [
"mcp-server-sqlite",
"--db-path",
"C:\\Users\\YOUR_USERNAME\\test.db"
]
}
}
}
```
This tells Claude Desktop:
1. There's an MCP server named "sqlite"
2. Launch it by running `uvx mcp-server-sqlite`
3. Connect it to your test database
Save the file, and restart **Claude Desktop**.
</Step>
</Steps>
</Tab>
</Tabs>
## Test it out
Let's verify everything is working. Try sending this prompt to Claude Desktop:
```
Can you connect to my SQLite database and tell me what products are available, and their prices?
```
Claude Desktop will:
1. Connect to the SQLite MCP server
2. Query your local database
3. Format and present the results
<Frame caption="Claude Desktop successfully queries our SQLite database 🎉">
<img src="/images/quickstart-screenshot.png" alt="Example Claude Desktop conversation showing database query results" />
</Frame>
## What's happening under the hood?
When you interact with Claude Desktop using MCP:
1. **Server Discovery**: Claude Desktop connects to your configured MCP servers on startup
2. **Protocol Handshake**: When you ask about data, Claude Desktop:
- Identifies which MCP server can help (sqlite in this case)
- Negotiates capabilities through the protocol
- Requests data or actions from the MCP server
3. **Interaction Flow**:
```mermaid
sequenceDiagram
participant C as Claude Desktop
participant M as MCP Server
participant D as SQLite DB
C->>M: Initialize connection
M-->>C: Available capabilities
C->>M: Query request
M->>D: SQL query
D-->>M: Results
M-->>C: Formatted results
```
4. **Security**:
- MCP servers only expose specific, controlled capabilities
- MCP servers run locally on your machine, and the resources they access are not exposed to the internet
- Claude Desktop requires user confirmation for sensitive operations
## Try these examples
Now that MCP is working, try these increasingly powerful examples:
<AccordionGroup>
<Accordion title="Basic Queries" active>
```
What's the average price of all products in the database?
```
</Accordion>
<Accordion title="Data Analysis">
```
Can you analyze the price distribution and suggest any pricing optimizations?
```
</Accordion>
<Accordion title="Complex Operations">
```
Could you help me design and create a new table for storing customer orders?
```
</Accordion>
</AccordionGroup>
## Add more capabilities
Want to give Claude Desktop more local integration capabilities? Add these servers to your configuration:
<Note>
Note that these MCP servers will require [Node.js](https://nodejs.org/en) to be installed on your machine.
</Note>
<AccordionGroup>
<Accordion title="File System Access" icon="folder-open">
Add this to your config to let Claude Desktop read and analyze files:
```json
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/YOUR_USERNAME/Desktop"]
}
```
</Accordion>
<Accordion title="PostgreSQL Connection" icon="database">
Connect Claude Desktop to your PostgreSQL database:
```json
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "postgresql://localhost/mydb"]
}
```
</Accordion>
</AccordionGroup>
## More MCP Clients
While this guide demonstrates MCP using Claude Desktop as a client, several other applications support MCP integration:
<CardGroup cols={2}>
<Card
title="Zed Editor"
icon="pen-to-square"
href="https://zed.dev">
A high-performance, multiplayer code editor with built-in MCP support for AI-powered coding assistance
</Card>
<Card
title="Cody"
icon="magnifying-glass"
href="https://sourcegraph.com/cody">
Code intelligence platform featuring MCP integration for enhanced code search and analysis capabilities
</Card>
</CardGroup>
Each host application may implement MCP features differently or support different capabilities. Check out our [clients](/clients) documentation for more information.
## Troubleshooting
<AccordionGroup>
<Accordion title="Nothing showing up in Claude Desktop?">
1. Check if MCP is enabled:
- Click the 🔌 icon in Claude Desktop, next to the chat box
- Expand "Installed MCP Servers"
- You should see your configured servers
2. Verify your config:
- From Claude Desktop, go to Claude > Settings…
- Open the "Developer" tab to see your configuration
3. Restart Claude Desktop completely:
- Quit the app (not just close the window)
- Start it again
</Accordion>
<Accordion title="MCP or database errors?">
1. Check Claude Desktop's logs:
```bash
tail -n 20 -f ~/Library/Logs/Claude/mcp*.log
```
2. Verify database access:
```bash
# Test database connection
sqlite3 ~/test.db ".tables"
```
3. Common fixes:
- Check file paths in your config
- Verify database file permissions
- Ensure SQLite is installed properly
</Accordion>
</AccordionGroup>
## Next steps
<CardGroup cols={2}>
<Card title="Build your first MCP client and server" icon="code" href="/quickstart">
Build your first MCP client and server from scratch in less than 5 minutes.
</Card>
<Card title="Explore examples" icon="github" href="https://github.com/modelcontextprotocol/servers">
Browse our collection of example servers to see what's possible.
</Card>
</CardGroup>