finished draft of quickstarts
This commit is contained in:
parent
2d987afeed
commit
e4dd2199fe
24 changed files with 2657 additions and 711 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
|
|
@ -1,5 +0,0 @@
|
||||||
---
|
|
||||||
title: "Configuration"
|
|
||||||
description: "Configure MCP servers for production"
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
---
|
|
||||||
title: "Environment Setup"
|
|
||||||
description: "Set up your production environment"
|
|
||||||
---
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
title: "Monitoring"
|
|
||||||
description: "Monitor your MCP servers in production"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Monitoring
|
|
||||||
|
|
||||||
Monitor your MCP servers in production
|
|
||||||
235
docs/guides/claude.mdx
Normal file
235
docs/guides/claude.mdx
Normal file
|
|
@ -0,0 +1,235 @@
|
||||||
|
---
|
||||||
|
title: "Connecting MCP to Claude"
|
||||||
|
description: "Get started with MCP in less than 5 minutes"
|
||||||
|
---
|
||||||
|
|
||||||
|
Give Claude superpowers by connecting it to your local computer! This guide will show you how to set up Model Context Protocol (MCP) and experience its magic in just 5 minutes.
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
MCP connects Claude to external data sources and tools. In this guide, we'll give Claude the ability to:
|
||||||
|
- Take screenshots and analyze them
|
||||||
|
- Control window arrangement
|
||||||
|
- Execute local automation scripts
|
||||||
|
|
||||||
|
And it does this all securely - Claude can only access what you explicitly allow through MCP servers.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- macOS (Windows/Linux guides coming soon)
|
||||||
|
- Node.js v16 or higher (`node --version` to check)
|
||||||
|
- Git (`git --version` to check)
|
||||||
|
- [Claude desktop app](https://claude.ai/download) installed
|
||||||
|
|
||||||
|
<Accordion title="Need to install the prerequisites?">
|
||||||
|
```bash
|
||||||
|
# Using Homebrew
|
||||||
|
brew install node git
|
||||||
|
|
||||||
|
# Or download directly:
|
||||||
|
# Node.js: https://nodejs.org
|
||||||
|
# Git: https://git-scm.com
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Install example servers">
|
||||||
|
Let's start by getting some pre-built MCP servers that Claude can use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone the example servers
|
||||||
|
git clone https://github.com/modelcontextprotocol/example-servers
|
||||||
|
cd example-servers
|
||||||
|
|
||||||
|
# Install dependencies and build
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Make servers available globally
|
||||||
|
npm install -g
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Configure Claude">
|
||||||
|
Open your Claude Desktop App configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Add this configuration (replace YOUR_USERNAME with your actual username):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"osascript": {
|
||||||
|
"command": "mcp-server-osascript",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Save and **restart Claude Desktop App**.
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Test it out
|
||||||
|
|
||||||
|
Let's verify everything is working with a simple example. Try sending this prompt to Claude:
|
||||||
|
|
||||||
|
```
|
||||||
|
Can you take a screenshot of my desktop and describe what you see?
|
||||||
|
```
|
||||||
|
|
||||||
|
Claude will:
|
||||||
|
|
||||||
|
1. Request permission to take a screenshot
|
||||||
|
2. Capture your screen through the MCP server
|
||||||
|
3. Analyze and describe the screenshot
|
||||||
|
|
||||||
|
<Frame>
|
||||||
|
<img src="/api/placeholder/800/400" alt="Example Claude conversation showing a screenshot analysis" />
|
||||||
|
</Frame>
|
||||||
|
<Note>
|
||||||
|
The first time you use screenshot features, macOS will ask for permissions:
|
||||||
|
- Screen Recording (for screenshots)
|
||||||
|
- Automation (for window control)
|
||||||
|
You'll need to grant these to Claude.app in System Settings.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## Try these examples
|
||||||
|
|
||||||
|
Now that MCP is working, try these increasingly powerful examples:
|
||||||
|
|
||||||
|
<AccordionGroup>
|
||||||
|
<Accordion title="Basic Window Analysis" active>
|
||||||
|
```
|
||||||
|
Could you analyze my open windows and tell me:
|
||||||
|
- How many windows I have open
|
||||||
|
- Which apps are running
|
||||||
|
- If any windows look cluttered
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
<Accordion title="Window Organization">
|
||||||
|
```
|
||||||
|
Could you help organize my windows? I'd like:
|
||||||
|
- Browser on the left half
|
||||||
|
- VSCode on the right half
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
<Accordion title="Create a Window Layout Script">
|
||||||
|
```
|
||||||
|
I often need this window layout for coding:
|
||||||
|
- VSCode taking up the left 60%
|
||||||
|
- Terminal in the top right
|
||||||
|
- Browser in the bottom right
|
||||||
|
Could you create an AppleScript to automate this?
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
</AccordionGroup>
|
||||||
|
|
||||||
|
## What just happened?
|
||||||
|
|
||||||
|
Let's break down the magic:
|
||||||
|
|
||||||
|
1. When you installed the `osascript` server, you gave Claude a way to:
|
||||||
|
- Take screenshots (see what you see)
|
||||||
|
- Run AppleScript (control your windows)
|
||||||
|
- Automate macOS tasks
|
||||||
|
|
||||||
|
2. The MCP protocol lets Claude:
|
||||||
|
- Request these actions securely
|
||||||
|
- Get results back in real-time
|
||||||
|
- Chain actions together intelligently
|
||||||
|
|
||||||
|
3. All of this happens locally on your machine - no data leaves your computer!
|
||||||
|
|
||||||
|
## Add more capabilities
|
||||||
|
|
||||||
|
Want to give Claude even more superpowers? Add these servers to your configuration:
|
||||||
|
<AccordionGroup>
|
||||||
|
<Accordion title="File System Access" icon="folder-open">
|
||||||
|
Add this to your config to let Claude read and analyze files:
|
||||||
|
```json
|
||||||
|
"tee": {
|
||||||
|
"command": "mcp-server-tee",
|
||||||
|
"args": ["tail", "-f", "/var/log/system.log"],
|
||||||
|
"env": {
|
||||||
|
"HOME": "/Users/YOUR_USERNAME"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="Database Connection" icon="database">
|
||||||
|
Connect Claude to your PostgreSQL database:
|
||||||
|
```json
|
||||||
|
"postgres": {
|
||||||
|
"command": "mcp-server-postgres",
|
||||||
|
"args": ["postgresql://localhost/mydb"],
|
||||||
|
"env": {
|
||||||
|
"HOME": "/Users/YOUR_USERNAME"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="Web Automation" icon="globe">
|
||||||
|
Enable web automation capabilities:
|
||||||
|
```json
|
||||||
|
"puppeteer": {
|
||||||
|
"command": "mcp-server-puppeteer",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
</AccordionGroup>
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
<AccordionGroup>
|
||||||
|
<Accordion title="Nothing showing up in Claude?">
|
||||||
|
1. Check if MCP is enabled:
|
||||||
|
- Click the 🔌 icon in Claude
|
||||||
|
- You should see your configured servers
|
||||||
|
|
||||||
|
2. Verify your config:
|
||||||
|
```bash
|
||||||
|
cat ~/Library/Application\ Support/Claude/claude_desktop_config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Restart Claude completely:
|
||||||
|
- Quit the app (not just close the window)
|
||||||
|
- Start it again
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="Permission errors?">
|
||||||
|
Run this command to see Claude's logs:
|
||||||
|
```bash
|
||||||
|
tail -f ~/Claude.log
|
||||||
|
```
|
||||||
|
|
||||||
|
Common fixes:
|
||||||
|
- Check `HOME` env variable in your config
|
||||||
|
- Grant missing permissions in System Settings
|
||||||
|
- Try running example commands manually in Terminal
|
||||||
|
</Accordion>
|
||||||
|
</AccordionGroup>
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
<CardGroup cols={3}>
|
||||||
|
<Card title="Build Custom Servers" icon="code" href="/docs/guides/first-server-python">
|
||||||
|
Create your own MCP integrations to give Claude new capabilities.
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card title="Explore Examples" icon="microscope" href="https://github.com/modelcontextprotocol/example-servers">
|
||||||
|
Browse our collection of example servers to see what's possible.
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card title="Join Community" icon="users">
|
||||||
|
Share your MCP creations and connect with other developers.
|
||||||
|
[Join us →](/community)
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
title: "Your First MCP Server"
|
|
||||||
description: "Build a complete MCP server from scratch"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Your First MCP Server
|
|
||||||
|
|
||||||
Build a complete MCP server from scratch
|
|
||||||
422
docs/guides/first-server-python.mdx
Normal file
422
docs/guides/first-server-python.mdx
Normal file
|
|
@ -0,0 +1,422 @@
|
||||||
|
---
|
||||||
|
title: "Your First MCP Server (Python)"
|
||||||
|
description: "Create a simple MCP server in Python in 15 minutes"
|
||||||
|
---
|
||||||
|
|
||||||
|
Let's build your first MCP server in Python! We'll create a weather server that provides current weather data as a resource and lets Claude fetch forecasts using tools.
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
This guide uses the OpenWeatherMap API. You'll need a free API key from [OpenWeatherMap](https://openweathermap.org/api) to follow along.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Install Python">
|
||||||
|
You'll need Python 3.8 or higher:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 --version # Should be 3.8 or higher
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Set up a virtual environment">
|
||||||
|
```bash
|
||||||
|
# Create and activate virtual environment
|
||||||
|
python3 -m venv mcp-weather
|
||||||
|
cd mcp-weather
|
||||||
|
source bin/activate # On Windows: .\Scripts\activate
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Install MCP SDK">
|
||||||
|
```bash
|
||||||
|
pip install modelcontextprotocol-sdk requests
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Get API Key">
|
||||||
|
1. Sign up at [OpenWeatherMap](https://openweathermap.org/api)
|
||||||
|
2. Copy your API key
|
||||||
|
3. Set it as an environment variable:
|
||||||
|
```bash
|
||||||
|
export OPENWEATHER_API_KEY='your-api-key'
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Create Your Server
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Create project structure">
|
||||||
|
```bash
|
||||||
|
mkdir weather_server
|
||||||
|
cd weather_server
|
||||||
|
touch weather_server.py
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Add the base code">
|
||||||
|
Open `weather_server.py` and add this starter code:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from modelcontextprotocol.server import Server
|
||||||
|
from modelcontextprotocol.server.stdio import StdioServerTransport
|
||||||
|
from modelcontextprotocol.types import (
|
||||||
|
ListResourcesRequestSchema,
|
||||||
|
ListToolsRequestSchema,
|
||||||
|
ReadResourceRequestSchema,
|
||||||
|
CallToolRequestSchema
|
||||||
|
)
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
API_KEY = os.getenv('OPENWEATHER_API_KEY')
|
||||||
|
BASE_URL = 'http://api.openweathermap.org/data/2.5'
|
||||||
|
|
||||||
|
class WeatherServer:
|
||||||
|
def __init__(self):
|
||||||
|
self.server = Server(
|
||||||
|
name="example-weather-server",
|
||||||
|
version="0.1.0"
|
||||||
|
)
|
||||||
|
self.default_city = "San Francisco"
|
||||||
|
self.setup_handlers()
|
||||||
|
|
||||||
|
def setup_handlers(self):
|
||||||
|
# We'll add handlers here
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
transport = StdioServerTransport()
|
||||||
|
await self.server.connect(transport)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import asyncio
|
||||||
|
server = WeatherServer()
|
||||||
|
asyncio.run(server.run())
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Add resource handlers">
|
||||||
|
Let's add handlers to expose current weather as a resource:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def setup_handlers(self):
|
||||||
|
self.server.set_request_handler(
|
||||||
|
ListResourcesRequestSchema,
|
||||||
|
self.handle_list_resources
|
||||||
|
)
|
||||||
|
self.server.set_request_handler(
|
||||||
|
ReadResourceRequestSchema,
|
||||||
|
self.handle_read_resource
|
||||||
|
)
|
||||||
|
|
||||||
|
async def handle_list_resources(self, request):
|
||||||
|
return {
|
||||||
|
"resources": [{
|
||||||
|
"uri": f"weather://{self.default_city}/current",
|
||||||
|
"name": f"Current weather in {self.default_city}",
|
||||||
|
"mimeType": "application/json"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
async def handle_read_resource(self, request):
|
||||||
|
city = self.default_city
|
||||||
|
if request.params.uri != f"weather://{city}/current":
|
||||||
|
raise ValueError(f"Unknown resource: {request.params.uri}")
|
||||||
|
|
||||||
|
response = requests.get(
|
||||||
|
f"{BASE_URL}/weather",
|
||||||
|
params={
|
||||||
|
"q": city,
|
||||||
|
"appid": API_KEY,
|
||||||
|
"units": "metric"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"contents": [{
|
||||||
|
"uri": request.params.uri,
|
||||||
|
"mimeType": "application/json",
|
||||||
|
"text": json.dumps({
|
||||||
|
"temperature": data["main"]["temp"],
|
||||||
|
"conditions": data["weather"][0]["description"],
|
||||||
|
"humidity": data["main"]["humidity"],
|
||||||
|
"wind_speed": data["wind"]["speed"],
|
||||||
|
"timestamp": datetime.now().isoformat()
|
||||||
|
}, indent=2)
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Add tool handlers">
|
||||||
|
Now let's add a tool to fetch weather forecasts:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def setup_handlers(self):
|
||||||
|
# Add these lines to the existing method
|
||||||
|
self.server.set_request_handler(
|
||||||
|
ListToolsRequestSchema,
|
||||||
|
self.handle_list_tools
|
||||||
|
)
|
||||||
|
self.server.set_request_handler(
|
||||||
|
CallToolRequestSchema,
|
||||||
|
self.handle_call_tool
|
||||||
|
)
|
||||||
|
|
||||||
|
async def handle_list_tools(self, request):
|
||||||
|
return {
|
||||||
|
"tools": [{
|
||||||
|
"name": "get_forecast",
|
||||||
|
"description": "Get weather forecast for a city",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"city": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "City name"
|
||||||
|
},
|
||||||
|
"days": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Number of days (1-5)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["city"]
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
async def handle_call_tool(self, request):
|
||||||
|
if request.params.name != "get_forecast":
|
||||||
|
raise ValueError(f"Unknown tool: {request.params.name}")
|
||||||
|
|
||||||
|
args = request.params.arguments
|
||||||
|
city = args.get("city", self.default_city)
|
||||||
|
days = min(int(args.get("days", 3)), 5)
|
||||||
|
|
||||||
|
response = requests.get(
|
||||||
|
f"{BASE_URL}/forecast",
|
||||||
|
params={
|
||||||
|
"q": city,
|
||||||
|
"appid": API_KEY,
|
||||||
|
"units": "metric",
|
||||||
|
"cnt": days * 8 # API returns 3-hour intervals
|
||||||
|
}
|
||||||
|
)
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
# Process and return daily forecasts
|
||||||
|
forecasts = []
|
||||||
|
for i in range(0, len(data["list"]), 8): # One day = 8 intervals
|
||||||
|
day_data = data["list"][i]
|
||||||
|
forecasts.append({
|
||||||
|
"date": day_data["dt_txt"].split()[0],
|
||||||
|
"temperature": day_data["main"]["temp"],
|
||||||
|
"conditions": day_data["weather"][0]["description"]
|
||||||
|
})
|
||||||
|
|
||||||
|
return {"toolResult": forecasts}
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Test your server">
|
||||||
|
1. Save all changes
|
||||||
|
2. Run your server:
|
||||||
|
```bash
|
||||||
|
python weather_server.py
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Connect to Claude
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Install the server">
|
||||||
|
```bash
|
||||||
|
# From your project directory
|
||||||
|
pip install -e .
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Update Claude config">
|
||||||
|
Add this to your `claude_desktop_config.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"weather": {
|
||||||
|
"command": "python",
|
||||||
|
"args": ["-m", "weather_server"],
|
||||||
|
"env": {
|
||||||
|
"OPENWEATHER_API_KEY": "your-api-key",
|
||||||
|
"HOME": "/Users/YOUR_USERNAME"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Restart Claude">
|
||||||
|
1. Quit Claude completely
|
||||||
|
2. Start Claude again
|
||||||
|
3. Verify your weather server appears in the 🔌 menu
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Try It Out!
|
||||||
|
|
||||||
|
<AccordionGroup>
|
||||||
|
<Accordion title="Check Current Weather" active>
|
||||||
|
Ask Claude:
|
||||||
|
```
|
||||||
|
What's the current weather in San Francisco? Can you analyze the conditions?
|
||||||
|
```
|
||||||
|
|
||||||
|
Claude will:
|
||||||
|
1. Access the weather resource
|
||||||
|
2. Parse the JSON data
|
||||||
|
3. Provide a natural language summary
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="Get a Forecast">
|
||||||
|
Ask Claude:
|
||||||
|
```
|
||||||
|
Can you get me a 5-day forecast for Tokyo and tell me if I should pack an umbrella?
|
||||||
|
```
|
||||||
|
|
||||||
|
Claude will:
|
||||||
|
1. Use the `get_forecast` tool
|
||||||
|
2. Analyze the forecast data
|
||||||
|
3. Give you packing advice
|
||||||
|
</Accordion>
|
||||||
|
</AccordionGroup>
|
||||||
|
|
||||||
|
## Understanding the Code
|
||||||
|
|
||||||
|
Let's break down the key MCP concepts used in this server:
|
||||||
|
|
||||||
|
<Tabs>
|
||||||
|
<Tab title="Resources">
|
||||||
|
```python
|
||||||
|
async def handle_list_resources(self, request):
|
||||||
|
return {
|
||||||
|
"resources": [{
|
||||||
|
"uri": f"weather://{self.default_city}/current",
|
||||||
|
"name": f"Current weather in {self.default_city}",
|
||||||
|
"mimeType": "application/json"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Resources provide data that Claude can access as context. In this case, we're exposing current weather data that Claude can read and analyze.
|
||||||
|
</Tab>
|
||||||
|
|
||||||
|
<Tab title="Tools">
|
||||||
|
```python
|
||||||
|
async def handle_list_tools(self, request):
|
||||||
|
return {
|
||||||
|
"tools": [{
|
||||||
|
"name": "get_forecast",
|
||||||
|
"description": "Get weather forecast for a city",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"city": {"type": "string"},
|
||||||
|
"days": {"type": "number"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Tools let Claude take actions through your server. The `inputSchema` defines what parameters Claude can provide.
|
||||||
|
</Tab>
|
||||||
|
|
||||||
|
<Tab title="Transport">
|
||||||
|
```python
|
||||||
|
async def run(self):
|
||||||
|
transport = StdioServerTransport()
|
||||||
|
await self.server.connect(transport)
|
||||||
|
```
|
||||||
|
The transport layer handles communication between Claude and your server. Here we're using stdio, but MCP also supports WebSocket and SSE.
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
Now that you've built your first MCP server, try these enhancements:
|
||||||
|
|
||||||
|
<CardGroup cols={2}>
|
||||||
|
<Card
|
||||||
|
title="Add More Resources"
|
||||||
|
icon="database"
|
||||||
|
>
|
||||||
|
- Air quality data
|
||||||
|
- Weather alerts
|
||||||
|
- Historical weather
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
title="Enhance Tools"
|
||||||
|
icon="wrench"
|
||||||
|
>
|
||||||
|
- Compare multiple cities
|
||||||
|
- Get detailed hourly forecasts
|
||||||
|
- Save favorite locations
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
title="Improve Error Handling"
|
||||||
|
icon="shield"
|
||||||
|
>
|
||||||
|
- Handle API errors gracefully
|
||||||
|
- Add input validation
|
||||||
|
- Rate limiting
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
title="Add Subscriptions"
|
||||||
|
icon="bell"
|
||||||
|
>
|
||||||
|
- Weather alerts
|
||||||
|
- Temperature changes
|
||||||
|
- Condition updates
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
<AccordionGroup>
|
||||||
|
<Accordion title="Import Errors">
|
||||||
|
```bash
|
||||||
|
pip install modelcontextprotocol-sdk --upgrade
|
||||||
|
```
|
||||||
|
Ensure you're using the latest SDK version.
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="API Key Issues">
|
||||||
|
1. Verify your API key is set:
|
||||||
|
```bash
|
||||||
|
echo $OPENWEATHER_API_KEY
|
||||||
|
```
|
||||||
|
2. Check the API response for error messages
|
||||||
|
3. Ensure you're within free tier limits
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="Connection Issues">
|
||||||
|
1. Check Claude's logs:
|
||||||
|
```bash
|
||||||
|
tail -f ~/Claude.log
|
||||||
|
```
|
||||||
|
2. Verify your config JSON syntax
|
||||||
|
3. Ensure Python path is correct in config
|
||||||
|
</Accordion>
|
||||||
|
</AccordionGroup>
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
Remember: This is just the beginning! MCP servers can provide any type of data or functionality to Claude. Think about what unique capabilities you could add to Claude through MCP.
|
||||||
|
</Note>
|
||||||
533
docs/guides/first-server-ts.mdx
Normal file
533
docs/guides/first-server-ts.mdx
Normal file
|
|
@ -0,0 +1,533 @@
|
||||||
|
---
|
||||||
|
title: "Your First MCP Server (TypeScript)"
|
||||||
|
description: "Create a simple MCP server in TypeScript in 15 minutes"
|
||||||
|
---
|
||||||
|
|
||||||
|
Let's build your first MCP server in TypeScript! We'll create a weather server that provides current weather data as a resource and lets Claude fetch forecasts using tools.
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
This guide uses the OpenWeatherMap API. You'll need a free API key from [OpenWeatherMap](https://openweathermap.org/api) to follow along.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Install Node.js">
|
||||||
|
You'll need Node.js 16 or higher:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node --version # Should be v16 or higher
|
||||||
|
npm --version
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Create a new project">
|
||||||
|
```bash
|
||||||
|
mkdir weather-server
|
||||||
|
cd weather-server
|
||||||
|
npm init -y
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Install dependencies">
|
||||||
|
```bash
|
||||||
|
npm install @modelcontextprotocol/sdk axios dotenv typescript @types/node
|
||||||
|
npm install --save-dev ts-node
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Configure TypeScript">
|
||||||
|
Create `tsconfig.json`:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "ES2020",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Set up environment">
|
||||||
|
Create `.env`:
|
||||||
|
```bash
|
||||||
|
OPENWEATHER_API_KEY=your-api-key-here
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Create Your Server
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Create project structure">
|
||||||
|
```bash
|
||||||
|
mkdir src
|
||||||
|
touch src/index.ts
|
||||||
|
touch src/types.ts
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Define types">
|
||||||
|
In `src/types.ts`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export interface WeatherData {
|
||||||
|
temperature: number;
|
||||||
|
conditions: string;
|
||||||
|
humidity: number;
|
||||||
|
wind_speed: number;
|
||||||
|
timestamp: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ForecastDay {
|
||||||
|
date: string;
|
||||||
|
temperature: number;
|
||||||
|
conditions: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GetForecastArgs {
|
||||||
|
city: string;
|
||||||
|
days?: number;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Add the base code">
|
||||||
|
In `src/index.ts`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
||||||
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||||
|
import axios from "axios";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
import { WeatherData, ForecastDay, GetForecastArgs } from "./types.js";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const API_KEY = process.env.OPENWEATHER_API_KEY;
|
||||||
|
const BASE_URL = 'http://api.openweathermap.org/data/2.5';
|
||||||
|
const DEFAULT_CITY = 'San Francisco';
|
||||||
|
|
||||||
|
class WeatherServer {
|
||||||
|
private server: Server;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.server = new Server({
|
||||||
|
name: "example-weather-server",
|
||||||
|
version: "0.1.0"
|
||||||
|
});
|
||||||
|
this.setupHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupHandlers(): void {
|
||||||
|
// We'll add handlers here
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(): Promise<void> {
|
||||||
|
const transport = new StdioServerTransport();
|
||||||
|
await this.server.connect(transport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = new WeatherServer();
|
||||||
|
server.run().catch(console.error);
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Add resource handlers">
|
||||||
|
Add this to the `setupHandlers` method:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
private setupHandlers(): void {
|
||||||
|
this.server.setRequestHandler(
|
||||||
|
ListResourcesRequestSchema,
|
||||||
|
async () => ({
|
||||||
|
resources: [{
|
||||||
|
uri: `weather://${DEFAULT_CITY}/current`,
|
||||||
|
name: `Current weather in ${DEFAULT_CITY}`,
|
||||||
|
mimeType: "application/json"
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
this.server.setRequestHandler(
|
||||||
|
ReadResourceRequestSchema,
|
||||||
|
async (request) => {
|
||||||
|
const city = DEFAULT_CITY;
|
||||||
|
if (request.params.uri !== `weather://${city}/current`) {
|
||||||
|
throw new Error(`Unknown resource: ${request.params.uri}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await axios.get(`${BASE_URL}/weather`, {
|
||||||
|
params: {
|
||||||
|
q: city,
|
||||||
|
appid: API_KEY,
|
||||||
|
units: "metric"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const weatherData: WeatherData = {
|
||||||
|
temperature: response.data.main.temp,
|
||||||
|
conditions: response.data.weather[0].description,
|
||||||
|
humidity: response.data.main.humidity,
|
||||||
|
wind_speed: response.data.wind.speed,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
contents: [{
|
||||||
|
uri: request.params.uri,
|
||||||
|
mimeType: "application/json",
|
||||||
|
text: JSON.stringify(weatherData, null, 2)
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Add tool handlers">
|
||||||
|
Add these handlers to the `setupHandlers` method:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
this.server.setRequestHandler(
|
||||||
|
ListToolsRequestSchema,
|
||||||
|
async () => ({
|
||||||
|
tools: [{
|
||||||
|
name: "get_forecast",
|
||||||
|
description: "Get weather forecast for a city",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
city: {
|
||||||
|
type: "string",
|
||||||
|
description: "City name"
|
||||||
|
},
|
||||||
|
days: {
|
||||||
|
type: "number",
|
||||||
|
description: "Number of days (1-5)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ["city"]
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
this.server.setRequestHandler(
|
||||||
|
CallToolRequestSchema,
|
||||||
|
async (request) => {
|
||||||
|
if (request.params.name !== "get_forecast") {
|
||||||
|
throw new Error(`Unknown tool: ${request.params.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = request.params.arguments as GetForecastArgs;
|
||||||
|
const city = args.city || DEFAULT_CITY;
|
||||||
|
const days = Math.min(args.days || 3, 5);
|
||||||
|
|
||||||
|
const response = await axios.get(`${BASE_URL}/forecast`, {
|
||||||
|
params: {
|
||||||
|
q: city,
|
||||||
|
appid: API_KEY,
|
||||||
|
units: "metric",
|
||||||
|
cnt: days * 8 // API returns 3-hour intervals
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const forecasts: ForecastDay[] = [];
|
||||||
|
for (let i = 0; i < response.data.list.length; i += 8) {
|
||||||
|
const dayData = response.data.list[i];
|
||||||
|
forecasts.push({
|
||||||
|
date: dayData.dt_txt.split(' ')[0],
|
||||||
|
temperature: dayData.main.temp,
|
||||||
|
conditions: dayData.weather[0].description
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { toolResult: forecasts };
|
||||||
|
}
|
||||||
|
);
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Build and test">
|
||||||
|
Add these scripts to `package.json`:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"start": "node dist/index.js",
|
||||||
|
"dev": "ts-node src/index.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run:
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Connect to Claude
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Install globally">
|
||||||
|
```bash
|
||||||
|
npm install -g
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Update Claude config">
|
||||||
|
Add to `claude_desktop_config.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"weather": {
|
||||||
|
"command": "weather-server",
|
||||||
|
"env": {
|
||||||
|
"OPENWEATHER_API_KEY": "your-api-key",
|
||||||
|
"HOME": "/Users/YOUR_USERNAME"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Restart Claude">
|
||||||
|
1. Quit Claude completely
|
||||||
|
2. Start Claude again
|
||||||
|
3. Look for your weather server in the 🔌 menu
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Try It Out!
|
||||||
|
|
||||||
|
<AccordionGroup>
|
||||||
|
<Accordion title="Check Current Weather" active>
|
||||||
|
Ask Claude:
|
||||||
|
```
|
||||||
|
What's the current weather in San Francisco? Can you analyze the conditions?
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="Get a Forecast">
|
||||||
|
Ask Claude:
|
||||||
|
```
|
||||||
|
Can you get me a 5-day forecast for Tokyo and tell me if I should pack an umbrella?
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="Compare Weather">
|
||||||
|
Ask Claude:
|
||||||
|
```
|
||||||
|
Can you analyze the forecast for both Tokyo and San Francisco and tell me which city will be warmer this week?
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
</AccordionGroup>
|
||||||
|
|
||||||
|
## Understanding the Code
|
||||||
|
|
||||||
|
<Tabs>
|
||||||
|
<Tab title="Type Safety">
|
||||||
|
```typescript
|
||||||
|
interface WeatherData {
|
||||||
|
temperature: number;
|
||||||
|
conditions: string;
|
||||||
|
humidity: number;
|
||||||
|
wind_speed: number;
|
||||||
|
timestamp: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
TypeScript adds type safety to our MCP server, making it more reliable and easier to maintain.
|
||||||
|
</Tab>
|
||||||
|
|
||||||
|
<Tab title="Resources">
|
||||||
|
```typescript
|
||||||
|
this.server.setRequestHandler(
|
||||||
|
ListResourcesRequestSchema,
|
||||||
|
async () => ({
|
||||||
|
resources: [{
|
||||||
|
uri: `weather://${DEFAULT_CITY}/current`,
|
||||||
|
name: `Current weather in ${DEFAULT_CITY}`,
|
||||||
|
mimeType: "application/json"
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
```
|
||||||
|
Resources provide data that Claude can access as context.
|
||||||
|
</Tab>
|
||||||
|
|
||||||
|
<Tab title="Tools">
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
name: "get_forecast",
|
||||||
|
description: "Get weather forecast for a city",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
city: { type: "string" },
|
||||||
|
days: { type: "number" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Tools let Claude take actions through your server with type-safe inputs.
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
<CardGroup cols={2}>
|
||||||
|
<Card
|
||||||
|
title="Error Handling"
|
||||||
|
icon="shield"
|
||||||
|
>
|
||||||
|
```typescript
|
||||||
|
try {
|
||||||
|
const response = await axios.get(url);
|
||||||
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
throw new Error(`API error: ${error.response?.data.message}`);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
title="Type Guards"
|
||||||
|
icon="check"
|
||||||
|
>
|
||||||
|
```typescript
|
||||||
|
function isValidForecastArgs(args: unknown): args is GetForecastArgs {
|
||||||
|
return typeof args === "object" && args !== null &&
|
||||||
|
"city" in args;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
title="Environment Variables"
|
||||||
|
icon="gear"
|
||||||
|
>
|
||||||
|
```typescript
|
||||||
|
if (!process.env.OPENWEATHER_API_KEY) {
|
||||||
|
throw new Error("OPENWEATHER_API_KEY is required");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
title="Constants"
|
||||||
|
icon="box"
|
||||||
|
>
|
||||||
|
```typescript
|
||||||
|
const API_ENDPOINTS = {
|
||||||
|
CURRENT: "weather",
|
||||||
|
FORECAST: "forecast"
|
||||||
|
} as const;
|
||||||
|
```
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
||||||
|
## Enhancements
|
||||||
|
|
||||||
|
Try adding these features to your server:
|
||||||
|
|
||||||
|
<CardGroup cols={3}>
|
||||||
|
<Card
|
||||||
|
title="Resource Templates"
|
||||||
|
icon="square-terminal"
|
||||||
|
>
|
||||||
|
Add dynamic city resources:
|
||||||
|
```typescript
|
||||||
|
uriTemplate: "weather://{city}/current"
|
||||||
|
```
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
title="Caching"
|
||||||
|
icon="database"
|
||||||
|
>
|
||||||
|
Cache API responses:
|
||||||
|
```typescript
|
||||||
|
import NodeCache from "node-cache"
|
||||||
|
```
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
title="Rate Limiting"
|
||||||
|
icon="gauge"
|
||||||
|
>
|
||||||
|
Add API rate limiting:
|
||||||
|
```typescript
|
||||||
|
import rateLimit from "axios-rate-limit"
|
||||||
|
```
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
<AccordionGroup>
|
||||||
|
<Accordion title="Build Errors">
|
||||||
|
```bash
|
||||||
|
# Check TypeScript version
|
||||||
|
npx tsc --version
|
||||||
|
|
||||||
|
# Clean and rebuild
|
||||||
|
rm -rf dist/
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="Runtime Errors">
|
||||||
|
Add better error logging:
|
||||||
|
```typescript
|
||||||
|
server.onerror = (error) => {
|
||||||
|
console.error("[MCP Error]", error);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="Type Errors">
|
||||||
|
```bash
|
||||||
|
# Check types without building
|
||||||
|
npx tsc --noEmit
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
</AccordionGroup>
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
<CardGroup cols={2}>
|
||||||
|
<Card
|
||||||
|
title="Add WebSocket Transport"
|
||||||
|
icon="bolt"
|
||||||
|
href="/guides/websocket"
|
||||||
|
>
|
||||||
|
Switch to real-time updates with WebSocket transport
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
title="Add Testing"
|
||||||
|
icon="vial"
|
||||||
|
href="/guides/testing"
|
||||||
|
>
|
||||||
|
Add Jest tests for your MCP server
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
Need help? Ask Claude! Since it has access to the MCP SDK documentation, it can help you debug issues and suggest improvements to your server.
|
||||||
|
</Note>
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
title: "Implementing Tools"
|
|
||||||
description: "Create tools that LLMs can invoke"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Implementing Tools
|
|
||||||
|
|
||||||
Create tools that LLMs can invoke
|
|
||||||
442
docs/guides/integrate-python.mdx
Normal file
442
docs/guides/integrate-python.mdx
Normal file
|
|
@ -0,0 +1,442 @@
|
||||||
|
---
|
||||||
|
title: "Integrate MCP into Your Python App"
|
||||||
|
description: "Add MCP support to your LLM application in Python"
|
||||||
|
---
|
||||||
|
|
||||||
|
Learn how to integrate MCP into your Python LLM application. We'll build a simple web analyzer that uses Claude to analyze websites, enhanced with MCP's web automation capabilities.
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
This guide uses an existing MCP server ([puppeteer](/servers/puppeteer)) rather than building one from scratch. This lets us focus on the integration patterns.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## The example app
|
||||||
|
|
||||||
|
We'll build an "AI Web Analyzer" that:
|
||||||
|
1. Takes a URL from the user
|
||||||
|
2. Captures the webpage using MCP
|
||||||
|
3. Has the LLM analyze the page's content, design, and accessibility
|
||||||
|
|
||||||
|
<Frame>
|
||||||
|
<img src="/api/placeholder/800/400" alt="Screenshot of the Web Analyzer CLI interface" />
|
||||||
|
</Frame>
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Install dependencies">
|
||||||
|
```bash
|
||||||
|
pip install modelcontextprotocol-sdk anthropic click rich
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Set up puppeteer server">
|
||||||
|
```bash
|
||||||
|
# Install example MCP servers
|
||||||
|
git clone https://github.com/modelcontextprotocol/example-servers
|
||||||
|
cd example-servers
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
npm install -g
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Get API key">
|
||||||
|
```bash
|
||||||
|
# Set your Anthropic API key
|
||||||
|
export ANTHROPIC_API_KEY='your-key-here'
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Building the base app
|
||||||
|
|
||||||
|
First, let's create a basic version without MCP:
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Create project structure">
|
||||||
|
```bash
|
||||||
|
mkdir web-analyzer
|
||||||
|
cd web-analyzer
|
||||||
|
touch analyzer.py
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Add base implementation">
|
||||||
|
```python
|
||||||
|
# analyzer.py
|
||||||
|
import click
|
||||||
|
import anthropic
|
||||||
|
from rich.console import Console
|
||||||
|
from rich.panel import Panel
|
||||||
|
from rich import print as rprint
|
||||||
|
|
||||||
|
console = Console()
|
||||||
|
client = anthropic.Client()
|
||||||
|
|
||||||
|
def analyze_url(url: str) -> str:
|
||||||
|
messages = [{
|
||||||
|
"role": "user",
|
||||||
|
"content": f"Please analyze this website: {url}\n\n"
|
||||||
|
f"I'd like to know about:\n"
|
||||||
|
f"1. Content quality and organization\n"
|
||||||
|
f"2. Visual design (based on the URL/description)\n"
|
||||||
|
f"3. Potential improvements"
|
||||||
|
}]
|
||||||
|
|
||||||
|
response = client.messages.create(
|
||||||
|
model="claude-3-opus-20240229",
|
||||||
|
max_tokens=1000,
|
||||||
|
messages=messages
|
||||||
|
)
|
||||||
|
return response.content[0].text
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.argument('url')
|
||||||
|
def main(url: str):
|
||||||
|
with console.status("[bold green]Analyzing website..."):
|
||||||
|
analysis = analyze_url(url)
|
||||||
|
|
||||||
|
rprint(Panel.fit(
|
||||||
|
analysis,
|
||||||
|
title="Website Analysis",
|
||||||
|
border_style="blue"
|
||||||
|
))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Test the base app">
|
||||||
|
```bash
|
||||||
|
python analyzer.py https://example.com
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Adding MCP support
|
||||||
|
|
||||||
|
Now let's enhance the app with MCP capabilities:
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Create MCP client">
|
||||||
|
```python
|
||||||
|
# At the top of analyzer.py
|
||||||
|
from modelcontextprotocol.client import Client
|
||||||
|
from modelcontextprotocol.client.stdio import StdioClientTransport
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
class MCPWebClient:
|
||||||
|
def __init__(self):
|
||||||
|
self.client = Client(
|
||||||
|
client_info={
|
||||||
|
"name": "web-analyzer",
|
||||||
|
"version": "0.1.0"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.transport = None
|
||||||
|
|
||||||
|
async def connect(self):
|
||||||
|
self.transport = StdioClientTransport(
|
||||||
|
server={
|
||||||
|
"command": "mcp-server-puppeteer",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await self.client.connect(self.transport)
|
||||||
|
|
||||||
|
async def navigate(self, url: str) -> Optional[str]:
|
||||||
|
try:
|
||||||
|
result = await self.client.request(
|
||||||
|
{
|
||||||
|
"method": "tools/call",
|
||||||
|
"params": {
|
||||||
|
"name": "navigate",
|
||||||
|
"arguments": {"url": url}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
schema=CallToolResultSchema
|
||||||
|
)
|
||||||
|
return result.get("toolResult")
|
||||||
|
except Exception as e:
|
||||||
|
console.print(f"[red]Error navigating to URL: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def get_console_logs(self) -> str:
|
||||||
|
result = await self.client.request(
|
||||||
|
{
|
||||||
|
"method": "resources/read",
|
||||||
|
"params": {"uri": "console://logs"}
|
||||||
|
},
|
||||||
|
schema=ReadResourceResultSchema
|
||||||
|
)
|
||||||
|
return result["contents"][0]["text"]
|
||||||
|
|
||||||
|
async def close(self):
|
||||||
|
if self.transport:
|
||||||
|
await self.client.close()
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
<Step title="Update the analyzer">
|
||||||
|
```python
|
||||||
|
async def analyze_url_with_mcp(url: str) -> str:
|
||||||
|
# Create and connect MCP client
|
||||||
|
mcp = MCPWebClient()
|
||||||
|
await mcp.connect()
|
||||||
|
# Navigate to the URL
|
||||||
|
await mcp.navigate(url)
|
||||||
|
|
||||||
|
# Get console logs (includes errors, warnings, etc.)
|
||||||
|
console_logs = await mcp.get_console_logs()
|
||||||
|
|
||||||
|
# Close the connection
|
||||||
|
await mcp.close()
|
||||||
|
|
||||||
|
# Analyze with Claude
|
||||||
|
messages = [{
|
||||||
|
"role": "user",
|
||||||
|
"content": (
|
||||||
|
f"Please analyze this website: {url}\n\n"
|
||||||
|
f"Here are the browser console logs:\n{console_logs}\n\n"
|
||||||
|
f"Please analyze:\n"
|
||||||
|
f"1. Content quality and organization\n"
|
||||||
|
f"2. Visual design\n"
|
||||||
|
f"3. Technical issues (based on console logs)\n"
|
||||||
|
f"4. Potential improvements"
|
||||||
|
)
|
||||||
|
}]
|
||||||
|
|
||||||
|
response = client.messages.create(
|
||||||
|
model="claude-3-opus-20240229",
|
||||||
|
max_tokens=1000,
|
||||||
|
messages=messages
|
||||||
|
)
|
||||||
|
return response.content[0].text
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.argument('url')
|
||||||
|
def main(url: str):
|
||||||
|
with console.status("[bold green]Analyzing website..."):
|
||||||
|
analysis = asyncio.run(analyze_url_with_mcp(url))
|
||||||
|
|
||||||
|
rprint(Panel.fit(
|
||||||
|
analysis,
|
||||||
|
title="Website Analysis",
|
||||||
|
border_style="blue"
|
||||||
|
))
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
<Step title="Run the enhanced version">
|
||||||
|
```bash
|
||||||
|
python analyzer.py https://example.com
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Understanding MCP integration
|
||||||
|
|
||||||
|
Let's break down the key components of MCP integration:
|
||||||
|
<Tabs>
|
||||||
|
<Tab title="Client Setup">
|
||||||
|
```python
|
||||||
|
class MCPWebClient:
|
||||||
|
def __init__(self):
|
||||||
|
self.client = Client(
|
||||||
|
client_info={
|
||||||
|
"name": "web-analyzer",
|
||||||
|
"version": "0.1.0"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
The MCP client identifies your application and manages the connection to MCP servers.
|
||||||
|
</Tab>
|
||||||
|
<Tab title="Transport">
|
||||||
|
```python
|
||||||
|
self.transport = StdioClientTransport(
|
||||||
|
server={
|
||||||
|
"command": "mcp-server-puppeteer",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
The transport layer handles communication with the MCP server. Here we're using stdio to talk to the puppeteer server.
|
||||||
|
</Tab>
|
||||||
|
<Tab title="Tools">
|
||||||
|
```python
|
||||||
|
result = await self.client.request(
|
||||||
|
{
|
||||||
|
"method": "tools/call",
|
||||||
|
"params": {
|
||||||
|
"name": "navigate",
|
||||||
|
"arguments": {"url": url}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
schema=CallToolResultSchema
|
||||||
|
)
|
||||||
|
```
|
||||||
|
Tools let you take actions through MCP servers. Here we're using puppeteer's navigate tool.
|
||||||
|
</Tab>
|
||||||
|
<Tab title="Resources">
|
||||||
|
```python
|
||||||
|
result = await self.client.request(
|
||||||
|
{
|
||||||
|
"method": "resources/read",
|
||||||
|
"params": {"uri": "console://logs"}
|
||||||
|
},
|
||||||
|
schema=ReadResourceResultSchema
|
||||||
|
)
|
||||||
|
```
|
||||||
|
Resources provide data from MCP servers. Here we're reading console logs that puppeteer collected.
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
## Enhancement ideas
|
||||||
|
|
||||||
|
Now that you have MCP working, here are some ways to enhance the analyzer:
|
||||||
|
<CardGroup cols={2}>
|
||||||
|
<Card
|
||||||
|
title="Visual Analysis"
|
||||||
|
icon="image"
|
||||||
|
>
|
||||||
|
```python
|
||||||
|
# Take screenshots with puppeteer
|
||||||
|
await client.request({
|
||||||
|
"method": "tools/call",
|
||||||
|
"params": {
|
||||||
|
"name": "screenshot",
|
||||||
|
"arguments": {...}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
</Card>
|
||||||
|
<Card
|
||||||
|
title="Multiple Pages"
|
||||||
|
icon="layer-group">
|
||||||
|
```python
|
||||||
|
# Analyze multiple pages in sequence
|
||||||
|
for url in urls:
|
||||||
|
await navigate(url)
|
||||||
|
await analyze()
|
||||||
|
```
|
||||||
|
</Card>
|
||||||
|
<Card
|
||||||
|
title="Interactive Elements"
|
||||||
|
icon="mouse-pointer">
|
||||||
|
```python
|
||||||
|
# Test interactive features
|
||||||
|
await client.request({
|
||||||
|
"method": "tools/call",
|
||||||
|
"params": {
|
||||||
|
"name": "click",
|
||||||
|
"arguments": {"selector": "button"}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
||||||
|
Common Patterns
|
||||||
|
Here are some patterns you'll often use when integrating MCP:
|
||||||
|
<AccordionGroup>
|
||||||
|
<Accordion title="Resource Polling" active>
|
||||||
|
When you need to wait for resource updates:
|
||||||
|
```python
|
||||||
|
async def wait_for_resource(uri: str, timeout: int = 30):
|
||||||
|
start = time.time()
|
||||||
|
while time.time() - start < timeout:
|
||||||
|
result = await client.request(
|
||||||
|
{
|
||||||
|
"method": "resources/read",
|
||||||
|
"params": {"uri": uri}
|
||||||
|
},
|
||||||
|
schema=ReadResourceResultSchema
|
||||||
|
)
|
||||||
|
if some_condition(result):
|
||||||
|
return result
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
raise TimeoutError()
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
<Accordion title="Error Handling">
|
||||||
|
Robust error handling for MCP operations:
|
||||||
|
```python
|
||||||
|
try:
|
||||||
|
await client.request(...)
|
||||||
|
except MCPError as e:
|
||||||
|
if e.code == ErrorCode.ResourceNotFound:
|
||||||
|
# Handle missing resource
|
||||||
|
elif e.code == ErrorCode.InvalidRequest:
|
||||||
|
# Handle invalid parameters
|
||||||
|
else:
|
||||||
|
# Handle other errors
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
<Accordion title="Resource Subscriptions">
|
||||||
|
Subscribe to resource updates:
|
||||||
|
```python
|
||||||
|
await client.request(
|
||||||
|
{
|
||||||
|
"method": "resources/subscribe",
|
||||||
|
"params": {"uri": "console://logs"}
|
||||||
|
},
|
||||||
|
schema=EmptyResultSchema
|
||||||
|
)
|
||||||
|
# Now you'll get notifications when logs change
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
</AccordionGroup>
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
<AccordionGroup>
|
||||||
|
<Accordion title="Connection Issues">
|
||||||
|
```python
|
||||||
|
# Enable debug logging
|
||||||
|
import logging
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
```
|
||||||
|
Watch for connection errors and MCP protocol messages.
|
||||||
|
</Accordion>
|
||||||
|
<Accordion title="Resource Errors">
|
||||||
|
1. Check if the resource exists:
|
||||||
|
```python
|
||||||
|
resources = await client.request(
|
||||||
|
{"method": "resources/list"},
|
||||||
|
schema=ListResourcesResultSchema
|
||||||
|
)
|
||||||
|
print(resources)
|
||||||
|
```
|
||||||
|
2. Verify URI format
|
||||||
|
3. Check server logs
|
||||||
|
</Accordion>
|
||||||
|
<Accordion title="Tool Errors">
|
||||||
|
1. List available tools:
|
||||||
|
```python
|
||||||
|
tools = await client.request(
|
||||||
|
{"method": "tools/list"},
|
||||||
|
schema=ListToolsResultSchema
|
||||||
|
)
|
||||||
|
print(tools)
|
||||||
|
```
|
||||||
|
2. Check argument schema
|
||||||
|
3. Verify server capabilities
|
||||||
|
</Accordion>
|
||||||
|
</AccordionGroup>
|
||||||
|
|
||||||
|
## Next steps
|
||||||
|
|
||||||
|
<CardGroup cols={2}>
|
||||||
|
<Card
|
||||||
|
title="Add More Servers"
|
||||||
|
icon="plus"
|
||||||
|
href="/servers"
|
||||||
|
>
|
||||||
|
Connect to additional MCP servers for more capabilities
|
||||||
|
</Card>
|
||||||
|
<Card
|
||||||
|
title="Build Custom Server"
|
||||||
|
icon="code"
|
||||||
|
href="/docs/guides/first-server-python">
|
||||||
|
Create your own MCP server for custom functionality
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
885
docs/guides/integrate-ts.mdx
Normal file
885
docs/guides/integrate-ts.mdx
Normal file
|
|
@ -0,0 +1,885 @@
|
||||||
|
---
|
||||||
|
title: "Integrate MCP into Your TypeScript App"
|
||||||
|
description: "Add MCP support to your LLM application in TypeScript"
|
||||||
|
---
|
||||||
|
|
||||||
|
Learn how to integrate MCP into your TypeScript/React application. We'll build an AI Web Analyzer that uses Claude to analyze websites, enhanced with MCP's web automation capabilities.
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
This guide uses an existing MCP server ([puppeteer](/servers/puppeteer)) rather than building one from scratch. This lets us focus on the integration patterns.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## The example app
|
||||||
|
|
||||||
|
We'll build an "AI Web Analyzer" that:
|
||||||
|
1. Takes a URL from the user
|
||||||
|
2. Captures the webpage using MCP
|
||||||
|
3. Has Claude analyze the page's content, design, and accessibility
|
||||||
|
|
||||||
|
<Frame>
|
||||||
|
<img src="/api/placeholder/800/400" alt="Screenshot of the Web Analyzer interface" />
|
||||||
|
</Frame>
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Create React project">
|
||||||
|
```bash
|
||||||
|
# Create new Next.js project with TypeScript
|
||||||
|
npx create-next-app@latest web-analyzer --typescript --tailwind
|
||||||
|
cd web-analyzer
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Install dependencies">
|
||||||
|
```bash
|
||||||
|
npm install @modelcontextprotocol/sdk @anthropic-ai/sdk
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Set up puppeteer server">
|
||||||
|
```bash
|
||||||
|
# Install example MCP servers
|
||||||
|
git clone https://github.com/modelcontextprotocol/example-servers
|
||||||
|
cd example-servers
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
npm install -g
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Building the base app
|
||||||
|
|
||||||
|
First, let's create a basic version without MCP:
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Create components">
|
||||||
|
Create `components/WebAnalyzer.tsx`:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { useState } from 'react';
|
||||||
|
import Anthropic from '@anthropic-ai/sdk';
|
||||||
|
|
||||||
|
const anthropic = new Anthropic({
|
||||||
|
apiKey: process.env.NEXT_PUBLIC_ANTHROPIC_API_KEY
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function WebAnalyzer() {
|
||||||
|
const [url, setUrl] = useState('');
|
||||||
|
const [analysis, setAnalysis] = useState('');
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const analyzeUrl = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const response = await anthropic.messages.create({
|
||||||
|
model: 'claude-3-opus-20240229',
|
||||||
|
max_tokens: 1000,
|
||||||
|
messages: [{
|
||||||
|
role: 'user',
|
||||||
|
content: `Please analyze this website: ${url}\n\n`
|
||||||
|
+ `I'd like to know about:\n`
|
||||||
|
+ `1. Content quality and organization\n`
|
||||||
|
+ `2. Visual design (based on URL/description)\n`
|
||||||
|
+ `3. Potential improvements`
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
setAnalysis(response.content[0].text);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
setAnalysis('Error analyzing website');
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-2xl mx-auto p-4">
|
||||||
|
<h1 className="text-2xl font-bold mb-4">
|
||||||
|
AI Web Analyzer
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div className="flex gap-2 mb-4">
|
||||||
|
<input
|
||||||
|
type="url"
|
||||||
|
value={url}
|
||||||
|
onChange={(e) => setUrl(e.target.value)}
|
||||||
|
placeholder="Enter website URL"
|
||||||
|
className="flex-1 p-2 border rounded"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={analyzeUrl}
|
||||||
|
disabled={loading}
|
||||||
|
className="px-4 py-2 bg-blue-500 text-white rounded"
|
||||||
|
>
|
||||||
|
Analyze
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{loading && (
|
||||||
|
<div className="animate-pulse text-gray-500">
|
||||||
|
Analyzing website...
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{analysis && (
|
||||||
|
<div className="mt-4 p-4 border rounded bg-gray-50">
|
||||||
|
<pre className="whitespace-pre-wrap">{analysis}</pre>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Update app page">
|
||||||
|
Update `app/page.tsx`:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import WebAnalyzer from '@/components/WebAnalyzer';
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<main>
|
||||||
|
<WebAnalyzer />
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Add environment variables">
|
||||||
|
Create `.env.local`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
NEXT_PUBLIC_ANTHROPIC_API_KEY=your-key-here
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Adding MCP support
|
||||||
|
|
||||||
|
Now let's enhance the app with MCP capabilities:
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Create MCP client">
|
||||||
|
Create `lib/mcp.ts`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { Client } from '@modelcontextprotocol/sdk/client';
|
||||||
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio';
|
||||||
|
import {
|
||||||
|
CallToolResultSchema,
|
||||||
|
ReadResourceResultSchema
|
||||||
|
} from '@modelcontextprotocol/sdk/types';
|
||||||
|
|
||||||
|
export class MCPWebClient {
|
||||||
|
private client: Client;
|
||||||
|
private transport: StdioClientTransport | null = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.client = new Client({
|
||||||
|
name: 'web-analyzer',
|
||||||
|
version: '0.1.0'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async connect() {
|
||||||
|
this.transport = new StdioClientTransport({
|
||||||
|
command: 'mcp-server-puppeteer'
|
||||||
|
});
|
||||||
|
await this.client.connect(this.transport);
|
||||||
|
}
|
||||||
|
|
||||||
|
async navigate(url: string): Promise<string | null> {
|
||||||
|
try {
|
||||||
|
const result = await this.client.request(
|
||||||
|
{
|
||||||
|
method: 'tools/call',
|
||||||
|
params: {
|
||||||
|
name: 'navigate',
|
||||||
|
arguments: { url }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CallToolResultSchema
|
||||||
|
);
|
||||||
|
return result.toolResult as string;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error navigating:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getConsoleLogs(): Promise<string> {
|
||||||
|
const result = await this.client.request(
|
||||||
|
{
|
||||||
|
method: 'resources/read',
|
||||||
|
params: { uri: 'console://logs' }
|
||||||
|
},
|
||||||
|
ReadResourceResultSchema
|
||||||
|
);
|
||||||
|
return result.contents[0].text;
|
||||||
|
}
|
||||||
|
|
||||||
|
async close() {
|
||||||
|
if (this.transport) {
|
||||||
|
await this.client.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Create MCP context">
|
||||||
|
Create `contexts/MCPContext.tsx`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { createContext, useContext, useEffect, useRef } from 'react';
|
||||||
|
import { MCPWebClient } from '@/lib/mcp';
|
||||||
|
|
||||||
|
const MCPContext = createContext<MCPWebClient | null>(null);
|
||||||
|
|
||||||
|
export function MCPProvider({ children }: { children: React.ReactNode }) {
|
||||||
|
const clientRef = useRef<MCPWebClient | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const initMCP = async () => {
|
||||||
|
clientRef.current = new MCPWebClient();
|
||||||
|
await clientRef.current.connect();
|
||||||
|
};
|
||||||
|
|
||||||
|
initMCP();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (clientRef.current) {
|
||||||
|
clientRef.current.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MCPContext.Provider value={clientRef.current}>
|
||||||
|
{children}
|
||||||
|
</MCPContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useMCP() {
|
||||||
|
const context = useContext(MCPContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useMCP must be used within MCPProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Update the analyzer component">
|
||||||
|
Update `components/WebAnalyzer.tsx`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { useState } from 'react';
|
||||||
|
import Anthropic from '@anthropic-ai/sdk';
|
||||||
|
import { useMCP } from '@/contexts/MCPContext';
|
||||||
|
|
||||||
|
const anthropic = new Anthropic({
|
||||||
|
apiKey: process.env.NEXT_PUBLIC_ANTHROPIC_API_KEY
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function WebAnalyzer() {
|
||||||
|
const mcp = useMCP();
|
||||||
|
const [url, setUrl] = useState('');
|
||||||
|
const [analysis, setAnalysis] = useState('');
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const analyzeUrl = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
// Navigate to the URL with MCP
|
||||||
|
await mcp.navigate(url);
|
||||||
|
|
||||||
|
// Get console logs
|
||||||
|
const consoleLogs = await mcp.getConsoleLogs();
|
||||||
|
|
||||||
|
// Analyze with Claude
|
||||||
|
const response = await anthropic.messages.create({
|
||||||
|
model: 'claude-3-opus-20240229',
|
||||||
|
max_tokens: 1000,
|
||||||
|
messages: [{
|
||||||
|
role: 'user',
|
||||||
|
content: `Please analyze this website: ${url}\n\n`
|
||||||
|
+ `Here are the browser console logs:\n${consoleLogs}\n\n`
|
||||||
|
+ `Please analyze:\n`
|
||||||
|
+ `1. Content quality and organization\n`
|
||||||
|
+ `2. Visual design\n`
|
||||||
|
+ `3. Technical issues (based on console logs)\n`
|
||||||
|
+ `4. Potential improvements`
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
setAnalysis(response.content[0].text);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
setAnalysis('Error analyzing website');
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ... rest of the component remains the same
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Add the provider">
|
||||||
|
Update `app/layout.tsx`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { MCPProvider } from '@/contexts/MCPContext';
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<MCPProvider>
|
||||||
|
{children}
|
||||||
|
</MCPProvider>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Understanding MCP integration
|
||||||
|
|
||||||
|
Let's break down the key components:
|
||||||
|
|
||||||
|
<Tabs>
|
||||||
|
<Tab title="Client Setup">
|
||||||
|
```typescript
|
||||||
|
export class MCPWebClient {
|
||||||
|
private client: Client;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.client = new Client({
|
||||||
|
name: 'web-analyzer',
|
||||||
|
version: '0.1.0'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The MCP client manages the connection to MCP servers and handles requests.
|
||||||
|
</Tab>
|
||||||
|
|
||||||
|
<Tab title="Context Provider">
|
||||||
|
```typescript
|
||||||
|
export function MCPProvider({ children }: { children: React.ReactNode }) {
|
||||||
|
const clientRef = useRef<MCPWebClient | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const initMCP = async () => {
|
||||||
|
clientRef.current = new MCPWebClient();
|
||||||
|
await clientRef.current.connect();
|
||||||
|
};
|
||||||
|
|
||||||
|
initMCP();
|
||||||
|
// ... cleanup
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The context provider manages the MCP client lifecycle.
|
||||||
|
</Tab>
|
||||||
|
|
||||||
|
<Tab title="Tools">
|
||||||
|
```typescript
|
||||||
|
async navigate(url: string): Promise<string | null> {
|
||||||
|
const result = await this.client.request(
|
||||||
|
{
|
||||||
|
method: 'tools/call',
|
||||||
|
params: {
|
||||||
|
name: 'navigate',
|
||||||
|
arguments: { url }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CallToolResultSchema
|
||||||
|
);
|
||||||
|
return result.toolResult as string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Tools let you take actions through MCP servers.
|
||||||
|
</Tab>
|
||||||
|
|
||||||
|
<Tab title="Resources">
|
||||||
|
```typescript
|
||||||
|
async getConsoleLogs(): Promise<string> {
|
||||||
|
const result = await this.client.request(
|
||||||
|
{
|
||||||
|
method: 'resources/read',
|
||||||
|
params: { uri: 'console://logs' }
|
||||||
|
},
|
||||||
|
ReadResourceResultSchema
|
||||||
|
);
|
||||||
|
return result.contents[0].text;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Resources provide data from MCP servers.
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
## Enhancement ideas
|
||||||
|
|
||||||
|
Here are some ways to enhance the analyzer:
|
||||||
|
|
||||||
|
<CardGroup cols={2}>
|
||||||
|
<Card
|
||||||
|
title="Visual Snapshots"
|
||||||
|
icon="image"
|
||||||
|
>
|
||||||
|
```typescript
|
||||||
|
// Add screenshot capability
|
||||||
|
async function captureScreenshot() {
|
||||||
|
const result = await mcp.client.request({
|
||||||
|
method: 'tools/call',
|
||||||
|
params: {
|
||||||
|
name: 'screenshot',
|
||||||
|
arguments: { /* ... */ }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
title="Batch Analysis"
|
||||||
|
icon="layer-group"
|
||||||
|
>
|
||||||
|
```typescript
|
||||||
|
// Analyze multiple pages
|
||||||
|
async function analyzeBatch(urls: string[]) {
|
||||||
|
const results = [];
|
||||||
|
for (const url of urls) {
|
||||||
|
await mcp.navigate(url);
|
||||||
|
// ... analyze
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
title="Interactive Testing"
|
||||||
|
icon="mouse-pointer"
|
||||||
|
>
|
||||||
|
```typescript
|
||||||
|
// Test interactive elements
|
||||||
|
async function testInteraction() {
|
||||||
|
await mcp.client.request({
|
||||||
|
method: 'tools/call',
|
||||||
|
params: {
|
||||||
|
name: 'click',
|
||||||
|
arguments: { selector: 'button' }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
title="Performance Analysis"
|
||||||
|
icon="gauge"
|
||||||
|
>
|
||||||
|
```typescript
|
||||||
|
// Add performance metrics
|
||||||
|
async function getPerformance() {
|
||||||
|
const metrics = await mcp.client.request({
|
||||||
|
method: 'resources/read',
|
||||||
|
params: { uri: 'metrics://performance' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
||||||
|
## Common patterns
|
||||||
|
|
||||||
|
Here are some patterns you'll often use with MCP in React:
|
||||||
|
|
||||||
|
<AccordionGroup>
|
||||||
|
<Accordion title="Resource Polling" active>
|
||||||
|
```typescript
|
||||||
|
function useResourcePolling(uri: string, interval = 1000) {
|
||||||
|
const [data, setData] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let mounted = true;
|
||||||
|
|
||||||
|
async function poll() {
|
||||||
|
while (mounted) {
|
||||||
|
const result = await mcp.client.request({
|
||||||
|
method: 'resources/read',
|
||||||
|
params: { uri }
|
||||||
|
});
|
||||||
|
if (mounted) {
|
||||||
|
setData(result);
|
||||||
|
await new Promise(r => setTimeout(r, interval));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
poll();
|
||||||
|
return () => { mounted = false; };
|
||||||
|
}, [uri, interval]);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="Error Boundaries">
|
||||||
|
```typescript
|
||||||
|
class MCPErrorBoundary extends React.Component {
|
||||||
|
state = { hasError: false };
|
||||||
|
|
||||||
|
static getDerivedStateFromError(error) {
|
||||||
|
return { hasError: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidCatch(error, errorInfo) {
|
||||||
|
if (error instanceof MCPError) {
|
||||||
|
// Handle MCP-specific errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.state.hasError) {
|
||||||
|
return <ErrorDisplay />;
|
||||||
|
}
|
||||||
|
return this.props.children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="Resource Subscriptions">
|
||||||
|
```typescript
|
||||||
|
function useResourceSubscription(uri: string) {
|
||||||
|
useEffect(() => {
|
||||||
|
mcp.client.request({
|
||||||
|
method: 'resources/subscribe',
|
||||||
|
params: { uri }
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
mcp.client.request({
|
||||||
|
method: 'resources/unsubscribe',
|
||||||
|
params: { uri }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}, [uri]);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
</AccordionGroup>
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
<AccordionGroup>
|
||||||
|
<Accordion title="Connection Issues">
|
||||||
|
```typescript
|
||||||
|
// Enable debug logging
|
||||||
|
const initMCP = async () => {
|
||||||
|
const client = new MCPWebClient({
|
||||||
|
debug: true, // Enable debug logging
|
||||||
|
onError: (error) => {
|
||||||
|
console.error('MCP Error:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await client.connect();
|
||||||
|
};
|
||||||
|
```
|
||||||
|
Watch the browser console for connection errors and protocol messages.
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="React Component Lifecycle">
|
||||||
|
Common issues and solutions:
|
||||||
|
```typescript
|
||||||
|
function WebAnalyzer() {
|
||||||
|
// Don't create client on every render
|
||||||
|
const mcpRef = useRef<MCPWebClient | null>(null);
|
||||||
|
|
||||||
|
// Initialize in useEffect
|
||||||
|
useEffect(() => {
|
||||||
|
const init = async () => {
|
||||||
|
mcpRef.current = new MCPWebClient();
|
||||||
|
await mcpRef.current.connect();
|
||||||
|
};
|
||||||
|
init();
|
||||||
|
|
||||||
|
// Clean up properly
|
||||||
|
return () => {
|
||||||
|
if (mcpRef.current) {
|
||||||
|
mcpRef.current.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
|
||||||
|
<Accordion title="Type Safety">
|
||||||
|
Use TypeScript to catch MCP issues early:
|
||||||
|
```typescript
|
||||||
|
// Define expected response types
|
||||||
|
interface ToolResponse {
|
||||||
|
toolResult: {
|
||||||
|
screenshot?: string;
|
||||||
|
logs?: string[];
|
||||||
|
error?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use with request
|
||||||
|
const result = await client.request<ToolResponse>({
|
||||||
|
method: 'tools/call',
|
||||||
|
params: {
|
||||||
|
name: 'screenshot',
|
||||||
|
arguments: { /* ... */ }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// TypeScript will catch missing properties
|
||||||
|
console.log(result.toolResult.screenshot);
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
</AccordionGroup>
|
||||||
|
|
||||||
|
## Performance optimization
|
||||||
|
|
||||||
|
<CardGroup cols={2}>
|
||||||
|
<Card
|
||||||
|
title="Caching Resources"
|
||||||
|
icon="database"
|
||||||
|
>
|
||||||
|
```typescript
|
||||||
|
function useCachedResource(uri: string) {
|
||||||
|
const [data, setData] = useState<string | null>(null);
|
||||||
|
const cache = useRef(new Map<string, string>());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function fetchResource() {
|
||||||
|
if (cache.current.has(uri)) {
|
||||||
|
setData(cache.current.get(uri)!);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await mcp.client.request({
|
||||||
|
method: 'resources/read',
|
||||||
|
params: { uri }
|
||||||
|
});
|
||||||
|
|
||||||
|
cache.current.set(uri, result.contents[0].text);
|
||||||
|
setData(result.contents[0].text);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchResource();
|
||||||
|
}, [uri]);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card
|
||||||
|
title="Batching Requests"
|
||||||
|
icon="layer-group"
|
||||||
|
>
|
||||||
|
```typescript
|
||||||
|
class BatchedMCPClient extends MCPWebClient {
|
||||||
|
private batchQueue: Map<string, Promise<any>>;
|
||||||
|
|
||||||
|
async request(method: string, params: any) {
|
||||||
|
const key = JSON.stringify({ method, params });
|
||||||
|
|
||||||
|
if (!this.batchQueue.has(key)) {
|
||||||
|
this.batchQueue.set(
|
||||||
|
key,
|
||||||
|
super.request(method, params)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Clear after resolution
|
||||||
|
this.batchQueue.get(key)!.finally(() => {
|
||||||
|
this.batchQueue.delete(key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.batchQueue.get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
||||||
|
## Building for production
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Error handling">
|
||||||
|
Add proper error boundaries:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { ErrorBoundary } from 'react-error-boundary';
|
||||||
|
|
||||||
|
function ErrorFallback({ error, resetErrorBoundary }) {
|
||||||
|
return (
|
||||||
|
<div className="p-4 border border-red-500 rounded">
|
||||||
|
<h2 className="text-red-500">Something went wrong:</h2>
|
||||||
|
<pre>{error.message}</pre>
|
||||||
|
<button
|
||||||
|
onClick={resetErrorBoundary}
|
||||||
|
className="mt-2 px-4 py-2 bg-red-500 text-white rounded"
|
||||||
|
>
|
||||||
|
Try again
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
return (
|
||||||
|
<ErrorBoundary
|
||||||
|
FallbackComponent={ErrorFallback}
|
||||||
|
onReset={() => {
|
||||||
|
// Reset application state
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<WebAnalyzer />
|
||||||
|
</ErrorBoundary>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Loading states">
|
||||||
|
Add proper loading indicators:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function WebAnalyzer() {
|
||||||
|
const [state, setState] = useState<{
|
||||||
|
status: 'idle' | 'loading' | 'success' | 'error';
|
||||||
|
error?: Error;
|
||||||
|
data?: string;
|
||||||
|
}>({
|
||||||
|
status: 'idle'
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{state.status === 'loading' && (
|
||||||
|
<div className="animate-pulse">
|
||||||
|
<div className="h-4 bg-gray-200 rounded w-3/4"></div>
|
||||||
|
<div className="space-y-3 mt-4">
|
||||||
|
<div className="h-4 bg-gray-200 rounded"></div>
|
||||||
|
<div className="h-4 bg-gray-200 rounded w-5/6"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{state.status === 'error' && (
|
||||||
|
<div className="text-red-500">
|
||||||
|
{state.error?.message}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{state.status === 'success' && (
|
||||||
|
<div>{state.data}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Environment configuration">
|
||||||
|
Set up proper environment variables:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// next.config.js
|
||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
NEXT_PUBLIC_ANTHROPIC_API_KEY: process.env.NEXT_PUBLIC_ANTHROPIC_API_KEY,
|
||||||
|
MCP_DEBUG: process.env.NODE_ENV === 'development'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Usage in code
|
||||||
|
const mcpClient = new MCPWebClient({
|
||||||
|
debug: process.env.MCP_DEBUG === 'true'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
## Security considerations
|
||||||
|
|
||||||
|
<Warning>
|
||||||
|
Always validate and sanitize URLs before passing them to MCP servers. The puppeteer server has access to execute JavaScript and interact with web pages.
|
||||||
|
</Warning>
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function validateUrl(url: string): boolean {
|
||||||
|
try {
|
||||||
|
const parsed = new URL(url);
|
||||||
|
// Add your validation rules
|
||||||
|
return parsed.protocol === 'https:';
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function WebAnalyzer() {
|
||||||
|
const analyzeUrl = async () => {
|
||||||
|
if (!validateUrl(url)) {
|
||||||
|
setError('Please enter a valid HTTPS URL');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// ... rest of the analysis code
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Next steps
|
||||||
|
|
||||||
|
<CardGroup cols={2}>
|
||||||
|
<Card
|
||||||
|
title="Add Authentication"
|
||||||
|
icon="lock"
|
||||||
|
href="/guides/auth"
|
||||||
|
>
|
||||||
|
Learn how to handle authenticated web pages with MCP
|
||||||
|
</Card>
|
||||||
|
<Card
|
||||||
|
title="Custom MCP Server"
|
||||||
|
icon="server"
|
||||||
|
href="/quickstarts/first-server-typescript">
|
||||||
|
Build your own MCP server for custom functionality
|
||||||
|
</Card>
|
||||||
|
<Card
|
||||||
|
title="Testing"
|
||||||
|
icon="vial"
|
||||||
|
href="/guides/testing">
|
||||||
|
Learn how to test MCP integrations
|
||||||
|
</Card>
|
||||||
|
<Card
|
||||||
|
title="Production Setup"
|
||||||
|
icon="rocket"
|
||||||
|
href="/guides/production">
|
||||||
|
Deploy your MCP-enabled app to production
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
title: "Testing and Debugging"
|
|
||||||
description: "Test and debug your MCP servers effectively"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Testing and Debugging
|
|
||||||
|
|
||||||
Test and debug your MCP servers effectively
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
title: "Working with Resources"
|
|
||||||
description: "Learn how to expose and consume data through MCP"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Working with Resources
|
|
||||||
|
|
||||||
Learn how to expose and consume data through MCP
|
|
||||||
|
|
@ -1,160 +0,0 @@
|
||||||
---
|
|
||||||
title: "Installation"
|
|
||||||
description: "Get started with Model Context Protocol (MCP) by installing the SDK for your preferred language"
|
|
||||||
---
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
MCP supports both TypeScript and Python implementations. Choose the one that best fits your project.
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
<Steps>
|
|
||||||
<Step title="Choose Your SDK">
|
|
||||||
<Tabs>
|
|
||||||
<Tab title="TypeScript">
|
|
||||||
Install the TypeScript SDK using npm:
|
|
||||||
```bash
|
|
||||||
npm install @modelcontextprotocol/sdk
|
|
||||||
```
|
|
||||||
</Tab>
|
|
||||||
<Tab title="Python">
|
|
||||||
Install the Python SDK using pip:
|
|
||||||
```bash
|
|
||||||
pip install modelcontextprotocol
|
|
||||||
```
|
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
|
||||||
</Step>
|
|
||||||
|
|
||||||
<Step title="Verify Installation">
|
|
||||||
Verify your installation by running a simple test:
|
|
||||||
|
|
||||||
<CodeGroup>
|
|
||||||
```typescript typescript-test.ts
|
|
||||||
import { Client } from "@modelcontextprotocol/sdk";
|
|
||||||
|
|
||||||
const client = new Client({
|
|
||||||
name: "test-client",
|
|
||||||
version: "0.1.0"
|
|
||||||
});
|
|
||||||
console.log("MCP SDK installed successfully!");
|
|
||||||
```
|
|
||||||
|
|
||||||
```python python-test.py
|
|
||||||
from modelcontextprotocol import Client
|
|
||||||
|
|
||||||
client = Client(
|
|
||||||
name="test-client",
|
|
||||||
version="0.1.0"
|
|
||||||
)
|
|
||||||
print("MCP SDK installed successfully!")
|
|
||||||
```
|
|
||||||
</CodeGroup>
|
|
||||||
</Step>
|
|
||||||
|
|
||||||
<Step title="Set Up Development Environment">
|
|
||||||
<Info>
|
|
||||||
For local development, you'll need Node.js 18+ for TypeScript or Python 3.8+ for Python.
|
|
||||||
</Info>
|
|
||||||
|
|
||||||
Install the MCP Inspector for testing and debugging your implementations:
|
|
||||||
```bash
|
|
||||||
npm install -g @modelcontextprotocol/inspector
|
|
||||||
```
|
|
||||||
</Step>
|
|
||||||
</Steps>
|
|
||||||
|
|
||||||
## System Requirements
|
|
||||||
|
|
||||||
<AccordionGroup>
|
|
||||||
<Accordion title="TypeScript Requirements" icon="js">
|
|
||||||
- Node.js 18 or higher
|
|
||||||
- npm 7 or higher
|
|
||||||
- TypeScript 4.7 or higher (for TypeScript users)
|
|
||||||
|
|
||||||
Optional development tools:
|
|
||||||
- VS Code with TypeScript plugin
|
|
||||||
- ESLint for code quality
|
|
||||||
</Accordion>
|
|
||||||
|
|
||||||
<Accordion title="Python Requirements" icon="python">
|
|
||||||
- Python 3.8 or higher
|
|
||||||
- pip 20.0 or higher
|
|
||||||
- virtualenv (recommended)
|
|
||||||
|
|
||||||
Optional development tools:
|
|
||||||
- VS Code with Python extension
|
|
||||||
- pylint for code quality
|
|
||||||
</Accordion>
|
|
||||||
|
|
||||||
<Accordion title="Operating System Support" icon="computer">
|
|
||||||
MCP supports all major operating systems:
|
|
||||||
- Windows 10/11
|
|
||||||
- macOS 10.15+
|
|
||||||
- Linux (major distributions)
|
|
||||||
</Accordion>
|
|
||||||
</AccordionGroup>
|
|
||||||
|
|
||||||
## Architecture Overview
|
|
||||||
|
|
||||||
<Frame>
|
|
||||||
<img src="/images/mcp-architecture.png" />
|
|
||||||
</Frame>
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
flowchart LR
|
|
||||||
A[LLM Application] --> B[MCP Client]
|
|
||||||
B --> C[MCP Server]
|
|
||||||
C --> D[(External Resource)]
|
|
||||||
|
|
||||||
style A fill:#e0f4ff,stroke:#0077cc,stroke-width:2px
|
|
||||||
style B fill:#fff4e0,stroke:#ff8c00,stroke-width:2px
|
|
||||||
style C fill:#e0ffe0,stroke:#2d862d,stroke-width:2px
|
|
||||||
style D fill:#ffe0e0,stroke:#cc0000,stroke-width:2px
|
|
||||||
|
|
||||||
classDef default fill:#f9f9f9,stroke:#333,stroke-width:1px
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
<CardGroup>
|
|
||||||
<Card title="Quick Start Guide" icon="play" href="/quickstart">
|
|
||||||
Build your first MCP integration in under 5 minutes
|
|
||||||
</Card>
|
|
||||||
<Card title="Example Servers" icon="server" href="/example-servers">
|
|
||||||
Explore pre-built MCP servers for common use cases
|
|
||||||
</Card>
|
|
||||||
<Card title="API Reference" icon="book" href="/api-reference">
|
|
||||||
Dive into the complete API documentation
|
|
||||||
</Card>
|
|
||||||
</CardGroup>
|
|
||||||
<Tip>
|
|
||||||
Join our [Discord community](https://discord.gg/mcp) for help and discussions about MCP development.
|
|
||||||
</Tip>
|
|
||||||
|
|
||||||
## Troubleshooting Common Issues
|
|
||||||
|
|
||||||
<AccordionGroup>
|
|
||||||
<Accordion title="Version Conflicts">
|
|
||||||
If you encounter version conflicts, ensure all your dependencies are up to date:
|
|
||||||
```bash
|
|
||||||
# For TypeScript
|
|
||||||
npm update @modelcontextprotocol/sdk
|
|
||||||
Copy# For Python
|
|
||||||
pip install --upgrade modelcontextprotocol
|
|
||||||
```
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="Connection Issues">
|
|
||||||
If you're having trouble connecting to MCP servers:
|
|
||||||
1. Check your network configuration
|
|
||||||
2. Verify the server is running
|
|
||||||
3. Ensure your firewall isn't blocking connections
|
|
||||||
4. Check the server logs for errors
|
|
||||||
</Accordion>
|
|
||||||
<Accordion title="Build Errors">
|
|
||||||
Common build errors and their solutions:
|
|
||||||
- TypeScript type errors: Update your @types dependencies
|
|
||||||
- Python import errors: Check your PYTHONPATH
|
|
||||||
- Missing dependencies: Run package manager install commands
|
|
||||||
</Accordion>
|
|
||||||
</AccordionGroup>
|
|
||||||
<Warning>
|
|
||||||
Remember to keep your SDKs updated to the latest version for security patches and new features.
|
|
||||||
</Warning>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
title: "API Integration"
|
|
||||||
description: "Wrap external APIs with MCP"
|
|
||||||
---
|
|
||||||
|
|
||||||
# API Integration
|
|
||||||
|
|
||||||
Wrap external APIs with MCP
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
title: "Database Integration"
|
|
||||||
description: "Connect databases to your LLM through MCP"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Database Integration
|
|
||||||
|
|
||||||
Connect databases to your LLM through MCP
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
title: "Local File Access"
|
|
||||||
description: "Implement local file access in MCP"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Local File Access
|
|
||||||
|
|
||||||
Implement local file access in MCP
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
title: "Real-time Updates"
|
|
||||||
description: "Handle live data updates in MCP"
|
|
||||||
---
|
|
||||||
|
|
||||||
# Real-time Updates
|
|
||||||
|
|
||||||
Handle live data updates in MCP
|
|
||||||
161
introduction.mdx
161
introduction.mdx
|
|
@ -1,63 +1,142 @@
|
||||||
---
|
---
|
||||||
title: Introduction
|
title: Introduction
|
||||||
description: 'Introduction to the Model Context Protocol'
|
description: 'Get started with the Model Context Protocol (MCP)'
|
||||||
---
|
---
|
||||||
|
|
||||||
The Model Context Protocol (MCP) is a protocol designed to enable LLM assistant surfaces to receive contextual information from specialized servers. Whether you’re building an IDE coding assistant, a chat interface, or any other LLM-powered application, MCP provides a standardized way to obtain and utilize relevant context.
|
The Model Context Protocol (MCP) is an open protocol that enables seamless integration between LLM applications and external data sources and tools. Whether you're building an AI-powered IDE, enhancing a chat interface, or creating custom AI workflows, MCP provides a standardized way to connect LLMs with the context they need.
|
||||||
|
|
||||||
|
## Quickstart Guides
|
||||||
|
|
||||||
## Get started
|
Choose the path that best fits your needs:
|
||||||
|
|
||||||
The first step to world-class documentation is setting up your editing environments.
|
|
||||||
|
|
||||||
<CardGroup cols={2}>
|
<CardGroup cols={2}>
|
||||||
<Card
|
<Card
|
||||||
title="Edit Your Docs"
|
title="Try MCP with Claude"
|
||||||
icon="pen-to-square"
|
icon="bolt"
|
||||||
href="https://mintlify.com/docs/quickstart"
|
href="/docs/guides/claude"
|
||||||
>
|
>
|
||||||
Get your docs set up locally for easy development
|
The fastest way to see MCP in action - connect example servers to Claude in minutes
|
||||||
</Card>
|
</Card>
|
||||||
<Card
|
<Card
|
||||||
title="Preview Changes"
|
title="Build Your First Server (TypeScript)"
|
||||||
icon="image"
|
|
||||||
href="https://mintlify.com/docs/development"
|
|
||||||
>
|
|
||||||
Preview your changes before you push to make sure they're perfect
|
|
||||||
</Card>
|
|
||||||
</CardGroup>
|
|
||||||
|
|
||||||
## Make it yours
|
|
||||||
|
|
||||||
Update your docs to your brand and add valuable content for the best user conversion.
|
|
||||||
|
|
||||||
<CardGroup cols={2}>
|
|
||||||
<Card
|
|
||||||
title="Customize Style"
|
|
||||||
icon="palette"
|
|
||||||
href="https://mintlify.com/docs/settings/global"
|
|
||||||
>
|
|
||||||
Customize your docs to your company's colors and brands
|
|
||||||
</Card>
|
|
||||||
<Card
|
|
||||||
title="Reference APIs"
|
|
||||||
icon="code"
|
icon="code"
|
||||||
href="https://mintlify.com/docs/api-playground/openapi"
|
href="/docs/guides/first-server-ts"
|
||||||
>
|
>
|
||||||
Automatically generate endpoints from an OpenAPI spec
|
Create a simple MCP server in TypeScript to understand the basics
|
||||||
</Card>
|
</Card>
|
||||||
<Card
|
<Card
|
||||||
title="Add Components"
|
title="Build Your First Server (Python)"
|
||||||
icon="screwdriver-wrench"
|
icon="python"
|
||||||
href="https://mintlify.com/docs/components/accordion"
|
href="/docs/guides/first-server-python"
|
||||||
>
|
>
|
||||||
Build interactive features and designs to guide your users
|
Create a simple MCP server in Python to understand the basics
|
||||||
</Card>
|
</Card>
|
||||||
<Card
|
<Card
|
||||||
title="Get Inspiration"
|
title="Integrate MCP (TypeScript)"
|
||||||
icon="stars"
|
icon="plug"
|
||||||
href="https://mintlify.com/customers"
|
href="/docs/guides/integrate-ts"
|
||||||
>
|
>
|
||||||
Check out our showcase of our favorite documentation
|
Add MCP support to your TypeScript/JavaScript LLM application
|
||||||
|
</Card>
|
||||||
|
<Card
|
||||||
|
title="Integrate MCP (Python)"
|
||||||
|
icon="plug"
|
||||||
|
href="/docs/guides/integrate-python"
|
||||||
|
>
|
||||||
|
Add MCP support to your Python LLM application
|
||||||
</Card>
|
</Card>
|
||||||
</CardGroup>
|
</CardGroup>
|
||||||
|
|
||||||
|
## Explore MCP
|
||||||
|
|
||||||
|
Dive deeper into MCP's core concepts and capabilities:
|
||||||
|
|
||||||
|
<CardGroup cols={3}>
|
||||||
|
<Card
|
||||||
|
title="Core Architecture"
|
||||||
|
icon="sitemap"
|
||||||
|
href="/core/architecture"
|
||||||
|
>
|
||||||
|
Understand how MCP connects clients, servers, and LLMs
|
||||||
|
</Card>
|
||||||
|
<Card
|
||||||
|
title="Resources"
|
||||||
|
icon="database"
|
||||||
|
href="/core/resources"
|
||||||
|
>
|
||||||
|
Expose data and content from your servers to LLMs
|
||||||
|
</Card>
|
||||||
|
<Card
|
||||||
|
title="Prompts"
|
||||||
|
icon="message"
|
||||||
|
href="/core/prompts"
|
||||||
|
>
|
||||||
|
Create reusable prompt templates and workflows
|
||||||
|
</Card>
|
||||||
|
<Card
|
||||||
|
title="Tools"
|
||||||
|
icon="wrench"
|
||||||
|
href="/core/tools"
|
||||||
|
>
|
||||||
|
Enable LLMs to perform actions through your server
|
||||||
|
</Card>
|
||||||
|
<Card
|
||||||
|
title="Sampling"
|
||||||
|
icon="robot"
|
||||||
|
href="/core/sampling"
|
||||||
|
>
|
||||||
|
Let your servers request completions from LLMs
|
||||||
|
</Card>
|
||||||
|
<Card
|
||||||
|
title="Transport"
|
||||||
|
icon="network-wired"
|
||||||
|
href="/core/transport"
|
||||||
|
>
|
||||||
|
Learn about MCP's communication mechanisms
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
||||||
|
## Example Use Cases
|
||||||
|
|
||||||
|
See what you can build with MCP:
|
||||||
|
|
||||||
|
<CardGroup cols={2}>
|
||||||
|
<Card
|
||||||
|
title="Custom Knowledge Base"
|
||||||
|
icon="book"
|
||||||
|
>
|
||||||
|
Connect your documentation, wikis, and internal knowledge bases to LLMs
|
||||||
|
</Card>
|
||||||
|
<Card
|
||||||
|
title="Database Integration"
|
||||||
|
icon="database"
|
||||||
|
>
|
||||||
|
Query databases and analyze results with natural language
|
||||||
|
</Card>
|
||||||
|
<Card
|
||||||
|
title="Local Development"
|
||||||
|
icon="code"
|
||||||
|
>
|
||||||
|
Build AI-powered development tools with access to local files and git
|
||||||
|
</Card>
|
||||||
|
<Card
|
||||||
|
title="Enterprise Systems"
|
||||||
|
icon="building"
|
||||||
|
>
|
||||||
|
Securely connect enterprise tools and data to AI interfaces
|
||||||
|
</Card>
|
||||||
|
</CardGroup>
|
||||||
|
|
||||||
|
## Security and Best Practices
|
||||||
|
|
||||||
|
MCP is designed with security in mind, but requires careful implementation:
|
||||||
|
|
||||||
|
<Card
|
||||||
|
title="Security Guide"
|
||||||
|
icon="shield"
|
||||||
|
href="/security"
|
||||||
|
>
|
||||||
|
Learn about MCP's security model, best practices, and implementation guidelines
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
Want to contribute? Check out our [GitHub repository](https://github.com/modelcontextprotocol) or join our growing community of developers building with MCP.
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue