diff --git a/docs/core/architecture.mdx b/docs/concepts/architecture.mdx similarity index 100% rename from docs/core/architecture.mdx rename to docs/concepts/architecture.mdx diff --git a/docs/core/prompts.mdx b/docs/concepts/prompts.mdx similarity index 100% rename from docs/core/prompts.mdx rename to docs/concepts/prompts.mdx diff --git a/docs/core/resources.mdx b/docs/concepts/resources.mdx similarity index 100% rename from docs/core/resources.mdx rename to docs/concepts/resources.mdx diff --git a/docs/core/sampling.mdx b/docs/concepts/sampling.mdx similarity index 100% rename from docs/core/sampling.mdx rename to docs/concepts/sampling.mdx diff --git a/docs/core/tools.mdx b/docs/concepts/tools.mdx similarity index 100% rename from docs/core/tools.mdx rename to docs/concepts/tools.mdx diff --git a/docs/core/transport.mdx b/docs/concepts/transport.mdx similarity index 100% rename from docs/core/transport.mdx rename to docs/concepts/transport.mdx diff --git a/docs/first-server/python.mdx b/docs/first-server/python.mdx new file mode 100644 index 0000000..a551d5f --- /dev/null +++ b/docs/first-server/python.mdx @@ -0,0 +1,750 @@ +--- +title: "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. + + +This guide uses the OpenWeatherMap API. You'll need a free API key from [OpenWeatherMap](https://openweathermap.org/api) to follow along. + + +## Prerequisites + + + + You'll need Python 3.9 or higher: + + ```bash + python --version # Should be 3.9 or higher + pip --version + ``` + + + + ```bash + mkdir weather-server + cd weather-server + python -m venv venv + source venv/bin/activate # On Windows use: venv\Scripts\activate + ``` + + + + ```bash + pip install modelcontextprotocol requests python-dotenv + ``` + + + + Create `.env`: + ```bash + OPENWEATHER_API_KEY=your-api-key-here + ``` + + + +## Create your server + + + + ```bash + touch weather_server.py + ``` + + + + In `weather_server.py`: + + ```python + import os + import json + from datetime import datetime + from typing import Any, Dict, List, Optional + + import requests + from dotenv import load_dotenv + from modelcontextprotocol import Server, StdioServerTransport + from modelcontextprotocol.types import ( + ListResourcesRequest, + ReadResourceRequest, + ListToolsRequest, + CallToolRequest, + ErrorCode, + McpError + ) + + load_dotenv() + + API_KEY = os.getenv("OPENWEATHER_API_KEY") + if not API_KEY: + raise ValueError("OPENWEATHER_API_KEY environment variable is required") + + API_CONFIG = { + "BASE_URL": "http://api.openweathermap.org/data/2.5", + "DEFAULT_CITY": "San Francisco", + "ENDPOINTS": { + "CURRENT": "weather", + "FORECAST": "forecast" + } + } + + class WeatherServer: + def __init__(self): + self.server = Server( + name="example-weather-server", + version="0.1.0" + ) + + self.session = requests.Session() + self.session.params = { + "appid": API_KEY, + "units": "metric" + } + + self.setup_handlers() + self.setup_error_handling() + + def setup_error_handling(self) -> None: + def on_error(error: Exception) -> None: + print("[MCP Error]", error, flush=True) + + self.server.onerror = on_error + + def setup_handlers(self) -> None: + self.server.set_request_handler( + ListResourcesRequest, self.handle_list_resources) + self.server.set_request_handler( + ReadResourceRequest, self.handle_read_resource) + self.server.set_request_handler( + ListToolsRequest, self.handle_list_tools) + self.server.set_request_handler( + CallToolRequest, self.handle_call_tool) + + async def run(self) -> None: + transport = StdioServerTransport() + await self.server.connect(transport) + print("Weather MCP server running on stdio", flush=True) + + # Handler implementations will continue in next sections +``` + + + + Add these methods to the `WeatherServer` class: + + ```python + async def handle_list_resources( + self, + request: ListResourcesRequest + ) -> Dict[str, Any]: + return { + "resources": [{ + "uri": f"weather://{API_CONFIG['DEFAULT_CITY']}/current", + "name": f"Current weather in {API_CONFIG['DEFAULT_CITY']}", + "mimeType": "application/json", + "description": "Real-time weather data including temperature, conditions, humidity, and wind speed" + }] + } + + async def handle_read_resource( + self, + request: ReadResourceRequest + ) -> Dict[str, Any]: + city = API_CONFIG["DEFAULT_CITY"] + if request.params.uri != f"weather://{city}/current": + raise McpError( + ErrorCode.INVALID_REQUEST, + f"Unknown resource: {request.params.uri}" + ) + + try: + response = self.session.get( + f"{API_CONFIG['BASE_URL']}/{API_CONFIG['ENDPOINTS']['CURRENT']}", + params={"q": city} + ) + response.raise_for_status() + data = response.json() + + weather_data = { + "temperature": data["main"]["temp"], + "conditions": data["weather"][0]["description"], + "humidity": data["main"]["humidity"], + "wind_speed": data["wind"]["speed"], + "timestamp": datetime.now().isoformat() + } + + return { + "contents": [{ + "uri": request.params.uri, + "mimeType": "application/json", + "text": json.dumps(weather_data, indent=2) + }] + } + except requests.RequestException as e: + raise McpError( + ErrorCode.INTERNAL_ERROR, + f"Weather API error: {str(e)}" + ) + ``` + + + + Add these methods to the `WeatherServer` class: + + ```python + async def handle_list_tools( + self, + request: ListToolsRequest + ) -> Dict[str, Any]: + 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)", + "minimum": 1, + "maximum": 5 + } + }, + "required": ["city"] + } + }] + } + + async def handle_call_tool( + self, + request: CallToolRequest + ) -> Dict[str, Any]: + if request.params.name != "get_forecast": + raise McpError( + ErrorCode.METHOD_NOT_FOUND, + f"Unknown tool: {request.params.name}" + ) + + args = request.params.arguments + if not isinstance(args, dict) or "city" not in args: + raise McpError( + ErrorCode.INVALID_PARAMS, + "Invalid forecast arguments" + ) + + city = args["city"] + days = min(int(args.get("days", 3)), 5) + + try: + response = self.session.get( + f"{API_CONFIG['BASE_URL']}/{API_CONFIG['ENDPOINTS']['FORECAST']}", + params={ + "q": city, + "cnt": days * 8 # API returns 3-hour intervals + } + ) + response.raise_for_status() + data = response.json() + + forecasts = [] + for i in range(0, len(data["list"]), 8): + 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} + except requests.RequestException as e: + raise McpError( + ErrorCode.INTERNAL_ERROR, + f"Weather API error: {str(e)}" + ) + ``` + + + + Add this to the end of `weather_server.py`: + + ```python + if __name__ == "__main__": + import asyncio + import signal + + async def main(): + server = WeatherServer() + + def signal_handler(sig, frame): + print("Shutting down...", flush=True) + asyncio.create_task(server.server.close()) + + signal.signal(signal.SIGINT, signal_handler) + await server.run() + + asyncio.run(main()) + ``` + + + + ```bash + chmod +x weather_server.py + pip install -e . + ``` + + Create `setup.py`: + ```python + from setuptools import setup + + setup( + name="weather-server", + version="0.1.0", + py_modules=["weather_server"], + entry_points={ + "console_scripts": [ + "weather-server=weather_server:main", + ], + }, + install_requires=[ + "modelcontextprotocol", + "requests", + "python-dotenv" + ], + ) + ``` + + + +## Understanding the code + + + + ```python + async def handle_read_resource( + self, + request: ReadResourceRequest + ) -> Dict[str, Any]: + # ... + ``` + Python type hints help catch errors early and improve code maintainability. + + + + ```python + async def handle_list_resources( + self, + request: ListResourcesRequest + ) -> Dict[str, Any]: + return { + "resources": [{ + "uri": f"weather://{DEFAULT_CITY}/current", + "name": f"Current weather in {DEFAULT_CITY}", + "mimeType": "application/json" + }] + } + ``` + Resources provide data that Claude can access as context. + + + + ```python + { + "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 validated inputs. + + + +## Best practices + + + + ```python + try: + response = self.session.get(...) + response.raise_for_status() + except requests.RequestException as e: + raise McpError( + ErrorCode.INTERNAL_ERROR, + f"API error: {str(e)}" + ) + ``` + + + + ```python + if not isinstance(args, dict) or "city" not in args: + raise McpError( + ErrorCode.INVALID_PARAMS, + "Invalid forecast arguments" + ) + ``` + + + + ```python + if not API_KEY: + raise ValueError("OPENWEATHER_API_KEY is required") + ``` + + + + ```python + API_CONFIG = { + "BASE_URL": "...", + "ENDPOINTS": { + "CURRENT": "weather", + "FORECAST": "forecast" + } + } + ``` + + + +## Available Transports + +While this guide uses stdio transport, MCP supports multiple transport options: + +### WebSocket +```python +from modelcontextprotocol import WebSocketServerTransport +transport = WebSocketServerTransport() +``` + +### SSE (Server-Sent Events) +```python +from modelcontextprotocol import SSEServerTransport +transport = SSEServerTransport("/events", response) +``` + +## Troubleshooting + +### Installation Issues +```bash +# Check Python version +python --version + +# Reinstall dependencies +pip uninstall -r requirements.txt +pip install -r requirements.txt +``` + +### Runtime Errors +```bash +# Enable debug logging +export MCP_LOG_LEVEL=DEBUG +python weather_server.py +``` + +### Type Checking +```bash +# Install mypy +pip install mypy + +# Run type checker +mypy weather_server.py +``` + +## Connect to Claude Desktop + + + + Add to `claude_desktop_config.json`: + + ```json + { + "mcpServers": { + "weather": { + "command": "weather-server", + "env": { + "OPENWEATHER_API_KEY": "your-api-key", + } + } + } + } + ``` + + + + 1. Quit Claude completely + 2. Start Claude again + 3. Look for your weather server in the 🔌 menu + + + +## Try it out! + + + + Ask Claude: + ``` + What's the current weather in San Francisco? Can you analyze the conditions and tell me if it's a good day for outdoor activities? + ``` + + + + Ask Claude: + ``` + Can you get me a 5-day forecast for Tokyo and help me plan what clothes to pack for my trip? + ``` + + + + Ask Claude: + ``` + Can you analyze the forecast for both Tokyo and San Francisco and tell me which city would be better for outdoor photography this week? + ``` + + + +## Advanced Features + + + + ```python + from functools import lru_cache + from datetime import datetime, timedelta + + class WeatherServer: + def __init__(self): + self._cache_timeout = timedelta(minutes=15) + self._last_cache_time = None + self._cached_weather = None + + @lru_cache(maxsize=32) + async def _fetch_weather(self, city: str) -> Dict[str, Any]: + # Cache expires after 15 minutes + now = datetime.now() + if (self._cached_weather is None or + self._last_cache_time is None or + now - self._last_cache_time > self._cache_timeout): + + response = self.session.get(...) + self._cached_weather = response.json() + self._last_cache_time = now + + return self._cached_weather + ``` + + + + ```python + async def handle_call_tool( + self, + request: CallToolRequest + ) -> Dict[str, Any]: + if progress_token := request.params._meta.get("progressToken"): + # Send progress notifications + await self.server.notification({ + "method": "notifications/progress", + "params": { + "progress": 1, + "total": 2, + "progressToken": progress_token + } + }) + + # Fetch data... + + await self.server.notification({ + "method": "notifications/progress", + "params": { + "progress": 2, + "total": 2, + "progressToken": progress_token + } + }) + ``` + + + + ```python + import logging + + class WeatherServer: + def __init__(self): + self.logger = logging.getLogger("weather-server") + self.logger.setLevel(logging.INFO) + + # Set up logging handler + self.server.set_request_handler( + SetLevelRequest, + self.handle_set_log_level + ) + + async def handle_set_log_level( + self, + request: SetLevelRequest + ) -> Dict[str, Any]: + level = request.params.level.upper() + self.logger.setLevel(getattr(logging, level)) + + await self.server.notification({ + "method": "notifications/message", + "params": { + "level": "info", + "logger": "weather-server", + "data": f"Log level set to {level}" + } + }) + return {} + ``` + + + + ```python + async def handle_list_resources( + self, + request: ListResourcesRequest + ) -> Dict[str, Any]: + return { + "resources": [...], + "resourceTemplates": [{ + "uriTemplate": "weather://{city}/current", + "name": "Current weather for any city", + "mimeType": "application/json" + }] + } + ``` + + + +## Testing + + + + Create `test_weather_server.py`: + + ```python + import pytest + from unittest.mock import Mock, patch + from weather_server import WeatherServer + + @pytest.fixture + async def server(): + server = WeatherServer() + yield server + await server.server.close() + + @pytest.mark.asyncio + async def test_list_resources(server): + request = Mock() + result = await server.handle_list_resources(request) + assert len(result["resources"]) > 0 + assert result["resources"][0]["mimeType"] == "application/json" + + @pytest.mark.asyncio + async def test_get_forecast(): + with patch("requests.Session") as mock_session: + mock_session.return_value.get.return_value.json.return_value = { + "list": [{ + "dt_txt": "2024-01-01 12:00:00", + "main": {"temp": 20}, + "weather": [{"description": "sunny"}] + }] + } + + server = WeatherServer() + request = Mock() + request.params.name = "get_forecast" + request.params.arguments = {"city": "Tokyo", "days": 1} + + result = await server.handle_call_tool(request) + assert len(result["toolResult"]) == 1 + assert result["toolResult"][0]["temperature"] == 20 + ``` + + + + ```bash + pip install pytest pytest-asyncio + pytest test_weather_server.py -v + ``` + + + +## Deployment + + + + ```dockerfile + FROM python:3.11-slim + + WORKDIR /app + COPY requirements.txt . + RUN pip install -r requirements.txt + + COPY . . + + CMD ["python", "weather_server.py"] + ``` + + + + ```ini + [Unit] + Description=Weather MCP Server + After=network.target + + [Service] + ExecStart=/path/to/venv/bin/python /path/to/weather_server.py + Environment=OPENWEATHER_API_KEY=your-key-here + Restart=always + + [Install] + WantedBy=multi-user.target + ``` + + + +## Next steps + + + + Learn more about the MCP architecture + + + + Read more about the Python SDK + + \ No newline at end of file diff --git a/docs/first-server/typescript.mdx b/docs/first-server/typescript.mdx new file mode 100644 index 0000000..ecd06d8 --- /dev/null +++ b/docs/first-server/typescript.mdx @@ -0,0 +1,631 @@ +--- +title: "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. + + +This guide uses the OpenWeatherMap API. You'll need a free API key from [OpenWeatherMap](https://openweathermap.org/api) to follow along. + + +## Prerequisites + + + + You'll need Node.js 18 or higher: + + ```bash + node --version # Should be v18 or higher + npm --version + ``` + + + + ```bash + mkdir weather-server + cd weather-server + npm init -y + ``` + + + + ```bash + npm install @modelcontextprotocol/sdk axios dotenv typescript @types/node + npm install --save-dev ts-node + ``` + + + + Create `tsconfig.json`: + ```json + { + "compilerOptions": { + "target": "ES2020", + "module": "ES2020", + "moduleResolution": "node", + "esModuleInterop": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true + }, + "include": ["src/**/*"] + } + ``` + + + + Create `.env`: + ```bash + OPENWEATHER_API_KEY=your-api-key-here + ``` + + + +## Create your server + + + + ```bash + mkdir src + touch src/index.ts + touch src/types.ts + ``` + + + + In `src/types.ts`: + + ```typescript + export interface OpenWeatherResponse { + main: { + temp: number; + humidity: number; + }; + weather: Array<{ + description: string; + }>; + wind: { + speed: number; + }; + dt_txt?: string; + } + + 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; + } + + // Type guard for forecast arguments + export function isValidForecastArgs(args: unknown): args is GetForecastArgs { + return ( + typeof args === "object" && + args !== null && + "city" in args && + typeof args.city === "string" && + (args.days === undefined || typeof args.days === "number") + ); + } + ``` + + + + In `src/index.ts`: + + ```typescript + import { Server } from "@modelcontextprotocol/sdk/server/index.js"; + import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; + import { + ListResourcesRequestSchema, + ReadResourceRequestSchema, + ListToolsRequestSchema, + CallToolRequestSchema, + ErrorCode, + McpError + } from "@modelcontextprotocol/sdk/types.js"; + import axios from "axios"; + import dotenv from "dotenv"; + import { + WeatherData, + ForecastDay, + GetForecastArgs, + OpenWeatherResponse, + isValidForecastArgs + } from "./types.js"; + + dotenv.config(); + + const API_KEY = process.env.OPENWEATHER_API_KEY; + if (!API_KEY) { + throw new Error("OPENWEATHER_API_KEY environment variable is required"); + } + + const API_CONFIG = { + BASE_URL: 'http://api.openweathermap.org/data/2.5', + DEFAULT_CITY: 'San Francisco', + ENDPOINTS: { + CURRENT: 'weather', + FORECAST: 'forecast' + } + } as const; + + class WeatherServer { + private server: Server; + private axiosInstance; + + constructor() { + this.server = new Server({ + name: "example-weather-server", + version: "0.1.0" + }); + + // Configure axios with defaults + this.axiosInstance = axios.create({ + baseURL: API_CONFIG.BASE_URL, + params: { + appid: API_KEY, + units: "metric" + } + }); + + this.setupHandlers(); + this.setupErrorHandling(); + } + + private setupErrorHandling(): void { + this.server.onerror = (error) => { + console.error("[MCP Error]", error); + }; + + process.on('SIGINT', async () => { + await this.server.close(); + process.exit(0); + }); + } + + private setupHandlers(): void { + this.setupResourceHandlers(); + this.setupToolHandlers(); + } + + private setupResourceHandlers(): void { + // Implementation continues in next section + } + + private setupToolHandlers(): void { + // Implementation continues in next section + } + + async run(): Promise { + const transport = new StdioServerTransport(); + await this.server.connect(transport); + console.log("Weather MCP server running on stdio"); + } + } + + const server = new WeatherServer(); + server.run().catch(console.error); + ``` + + + + Add this to the `setupHandlers` method: + + ```typescript + private setupResourceHandlers(): void { + this.server.setRequestHandler( + ListResourcesRequestSchema, + async () => ({ + resources: [{ + uri: `weather://${API_CONFIG.DEFAULT_CITY}/current`, + name: `Current weather in ${API_CONFIG.DEFAULT_CITY}`, + mimeType: "application/json", + description: "Real-time weather data including temperature, conditions, humidity, and wind speed" + }] + }) + ); + + this.server.setRequestHandler( + ReadResourceRequestSchema, + async (request) => { + const city = API_CONFIG.DEFAULT_CITY; + if (request.params.uri !== `weather://${city}/current`) { + throw new McpError( + ErrorCode.InvalidRequest, + `Unknown resource: ${request.params.uri}` + ); + } + + try { + const response = await this.axiosInstance.get( + API_CONFIG.ENDPOINTS.CURRENT, + { + params: { q: city } + } + ); + + 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) + }] + }; + } catch (error) { + if (axios.isAxiosError(error)) { + throw new McpError( + ErrorCode.InternalError, + `Weather API error: ${error.response?.data.message ?? error.message}` + ); + } + throw error; + } + } + ); + } + ``` + + + + Add these handlers to the `setupHandlers` method: + + ```typescript + private setupToolHandlers(): void { + 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)", + minimum: 1, + maximum: 5 + } + }, + required: ["city"] + } + }] + }) + ); + + this.server.setRequestHandler( + CallToolRequestSchema, + async (request) => { + if (request.params.name !== "get_forecast") { + throw new McpError( + ErrorCode.MethodNotFound, + `Unknown tool: ${request.params.name}` + ); + } + + if (!isValidForecastArgs(request.params.arguments)) { + throw new McpError( + ErrorCode.InvalidParams, + "Invalid forecast arguments" + ); + } + + const city = request.params.arguments.city; + const days = Math.min(request.params.arguments.days || 3, 5); + + try { + const response = await this.axiosInstance.get<{ + list: OpenWeatherResponse[] + }>(API_CONFIG.ENDPOINTS.FORECAST, { + params: { + q: city, + 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] ?? new Date().toISOString().split('T')[0], + temperature: dayData.main.temp, + conditions: dayData.weather[0].description + }); + } + + return { toolResult: forecasts }; + } catch (error) { + if (axios.isAxiosError(error)) { + throw new McpError( + ErrorCode.InternalError, + `Weather API error: ${error.response?.data.message ?? error.message}` + ); + } + throw error; + } + } + ); + } + ``` + + + + 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 install -g . + ``` + + + +## Connect to Claude Desktop + + + + Add to `claude_desktop_config.json`: + + ```json + { + "mcpServers": { + "weather": { + "command": "weather-server", + "env": { + "OPENWEATHER_API_KEY": "your-api-key", + } + } + } + } + ``` + + + + 1. Quit Claude completely + 2. Start Claude again + 3. Look for your weather server in the 🔌 menu + + + +## Try it out! + + + + Ask Claude: + ``` + What's the current weather in San Francisco? Can you analyze the conditions? + ``` + + + + Ask Claude: + ``` + Can you get me a 5-day forecast for Tokyo and tell me if I should pack an umbrella? + ``` + + + + Ask Claude: + ``` + Can you analyze the forecast for both Tokyo and San Francisco and tell me which city will be warmer this week? + ``` + + + +## Understanding the code + + + + ```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. + + + + ```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. + + + + ```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. + + + +## Best practices + + + + ```typescript + try { + const response = await this.axiosInstance.get(...); + } catch (error) { + if (axios.isAxiosError(error)) { + throw new McpError( + ErrorCode.InternalError, + `API error: ${error.response?.data.message}` + ); + } + throw error; + } + ``` + + + + ```typescript + function isValidForecastArgs(args: unknown): args is GetForecastArgs { + return ( + typeof args === "object" && + args !== null && + "city" in args && + typeof args.city === "string" + ); + } + ``` + + + + ```typescript + if (!process.env.OPENWEATHER_API_KEY) { + throw new Error("OPENWEATHER_API_KEY is required"); + } + ``` + + + + ```typescript + const API_CONFIG = { + BASE_URL: '...', + ENDPOINTS: { + CURRENT: 'weather', + FORECAST: 'forecast' + } + } as const; + ``` + + + +## Available Transports + +While this guide uses stdio transport, MCP supports multiple transport options: + +### WebSocket +```typescript +import { WebSocketServerTransport } from "@modelcontextprotocol/sdk/server/websocket.js"; +const transport = new WebSocketServerTransport(); +``` + +### SSE (Server-Sent Events) +```typescript +import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; +const transport = new SSEServerTransport("/events", response); +``` + +## Troubleshooting + +### Build Errors +```bash +# Check TypeScript version +npx tsc --version + +# Clean and rebuild +rm -rf dist/ +npm run build +``` + +### Runtime Errors +Look for detailed error messages in the Claude app logs: +```bash +# Launch Claude with logging +open /Applications/Claude.app --stdout ~/Claude.log --stderr ~/Claude.log + +# Monitor logs +tail -f ~/Claude.log +``` + +### Type Errors +```bash +# Check types without building +npx tsc --noEmit +``` + +## Next steps + + + + Learn more about the MCP architecture + + + + Read more about the TypeScript SDK + + + + +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. + \ No newline at end of file diff --git a/docs/guides/first-server-python.mdx b/docs/guides/first-server-python.mdx deleted file mode 100644 index 3068ccb..0000000 --- a/docs/guides/first-server-python.mdx +++ /dev/null @@ -1,422 +0,0 @@ ---- -title: "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. - - -This guide uses the OpenWeatherMap API. You'll need a free API key from [OpenWeatherMap](https://openweathermap.org/api) to follow along. - - -## Prerequisites - - - - You'll need Python 3.8 or higher: - - ```bash - python3 --version # Should be 3.8 or higher - ``` - - - - ```bash - # Create and activate virtual environment - python3 -m venv mcp-weather - cd mcp-weather - source bin/activate # On Windows: .\Scripts\activate - ``` - - - - ```bash - pip install modelcontextprotocol-sdk requests - ``` - - - - 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' - ``` - - - -## Create your server - - - - ```bash - mkdir weather_server - cd weather_server - touch weather_server.py - ``` - - - - 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()) - ``` - - - - 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) - }] - } - ``` - - - - 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} - ``` - - - - 1. Save all changes - 2. Run your server: - ```bash - python weather_server.py - ``` - - - -## Connect to Claude - - - - ```bash - # From your project directory - pip install -e . - ``` - - - - 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" - } - } - } - } - ``` - - - - 1. Quit Claude completely - 2. Start Claude again - 3. Verify your weather server appears in the 🔌 menu - - - -## Try it out! - - - - 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 - - - - 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 - - - -## Understanding the code - -Let's break down the key MCP concepts used in this server: - - - - ```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. - - - - ```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. - - - - ```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. - - - -## Next steps - -Now that you've built your first MCP server, try these enhancements: - - - - - Air quality data - - Weather alerts - - Historical weather - - - - - Compare multiple cities - - Get detailed hourly forecasts - - Save favorite locations - - - - - Handle API errors gracefully - - Add input validation - - Rate limiting - - - - - Weather alerts - - Temperature changes - - Condition updates - - - -## Troubleshooting - - - - ```bash - pip install modelcontextprotocol-sdk --upgrade - ``` - Ensure you're using the latest SDK version. - - - - 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 - - - - 1. Check Claude's logs: - ```bash - tail -f ~/Claude.log - ``` - 2. Verify your config JSON syntax - 3. Ensure Python path is correct in config - - - - -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. - \ No newline at end of file diff --git a/docs/guides/first-server-ts.mdx b/docs/guides/first-server-ts.mdx deleted file mode 100644 index 2615856..0000000 --- a/docs/guides/first-server-ts.mdx +++ /dev/null @@ -1,533 +0,0 @@ ---- -title: "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. - - -This guide uses the OpenWeatherMap API. You'll need a free API key from [OpenWeatherMap](https://openweathermap.org/api) to follow along. - - -## Prerequisites - - - - You'll need Node.js 16 or higher: - - ```bash - node --version # Should be v16 or higher - npm --version - ``` - - - - ```bash - mkdir weather-server - cd weather-server - npm init -y - ``` - - - - ```bash - npm install @modelcontextprotocol/sdk axios dotenv typescript @types/node - npm install --save-dev ts-node - ``` - - - - Create `tsconfig.json`: - ```json - { - "compilerOptions": { - "target": "ES2020", - "module": "ES2020", - "moduleResolution": "node", - "esModuleInterop": true, - "outDir": "./dist", - "rootDir": "./src", - "strict": true - }, - "include": ["src/**/*"] - } - ``` - - - - Create `.env`: - ```bash - OPENWEATHER_API_KEY=your-api-key-here - ``` - - - -## Create your server - - - - ```bash - mkdir src - touch src/index.ts - touch src/types.ts - ``` - - - - 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; - } - ``` - - - - 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 { - const transport = new StdioServerTransport(); - await this.server.connect(transport); - } - } - - const server = new WeatherServer(); - server.run().catch(console.error); - ``` - - - - 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) - }] - }; - } - ); - } - ``` - - - - 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 }; - } - ); - ``` - - - - 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 - ``` - - - -## Connect to Claude - - - - ```bash - npm install -g - ``` - - - - Add to `claude_desktop_config.json`: - - ```json - { - "mcpServers": { - "weather": { - "command": "weather-server", - "env": { - "OPENWEATHER_API_KEY": "your-api-key", - "HOME": "/Users/YOUR_USERNAME" - } - } - } - } - ``` - - - - 1. Quit Claude completely - 2. Start Claude again - 3. Look for your weather server in the 🔌 menu - - - -## Try it out! - - - - Ask Claude: - ``` - What's the current weather in San Francisco? Can you analyze the conditions? - ``` - - - - Ask Claude: - ``` - Can you get me a 5-day forecast for Tokyo and tell me if I should pack an umbrella? - ``` - - - - Ask Claude: - ``` - Can you analyze the forecast for both Tokyo and San Francisco and tell me which city will be warmer this week? - ``` - - - -## Understanding the code - - - - ```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. - - - - ```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. - - - - ```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. - - - -## Best practices - - - - ```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; - } - ``` - - - - ```typescript - function isValidForecastArgs(args: unknown): args is GetForecastArgs { - return typeof args === "object" && args !== null && - "city" in args; - } - ``` - - - - ```typescript - if (!process.env.OPENWEATHER_API_KEY) { - throw new Error("OPENWEATHER_API_KEY is required"); - } - ``` - - - - ```typescript - const API_ENDPOINTS = { - CURRENT: "weather", - FORECAST: "forecast" - } as const; - ``` - - - -## Enhancements - -Try adding these features to your server: - - - - Add dynamic city resources: - ```typescript - uriTemplate: "weather://{city}/current" - ``` - - - - Cache API responses: - ```typescript - import NodeCache from "node-cache" - ``` - - - - Add API rate limiting: - ```typescript - import rateLimit from "axios-rate-limit" - ``` - - - -## Troubleshooting - - - - ```bash - # Check TypeScript version - npx tsc --version - - # Clean and rebuild - rm -rf dist/ - npm run build - ``` - - - - Add better error logging: - ```typescript - server.onerror = (error) => { - console.error("[MCP Error]", error); - }; - ``` - - - - ```bash - # Check types without building - npx tsc --noEmit - ``` - - - -## Next steps - - - - Switch to real-time updates with WebSocket transport - - - - Add Jest tests for your MCP server - - - - -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. - \ No newline at end of file diff --git a/docs/guides/integrating-with-llm.mdx b/docs/guides/integrating-with-llm.mdx deleted file mode 100644 index a801e53..0000000 --- a/docs/guides/integrating-with-llm.mdx +++ /dev/null @@ -1,385 +0,0 @@ ---- -title: "Integrating MCP into Your LLM Application" -description: 'Learn how to integrate the Model Context Protocol (MCP) into your LLM-powered applications' ---- - -The Model Context Protocol (MCP) enables seamless integration between LLM applications and various data sources and tools. This guide will walk you through integrating MCP into your own LLM-powered chatbot application. - -## Our Example Chatbot - -For this guide, we'll be working with a hypothetical customer service chatbot for a software company. Our chatbot currently: - -- Uses an LLM API (like Anthropic's Claude API) to generate responses -- Maintains conversation history in memory -- Has basic error handling and retry logic -- Runs as a web service that customers interact with through a REST API - -However, our chatbot faces some limitations: -- It can't access real-time product documentation or knowledge base articles -- It can't look up customer information or ticket history -- It can't perform actions like creating tickets or updating customer records -- It doesn't stay up-to-date with product changes and new features - -By integrating MCP, we'll enhance our chatbot to: -- Access documentation and knowledge base articles as contextual resources -- Look up customer information through a CRM integration -- Create and update support tickets through a ticketing system integration -- Get real-time updates when documentation or product information changes - -Let's get started! - -## Prerequisites - -1. Clone the example MCP servers: -```bash -git clone https://github.com/modelcontextprotocol/example-servers.git -cd example-servers -npm install -``` - -2. Install the MCP SDK in your chatbot project: -```bash -npm install @modelcontextprotocol/sdk -``` - -## Starting Point: Our Basic Chatbot - -Here's our starting chatbot implementation that we'll enhance with MCP: - -```typescript -interface Message { - role: 'user' | 'assistant'; - content: string; -} - -class CustomerServiceBot { - private conversation: Message[] = []; - private llmClient: LLMClient; // Your LLM API client - - constructor(llmClient: LLMClient) { - this.llmClient = llmClient; - } - - async handleMessage(message: string): Promise { - // Add user message to conversation - this.conversation.push({ role: 'user', content: message }); - - // Get LLM response - const response = await this.llmClient.complete({ - messages: this.conversation, - maxTokens: 1000 - }); - - // Add assistant response to conversation - this.conversation.push({ role: 'assistant', content: response }); - - return response; - } -} -``` - -## Step 1: Adding Documentation Context - -First, let's enhance our chatbot to incorporate product documentation. We'll use the example PostgreSQL server which can store and serve our docs. - -```typescript -import { Client } from "@modelcontextprotocol/sdk/client"; -import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio"; -import { - ListResourcesResultSchema, - ReadResourceResultSchema -} from "@modelcontextprotocol/sdk/types"; - -class EnhancedCustomerServiceBot { - private conversation: Message[] = []; - private llmClient: LLMClient; - private mcpClient: Client; - - constructor(llmClient: LLMClient) { - this.llmClient = llmClient; - - // Initialize MCP client - this.mcpClient = new Client({ - name: "customer-service-bot", - version: "1.0.0" - }); - } - - async initialize() { - // Connect to PostgreSQL server for documentation - const transport = new StdioClientTransport({ - command: "./example-servers/postgres/index.js", - args: ["postgres://localhost/docs"] // Your docs database - }); - await this.mcpClient.connect(transport); - } - - async getRelevantDocs(message: string): Promise { - // List available documentation resources - const { resources } = await this.mcpClient.request( - { method: "resources/list" }, - ListResourcesResultSchema - ); - - // Read each documentation resource - const docs = await Promise.all( - resources.map(async (resource) => { - const { contents } = await this.mcpClient.request( - { - method: "resources/read", - params: { uri: resource.uri } - }, - ReadResourceResultSchema - ); - return contents[0].text; - }) - ); - - return docs.join("\n\n"); - } - - async handleMessage(message: string): Promise { - // Get relevant documentation - const docs = await this.getRelevantDocs(message); - - // Build prompt with documentation context - const prompt = ` - Here is relevant documentation about our product: - ${docs} - - Please use this information to help answer the customer's question. - - Customer: ${message} - `; - - // Add user message to conversation - this.conversation.push({ role: 'user', content: message }); - - // Get LLM response with documentation context - const response = await this.llmClient.complete({ - messages: [...this.conversation, { role: 'user', content: prompt }], - maxTokens: 1000 - }); - - // Add assistant response to conversation - this.conversation.push({ role: 'assistant', content: response }); - - return response; - } -} -``` - -## Step 2: Adding Ticket Management - -Now let's add the ability to create and update support tickets. We'll use the example tools server for this: - -```typescript -import { CallToolResultSchema } from "@modelcontextprotocol/sdk/types"; - -interface Ticket { - id: string; - subject: string; - description: string; - status: 'open' | 'closed'; -} - -class EnhancedCustomerServiceBot { - // ... previous code ... - - async createTicket(subject: string, description: string): Promise { - const result = await this.mcpClient.request( - { - method: "tools/call", - params: { - name: "create_ticket", - arguments: { - subject, - description - } - } - }, - CallToolResultSchema - ); - - return result.toolResult as Ticket; - } - - async handleMessage(message: string): Promise { - // Get relevant documentation - const docs = await this.getRelevantDocs(message); - - // Build prompt with documentation context and ticket creation ability - const prompt = ` - Here is relevant documentation about our product: - ${docs} - - You can create support tickets when needed using the createTicket function. - Only create tickets for actual technical issues that need follow-up. - - Customer: ${message} - `; - - // Get LLM response - const response = await this.llmClient.complete({ - messages: [...this.conversation, { role: 'user', content: prompt }], - maxTokens: 1000, - tools: [{ - name: 'createTicket', - description: 'Create a support ticket for technical issues', - parameters: { - subject: 'string', - description: 'string' - } - }] - }); - - // Check if the LLM wants to create a ticket - if (response.toolCalls?.length > 0) { - const call = response.toolCalls[0]; - const ticket = await this.createTicket( - call.arguments.subject, - call.arguments.description - ); - - // Add ticket creation to conversation - this.conversation.push({ - role: 'system', - content: `Created ticket #${ticket.id}: ${ticket.subject}` - }); - } - - // Add messages to conversation - this.conversation.push({ role: 'user', content: message }); - this.conversation.push({ role: 'assistant', content: response.content }); - - return response.content; - } -} -``` - -## Step 3: Real-time Documentation Updates - -Finally, let's make our chatbot aware of documentation changes in real-time: - -```typescript -class EnhancedCustomerServiceBot { - // ... previous code ... - - async initialize() { - // Connect to PostgreSQL server - const transport = new StdioClientTransport({ - command: "./example-servers/postgres/index.js", - args: ["postgres://localhost/docs"] - }); - await this.mcpClient.connect(transport); - - // Subscribe to documentation updates - const { resources } = await this.mcpClient.request( - { method: "resources/list" }, - ListResourcesResultSchema - ); - - for (const resource of resources) { - await this.mcpClient.request( - { - method: "resources/subscribe", - params: { uri: resource.uri } - }, - EmptyResultSchema - ); - } - - // Handle update notifications - this.mcpClient.setNotificationHandler( - ResourceUpdatedNotificationSchema, - async (notification) => { - console.log(`Documentation updated: ${notification.params.uri}`); - // Optionally clear any cached documentation - } - ); - } -} -``` - -## Using the Enhanced Chatbot - -Here's how to use our enhanced chatbot: - -```typescript -const bot = new EnhancedCustomerServiceBot(llmClient); -await bot.initialize(); - -// Example conversation -const response1 = await bot.handleMessage( - "How do I reset my password?" -); -// Bot responds with information from documentation - -const response2 = await bot.handleMessage( - "I can't log in even after resetting my password" -); -// Bot creates a ticket and responds with next steps - -``` - -## Error Handling - -Add robust error handling around MCP operations: - -```typescript -class EnhancedCustomerServiceBot { - // ... previous code ... - - async getRelevantDocs(message: string): Promise { - try { - const { resources } = await this.mcpClient.request( - { method: "resources/list" }, - ListResourcesResultSchema - ); - - const docs = await Promise.all( - resources.map(async (resource) => { - try { - const { contents } = await this.mcpClient.request( - { - method: "resources/read", - params: { uri: resource.uri } - }, - ReadResourceResultSchema - ); - return contents[0].text; - } catch (error) { - console.error(`Failed to read resource ${resource.uri}:`, error); - return ""; // Skip failed resources - } - }) - ); - - return docs.join("\n\n"); - } catch (error) { - console.error("Failed to list resources:", error); - return ""; // Fallback to no documentation - } - } -} -``` - -## Next Steps - -Now that you've enhanced your chatbot with MCP, you can: - -1. Add more integrations: - - Connect to your actual documentation system - - Integrate with your production ticketing system - - Add CRM integration for customer context - -2. Improve the implementation: - - Add better documentation relevance filtering - - Implement more sophisticated prompt engineering - - Add authentication and access control - - Add monitoring and logging - - Implement caching for frequently accessed resources - -3. Consider building custom MCP servers for your specific needs - -The example servers repository includes other servers you can experiment with, and you can build your own servers to expose custom functionality to your chatbot. \ No newline at end of file diff --git a/docs/guides/integrate-python.mdx b/docs/unused/integrate-python.mdx similarity index 99% rename from docs/guides/integrate-python.mdx rename to docs/unused/integrate-python.mdx index e84e9de..1ca1919 100644 --- a/docs/guides/integrate-python.mdx +++ b/docs/unused/integrate-python.mdx @@ -32,8 +32,8 @@ We'll build an "AI Web Analyzer" that: ```bash # Install example MCP servers - git clone https://github.com/modelcontextprotocol/example-servers - cd example-servers + git clone https://github.com/modelcontextprotocol/servers + cd servers npm install npm run build npm install -g diff --git a/docs/guides/integrate-ts.mdx b/docs/unused/integrate-ts.mdx similarity index 99% rename from docs/guides/integrate-ts.mdx rename to docs/unused/integrate-ts.mdx index fdb3a2e..45dc832 100644 --- a/docs/guides/integrate-ts.mdx +++ b/docs/unused/integrate-ts.mdx @@ -40,8 +40,8 @@ We'll build an "AI Web Analyzer" that: ```bash # Install example MCP servers - git clone https://github.com/modelcontextprotocol/example-servers - cd example-servers + git clone https://github.com/modelcontextprotocol/servers + cd servers npm install npm run build npm install -g diff --git a/favicon.svg b/favicon.svg index 6a32332..03d9f85 100644 --- a/favicon.svg +++ b/favicon.svg @@ -1,49 +1,12 @@ - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + diff --git a/introduction.mdx b/introduction.mdx index ce585d0..5783886 100644 --- a/introduction.mdx +++ b/introduction.mdx @@ -9,120 +9,79 @@ The Model Context Protocol (MCP) is an open protocol that enables seamless integ Choose the path that best fits your needs: - + The fastest way to see MCP in action - connect example servers to Claude Desktop - - Create a simple MCP server in TypeScript to understand the basics - Create a simple MCP server in Python to understand the basics + + Create a simple MCP server in TypeScript to understand the basics + ## Explore MCP Dive deeper into MCP's core concepts and capabilities: - + Understand how MCP connects clients, servers, and LLMs Expose data and content from your servers to LLMs Create reusable prompt templates and workflows Enable LLMs to perform actions through your server Let your servers request completions from LLMs Learn about MCP's communication mechanisms -## Example Use Cases - -See what you can build with MCP: - - - - Connect your documentation, wikis, and internal knowledge bases to LLMs - - - Query databases and analyze results with natural language - - - Build AI-powered development tools with access to local files and git - - - Securely connect enterprise tools and data to AI interfaces - - - -## Security and Best Practices - -MCP is designed with security in mind, but requires careful implementation: - - - Learn about MCP's security model, best practices, and implementation guidelines - +## Contributing Want to contribute? Check out our [GitHub repository](https://github.com/modelcontextprotocol) or join our growing community of developers building with MCP. \ No newline at end of file diff --git a/mint.json b/mint.json index b583f2d..3f2df25 100644 --- a/mint.json +++ b/mint.json @@ -47,24 +47,21 @@ "navigation": [ { "group": "Get Started", - "pages": ["introduction", "docs/guides/quickstart"] + "pages": ["introduction", "quickstart"] }, { "group": "Your First MCP Server", - "pages": [ - "docs/guides/first-server-python", - "docs/guides/first-server-ts" - ] + "pages": ["docs/first-server/python", "docs/first-server/typescript"] }, { "group": "Concepts", "pages": [ - "docs/core/architecture", - "docs/core/resources", - "docs/core/prompts", - "docs/core/tools", - "docs/core/sampling", - "docs/core/transport" + "docs/concepts/architecture", + "docs/concepts/resources", + "docs/concepts/prompts", + "docs/concepts/tools", + "docs/concepts/sampling", + "docs/concepts/transport" ] }, { @@ -123,8 +120,6 @@ } ], "footerSocials": { - "x": "https://x.com/mintlify", - "github": "https://github.com/mintlify", - "linkedin": "https://linkedin.com/company/mintlify" + "github": "https://github.com/modelcontextprotocol" } } diff --git a/docs/guides/quickstart.mdx b/quickstart.mdx similarity index 94% rename from docs/guides/quickstart.mdx rename to quickstart.mdx index 0cac428..99d2da2 100644 --- a/docs/guides/quickstart.mdx +++ b/quickstart.mdx @@ -98,8 +98,8 @@ brew install node git sqlite3 ```bash # Clone the example servers - git clone https://github.com/modelcontextprotocol/example-servers - cd example-servers + git clone https://github.com/modelcontextprotocol/servers + cd servers # Install dependencies and build npm install @@ -325,17 +325,12 @@ Each client may implement MCP features differently or support different capabili ## Next steps - - - Create your own MCP servers to give Claude Desktop new capabilities. + + + Create your own MCP servers to give your LLM clients new capabilities. - + Browse our collection of example servers to see what's possible. - - - Share your MCP creations and connect with other developers. - [Join us →](/community) - \ No newline at end of file diff --git a/specification/logging.mdx b/specification/logging.mdx index 4d91cfc..62e56b9 100644 --- a/specification/logging.mdx +++ b/specification/logging.mdx @@ -59,6 +59,7 @@ Common use cases for logging include: ### Transient errors +```json { "level": "error", "logger": "git_clone", @@ -73,6 +74,7 @@ Common use cases for logging include: } } } +``` ## Diagram