diff --git a/images/available-mcp-tools.png b/images/available-mcp-tools.png
new file mode 100644
index 0000000..89a2b9c
Binary files /dev/null and b/images/available-mcp-tools.png differ
diff --git a/images/current-weather.png b/images/current-weather.png
new file mode 100644
index 0000000..7b440f1
Binary files /dev/null and b/images/current-weather.png differ
diff --git a/images/quickstart-weather.png b/images/quickstart-weather.png
deleted file mode 100644
index cee70d2..0000000
Binary files a/images/quickstart-weather.png and /dev/null differ
diff --git a/images/time-server-quickstart.png b/images/time-server-quickstart.png
deleted file mode 100644
index f48263b..0000000
Binary files a/images/time-server-quickstart.png and /dev/null differ
diff --git a/images/visual-indicator-mcp-tools.png b/images/visual-indicator-mcp-tools.png
new file mode 100644
index 0000000..af71d07
Binary files /dev/null and b/images/visual-indicator-mcp-tools.png differ
diff --git a/images/weather-alerts.png b/images/weather-alerts.png
new file mode 100644
index 0000000..d234ac5
Binary files /dev/null and b/images/weather-alerts.png differ
diff --git a/quickstart.mdx b/quickstart.mdx
index 341ae62..734fa39 100644
--- a/quickstart.mdx
+++ b/quickstart.mdx
@@ -1,11 +1,9 @@
---
title: "Quickstart"
-description: "Get started with building your first MCP server and connecting it to an MCP host"
+description: "Get started with building your first MCP server and connecting it to a host"
---
-In this tutorial, we'll build a simple MCP server with a `get_weather` tool and connect it to a popular MCP host, Claude for Desktop. We'll start with a basic setup, and then progress to more complex use cases.
-
-You can think of MCP as a bridge between your LLMs and your data and tools. Model Context Protocol was developed by Anthropic (the makers of [Claude](https://claude.ai)), but MCP can work with any Large Language Model provider that supports tool calling.
+In this tutorial, we'll build a simple MCP weather server and connect it to a host, Claude for Desktop. We'll start with a basic setup, and then progress to more complex use cases.
### What is MCP?
@@ -18,13 +16,20 @@ MCP is an open protocol that standardizes how applications provide context to LL
### What we'll be building
-Many LLMs (including Claude) do not currently have the ability to fetch the current temperature and weather conditions. Let's use MCP to solve that!
+Many LLMs (including Claude) do not currently have the ability to fetch the forecast and severe weather alerts. Let's use MCP to solve that!
+
+We'll build a server that exposes two tools: `get-alerts` and `get-forecast`. Then we'll connect the server to an MCP host (in this case, Claude Desktop):
-
+
+
+
+
-Servers can connect to any host. We'll walk through using Claude Desktop as the host and eventually show you how to build your own.
+
+Servers can connect to any host. We've chosen Claude desktop here for simplicity, but we also have guides on [building your own client](/tutorials/building-a-client)
+
Because servers are locally run, MCP currently only supports desktop hosts. Remote hosts are in active development.
@@ -50,7 +55,7 @@ It also helps to be familiar with [tool use (aka function calling)](https://docs
### System requirements
-For Python, make sure you have the latest version of Python installed. For TypeScript, make sure you have the latest version of Node installed.
+For Python, make sure you have Python 3.9 or higher installed. For TypeScript, make sure you have the latest version of Node installed.
### Set up your environment
@@ -64,11 +69,14 @@ First, let's install `uv` and set up our Python project and environment:
curl -LsSf https://astral.sh/uv/install.sh | sh
```
-```bash Windows
+```powershell Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
```
+
+Make sure to restart your terminal afterwards to ensure that the `uv` command gets picked up.
+
First, let's install Node.js and npm if you haven't already. You can download them from [nodejs.org](https://nodejs.org/).
@@ -98,17 +106,16 @@ source .venv/bin/activate
# Install dependencies
uv pip install mcp httpx
-# Clean up boilerplate code
-rm -rf hello.py
+# Remove template file
+rm hello.py
# Create our files
-mkdir src
-mkdir src/weather
+mkdir -p src/weather
touch src/weather/__init__.py
touch src/weather/server.py
```
-```bash Windows
+```powershell Windows
# Create a new directory for our project
uv init mcp-quickstart
cd mcp-quickstart
@@ -121,13 +128,13 @@ uv venv
uv pip install mcp httpx
# Clean up boilerplate code
-del /f /q hello.py
+rm hello.py
# Create our files
md src
md src\weather
-echo.> src\__init__.py
-echo.> src\server.py
+new-item src\weather\__init__.py
+new-item src\weather\server.py
```
@@ -160,7 +167,9 @@ __all__ = ['main', 'server']
-```bash
+
+
+```bash MacOS/Linux
# Create a new directory for our project
mkdir mcp-quickstart
cd mcp-quickstart
@@ -169,23 +178,39 @@ cd mcp-quickstart
npm init -y
# Install dependencies
-npm install @modelcontextprotocol/sdk node-fetch
+npm install @modelcontextprotocol/sdk zod
npm install -D @types/node typescript
-```
-Then create a new file called `src/index.ts` in your project directory:
-
-```bash
+# Create our files
mkdir src
touch src/index.ts
```
+
+```powershell Windows
+# Create a new directory for our project
+md mcp-quickstart
+cd mcp-quickstart
+
+# Initialize a new npm project
+npm init -y
+
+# Install dependencies
+npm install @modelcontextprotocol/sdk zod
+npm install -D @types/node typescript
+
+# Create our files
+md src
+new-item src\index.ts
+```
+
+
Update your package.json to add type: "module" and a build script:
```json package.json
{
"type": "module",
"bin": {
- "mcp-quickstart": "./build/index.js"
+ "weather": "./build/index.js"
},
"scripts": {
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
@@ -196,7 +221,7 @@ Update your package.json to add type: "module" and a build script:
}
```
-Create a tsconfig.json:
+Create a `tsconfig.json` in the root of your project:
```json tsconfig.json
{
@@ -224,113 +249,563 @@ Now let's dive into building your server.
### Importing packages
-Add these to your server.py:
+
+
+Add these to the top of your `server.py`:
```python
from typing import Any
+
import httpx
from mcp.server.models import InitializationOptions
import mcp.types as types
from mcp.server import NotificationOptions, Server
import mcp.server.stdio
```
+
+
+Add these to the top of your `src/index.ts`:
+
+```typescript
+import { Server } from "@modelcontextprotocol/sdk/server/index.js";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import {
+ CallToolRequestSchema,
+ ListToolsRequestSchema,
+} from "@modelcontextprotocol/sdk/types.js";
+import { z } from "zod";
+```
+
+
### Setting up the instance
-Then add the cache, server instance and the base URL for the NWS API:
+
+
+Then initialize the server instance and the base URL for the NWS API:
```python
-# Cache weather data
-weather_cache: dict[str, Any] = {}
NWS_API_BASE = "https://api.weather.gov"
+USER_AGENT = "weather-app/1.0"
server = Server("weather")
```
+
+
+Then initialize the NWS API base URL, validation schemas, and server instance:
+
+```typescript
+const NWS_API_BASE = "https://api.weather.gov";
+const USER_AGENT = "weather-app/1.0";
+
+// Define Zod schemas for validation
+const AlertsArgumentsSchema = z.object({
+ state: z.string().length(2),
+});
+
+const ForecastArgumentsSchema = z.object({
+ latitude: z.number().min(-90).max(90),
+ longitude: z.number().min(-180).max(180),
+});
+
+// Create server instance
+const server = new Server(
+ {
+ name: "weather",
+ version: "1.0.0",
+ },
+ {
+ capabilities: {
+ tools: {},
+ },
+ }
+);
+```
+
+
### Implementing tool listing
+
+
We need to tell clients what tools are available. The `list_tools()` decorator registers this handler:
```python
@server.list_tools()
async def handle_list_tools() -> list[types.Tool]:
+ """
+ List available tools.
+ Each tool specifies its arguments using JSON Schema validation.
+ """
return [
types.Tool(
- name="get-weather",
- description="Get weather data for a location",
+ name="get-alerts",
+ description="Get weather alerts for a state",
inputSchema={
"type": "object",
"properties": {
- "latitude": {"type": "number"},
- "longitude": {"type": "number"},
+ "state": {
+ "type": "string",
+ "description": "Two-letter state code (e.g. CA, NY)",
+ },
+ },
+ "required": ["state"],
+ },
+ ),
+ types.Tool(
+ name="get-forecast",
+ description="Get weather forecast for a location",
+ inputSchema={
+ "type": "object",
+ "properties": {
+ "latitude": {
+ "type": "number",
+ "description": "Latitude of the location",
+ },
+ "longitude": {
+ "type": "number",
+ "description": "Longitude of the location",
+ },
},
"required": ["latitude", "longitude"],
},
- )
+ ),
]
-```
-This defines a single tool that requires latitude and longitude parameters.
+```
+
+
+We need to tell clients what tools are available. This `server.setRequestHandler` call will register this list for us:
+
+```typescript
+// List available tools
+server.setRequestHandler(ListToolsRequestSchema, async () => {
+ return {
+ tools: [
+ {
+ name: "get-alerts",
+ description: "Get weather alerts for a state",
+ inputSchema: {
+ type: "object",
+ properties: {
+ state: {
+ type: "string",
+ description: "Two-letter state code (e.g. CA, NY)",
+ },
+ },
+ required: ["state"],
+ },
+ },
+ {
+ name: "get-forecast",
+ description: "Get weather forecast for a location",
+ inputSchema: {
+ type: "object",
+ properties: {
+ latitude: {
+ type: "number",
+ description: "Latitude of the location",
+ },
+ longitude: {
+ type: "number",
+ description: "Longitude of the location",
+ },
+ },
+ required: ["latitude", "longitude"],
+ },
+ },
+ ],
+ };
+});
+```
+
+
+
+This defines our two tools: `get-alerts` and `get-forecast`.
+
+### Helper functions
+
+Next, let's add our helper functions for querying and formatting the data from the National Weather Service API:
+
+
+
+```python
+async def make_nws_request(client: httpx.AsyncClient, url: str) -> dict[str, Any] | None:
+ """Make a request to the NWS API with proper error handling."""
+ headers = {
+ "User-Agent": USER_AGENT,
+ "Accept": "application/geo+json"
+ }
+
+ try:
+ response = await client.get(url, headers=headers, timeout=30.0)
+ response.raise_for_status()
+ return response.json()
+ except Exception:
+ return None
+
+def format_alert(feature: dict) -> str:
+ """Format an alert feature into a concise string."""
+ props = feature["properties"]
+ return (
+ f"Event: {props.get('event', 'Unknown')}\n"
+ f"Area: {props.get('areaDesc', 'Unknown')}\n"
+ f"Severity: {props.get('severity', 'Unknown')}\n"
+ f"Status: {props.get('status', 'Unknown')}\n"
+ f"Headline: {props.get('headline', 'No headline')}\n"
+ "---"
+ )
+```
+
+
+```typescript
+// Helper function for making NWS API requests
+async function makeNWSRequest(url: string): Promise {
+ const headers = {
+ "User-Agent": USER_AGENT,
+ Accept: "application/geo+json",
+ };
+
+ try {
+ const response = await fetch(url, { headers });
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ return (await response.json()) as T;
+ } catch (error) {
+ console.error("Error making NWS request:", error);
+ return null;
+ }
+}
+
+interface AlertFeature {
+ properties: {
+ event?: string;
+ areaDesc?: string;
+ severity?: string;
+ status?: string;
+ headline?: string;
+ };
+}
+
+// Format alert data
+function formatAlert(feature: AlertFeature): string {
+ const props = feature.properties;
+ return [
+ `Event: ${props.event || "Unknown"}`,
+ `Area: ${props.areaDesc || "Unknown"}`,
+ `Severity: ${props.severity || "Unknown"}`,
+ `Status: ${props.status || "Unknown"}`,
+ `Headline: ${props.headline || "No headline"}`,
+ "---",
+ ].join("\n");
+}
+
+interface ForecastPeriod {
+ name?: string;
+ temperature?: number;
+ temperatureUnit?: string;
+ windSpeed?: string;
+ windDirection?: string;
+ shortForecast?: string;
+}
+
+interface AlertsResponse {
+ features: AlertFeature[];
+}
+
+interface PointsResponse {
+ properties: {
+ forecast?: string;
+ };
+}
+
+interface ForecastResponse {
+ properties: {
+ periods: ForecastPeriod[];
+ };
+}
+```
+
+
### Implementing tool execution
-The tool execution handler processes requests to get weather data:
+The tool execution handler is responsible for actually executing the logic of each tool. Let's add it:
+
+
```python
@server.call_tool()
async def handle_call_tool(
- name: str,
- arguments: dict | None
+ name: str, arguments: dict | None
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
- # Validate tool name and arguments
- if name != "get-weather":
- raise ValueError(f"Unknown tool: {name}")
+ """
+ Handle tool execution requests.
+ Tools can fetch weather data and notify clients of changes.
+ """
if not arguments:
raise ValueError("Missing arguments")
-
- lat = arguments.get("latitude")
- lon = arguments.get("longitude")
- if lat is None or lon is None:
- raise ValueError("Missing latitude or longitude")
-
- # Fetch weather data
- async with httpx.AsyncClient() as client:
- # Get grid point
- points_url = f"{NWS_API_BASE}/points/{lat},{lon}"
- points_response = await client.get(
- points_url,
- headers={"Accept": "application/json"}
- )
- points_data = points_response.json()
-
- # Get forecast
- forecast_url = points_data["properties"]["forecast"]
- forecast_response = await client.get(
- forecast_url,
- headers={"Accept": "application/json"}
- )
- forecast_data = forecast_response.json()
+
+ if name == "get-alerts":
+ state = arguments.get("state")
+ if not state:
+ raise ValueError("Missing state parameter")
- # Cache the forecast data
- location_key = f"{lat},{lon}"
- weather_cache[location_key] = forecast_data
-
- # Notify clients of resource changes
- await server.request_context.session.send_resource_list_changed()
-
- return [
- types.TextContent(
+ # Convert state to uppercase to ensure consistent format
+ state = state.upper()
+ if len(state) != 2:
+ raise ValueError("State must be a two-letter code (e.g. CA, NY)")
+
+ async with httpx.AsyncClient() as client:
+ alerts_url = f"{NWS_API_BASE}/alerts?area={state}"
+ alerts_data = await make_nws_request(client, alerts_url)
+
+ if not alerts_data:
+ return [types.TextContent(type="text", text="Failed to retrieve alerts data")]
+
+ features = alerts_data.get("features", [])
+ if not features:
+ return [types.TextContent(type="text", text=f"No active alerts for {state}")]
+
+ # Format each alert into a concise string
+ formatted_alerts = [format_alert(feature) for feature in features]
+ alerts_text = f"Active alerts for {state}:\n\n" + "\n".join(formatted_alerts)
+
+ return [
+ types.TextContent(
+ type="text",
+ text=alerts_text
+ )
+ ]
+ elif name == "get-forecast":
+ try:
+ latitude = float(arguments.get("latitude"))
+ longitude = float(arguments.get("longitude"))
+ except (TypeError, ValueError):
+ return [types.TextContent(
type="text",
- text=f"Retrieved weather data for location ({lat}, {lon})",
- )
- ]
+ text="Invalid coordinates. Please provide valid numbers for latitude and longitude."
+ )]
+
+ # Basic coordinate validation
+ if not (-90 <= latitude <= 90) or not (-180 <= longitude <= 180):
+ return [types.TextContent(
+ type="text",
+ text="Invalid coordinates. Latitude must be between -90 and 90, longitude between -180 and 180."
+ )]
+
+ async with httpx.AsyncClient() as client:
+ # First get the grid point
+ lat_str = f"{latitude:.4f}"
+ lon_str = f"{longitude:.4f}"
+ points_url = f"{NWS_API_BASE}/points/{lat_str},{lon_str}"
+ points_data = await make_nws_request(client, points_url)
+
+ if not points_data:
+ return [types.TextContent(type="text", text=f"Failed to retrieve grid point data for coordinates: {latitude}, {longitude}. This location may not be supported by the NWS API (only US locations are supported).")]
+
+ # Extract forecast URL from the response
+ properties = points_data.get("properties", {})
+ forecast_url = properties.get("forecast")
+
+ if not forecast_url:
+ return [types.TextContent(type="text", text="Failed to get forecast URL from grid point data")]
+
+ # Get the forecast
+ forecast_data = await make_nws_request(client, forecast_url)
+
+ if not forecast_data:
+ return [types.TextContent(type="text", text="Failed to retrieve forecast data")]
+
+ # Format the forecast periods
+ periods = forecast_data.get("properties", {}).get("periods", [])
+ if not periods:
+ return [types.TextContent(type="text", text="No forecast periods available")]
+
+ # Format each period into a concise string
+ formatted_forecast = []
+ for period in periods:
+ forecast_text = (
+ f"{period.get('name', 'Unknown')}:\n"
+ f"Temperature: {period.get('temperature', 'Unknown')}°{period.get('temperatureUnit', 'F')}\n"
+ f"Wind: {period.get('windSpeed', 'Unknown')} {period.get('windDirection', '')}\n"
+ f"{period.get('shortForecast', 'No forecast available')}\n"
+ "---"
+ )
+ formatted_forecast.append(forecast_text)
+
+ forecast_text = f"Forecast for {latitude}, {longitude}:\n\n" + "\n".join(formatted_forecast)
+
+ return [types.TextContent(
+ type="text",
+ text=forecast_text
+ )]
+ else:
+ raise ValueError(f"Unknown tool: {name}")
```
+
+
+
+```typescript
+// Handle tool execution
+server.setRequestHandler(CallToolRequestSchema, async (request) => {
+ const { name, arguments: args } = request.params;
+
+ try {
+ if (name === "get-alerts") {
+ const { state } = AlertsArgumentsSchema.parse(args);
+ const stateCode = state.toUpperCase();
+
+ const alertsUrl = `${NWS_API_BASE}/alerts?area=${stateCode}`;
+ const alertsData = await makeNWSRequest(alertsUrl);
+
+ if (!alertsData) {
+ return {
+ content: [
+ {
+ type: "text",
+ text: "Failed to retrieve alerts data",
+ },
+ ],
+ };
+ }
+
+ const features = alertsData.features || [];
+ if (features.length === 0) {
+ return {
+ content: [
+ {
+ type: "text",
+ text: `No active alerts for ${stateCode}`,
+ },
+ ],
+ };
+ }
+
+ const formattedAlerts = features.map(formatAlert);
+ const alertsText = `Active alerts for ${stateCode}:\n\n${formattedAlerts.join(
+ "\n"
+ )}`;
+
+ return {
+ content: [
+ {
+ type: "text",
+ text: alertsText,
+ },
+ ],
+ };
+ } else if (name === "get-forecast") {
+ const { latitude, longitude } = ForecastArgumentsSchema.parse(args);
+
+ // Get grid point data
+ const pointsUrl = `${NWS_API_BASE}/points/${latitude.toFixed(
+ 4
+ )},${longitude.toFixed(4)}`;
+ const pointsData = await makeNWSRequest(pointsUrl);
+
+ if (!pointsData) {
+ return {
+ content: [
+ {
+ type: "text",
+ text: `Failed to retrieve grid point data for coordinates: ${latitude}, ${longitude}. This location may not be supported by the NWS API (only US locations are supported).`,
+ },
+ ],
+ };
+ }
+
+ const forecastUrl = pointsData.properties?.forecast;
+ if (!forecastUrl) {
+ return {
+ content: [
+ {
+ type: "text",
+ text: "Failed to get forecast URL from grid point data",
+ },
+ ],
+ };
+ }
+
+ // Get forecast data
+ const forecastData = await makeNWSRequest(forecastUrl);
+ if (!forecastData) {
+ return {
+ content: [
+ {
+ type: "text",
+ text: "Failed to retrieve forecast data",
+ },
+ ],
+ };
+ }
+
+ const periods = forecastData.properties?.periods || [];
+ if (periods.length === 0) {
+ return {
+ content: [
+ {
+ type: "text",
+ text: "No forecast periods available",
+ },
+ ],
+ };
+ }
+
+ // Format forecast periods
+ const formattedForecast = periods.map((period: ForecastPeriod) =>
+ [
+ `${period.name || "Unknown"}:`,
+ `Temperature: ${period.temperature || "Unknown"}°${
+ period.temperatureUnit || "F"
+ }`,
+ `Wind: ${period.windSpeed || "Unknown"} ${
+ period.windDirection || ""
+ }`,
+ `${period.shortForecast || "No forecast available"}`,
+ "---",
+ ].join("\n")
+ );
+
+ const forecastText = `Forecast for ${latitude}, ${longitude}:\n\n${formattedForecast.join(
+ "\n"
+ )}`;
+
+ return {
+ content: [
+ {
+ type: "text",
+ text: forecastText,
+ },
+ ],
+ };
+ } else {
+ throw new Error(`Unknown tool: ${name}`);
+ }
+ } catch (error) {
+ if (error instanceof z.ZodError) {
+ throw new Error(
+ `Invalid arguments: ${error.errors
+ .map((e) => `${e.path.join(".")}: ${e.message}`)
+ .join(", ")}`
+ );
+ }
+ throw error;
+ }
+});
+```
+
+
### Running the server
Finally, implement the main function to run the server:
+
+
```python
async def main():
+ # Run the server using stdin/stdout streams
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
@@ -345,6 +820,25 @@ async def main():
),
)
```
+
+
+```typescript
+// Start the server
+async function main() {
+ const transport = new StdioServerTransport();
+ await server.connect(transport);
+ console.error("Weather MCP Server running on stdio");
+}
+
+main().catch((error) => {
+ console.error("Fatal error in main():", error);
+ process.exit(1);
+});
+```
+
+Make sure to run `npm run build` to build your server! This is a very important step in getting your server to connect.
+
+
Your server is complete! Let's now test your server from an existing MCP host, Claude for Desktop.
@@ -356,7 +850,7 @@ Unfortunately, Claude for Desktop is not yet available on Linux. Linux users can
First, make sure you have Claude for Desktop installed. [You can install the latest version here.](https://claude.ai/download)
-Open your Claude Desktop App configuration at `~/Library/Application Support/Claude/claude_desktop_config.json` in a text editor.
+Next, open your Claude Desktop App configuration at `~/Library/Application Support/Claude/claude_desktop_config.json` in a text editor.
For example, if you have [VS Code](https://code.visualstudio.com/) installed:
@@ -378,7 +872,8 @@ Add this configuration (replace the parent folder path):
-```json
+
+```json Python
{
"mcpServers": {
"weather": {
@@ -393,9 +888,25 @@ Add this configuration (replace the parent folder path):
}
}
```
+
+```json Node
+{
+ "mcpServers": {
+ "weather": {
+ "command": "node",
+ "args": [
+ "/PATH/TO/PARENT/FOLDER/mcp-quickstart/build/index.js",
+ ]
+ }
+ }
+}
+```
+
-```json
+
+
+```json Python
{
"mcpServers": {
"weather": {
@@ -410,24 +921,56 @@ Add this configuration (replace the parent folder path):
}
}
```
+
+```json Node
+{
+ "mcpServers": {
+ "weather": {
+ "command": "node",
+ "args": [
+ "C:\\PATH\TO\PARENT\FOLDER\mcp-quickstart\build\index.ts",
+ ]
+ }
+ }
+}
+```
+
+
This tells Claude Desktop:
1. There's an MCP server named "weather"
-2. Launch it by running `uv --directory /PATH/TO/PARENT/FOLDER/mcp-quickstart run weather`
+2. Launch it by running `uv --directory /PATH/TO/PARENT/FOLDER/mcp-quickstart run weather` if using Python or `node /PATH/TO/PARENT/FOLDER/mcp-quickstart/build/index.js` if using Node
Save the file, and restart **Claude Desktop**.
### Test with commands
-You can now test your server by running the following commands in Claude Desktop:
-
-- What's the weather in Brooklyn?
-- What's the weather in San Francisco?
+First, make sure Claude Desktop is picking up the two tools we've exposed in our `weather` server. You can do this by looking for the hammer
icon:
-
+
+
+
+After clicking on the hammer icon, you should see two tools listed:
+
+
+
+
+
+If your server isn't being picked up by Claude Desktop, proceed to the **Troubleshooting** section for debugging advice.
+
+You can now test your server by running the following commands in Claude Desktop:
+
+- What's the weather in Sacramento?
+- What are the active weather alerts in Texas?
+
+
+
+
+
+
@@ -445,4 +988,79 @@ When you ask a question:
5. Claude formulates a natural language response
6. The response is displayed to you!
-## Next steps
\ No newline at end of file
+## Troubleshooting
+
+
+
+**Error: Failed to retrieve grid point data**
+
+This usually means either:
+1. The coordinates are outside the US
+2. The NWS API is having issues
+3. You're being rate limited
+
+Fix:
+
+- Verify you're using US coordinates
+- Add a small delay between requests
+- Check the NWS API status page
+
+**Error: No active alerts for [STATE]**
+
+This isn't an error - it just means there are no current weather alerts for that state. Try a different state or check during severe weather.
+
+
+**Server not showing up in Claude**
+1. Check your configuration file syntax
+2. Make sure the path to your project is correct
+3. Restart Claude Desktop completely
+
+You can also check Claude's logs for errors like so:
+```bash
+# Check Claude's logs for errors
+tail -f ~/Library/Application\ Support/Claude/Claude.log
+```
+
+**Tool calls failing silently**
+
+If Claude attempts to use the tools but they fail:
+
+1. Check Claude's logs for errors
+2. Verify your server runs without errors
+3. Try restarting Claude Desktop
+
+
+
+## Next steps
+
+
+
+ Learn how to build your first MCP client
+
+
+ Build an intermediate MCP server that communicates with your local filesystem
+
+
+ Check out our gallery of official MCP servers and implementations
+
+
+
+ Understand how MCP connects clients, servers, and LLMs
+
+
\ No newline at end of file