Skip to main content
Glama
savkolar

pythonWeatherMCP

by savkolar
README.md
# Weather MCP Server Workshop

This project is a hands-on Python workshop for building and testing an MCP server
that calls the US National Weather Service (NWS) API. The NWS requires no API key,
but it supports US locations only.

This README is the complete participant lab. Follow it from top to bottom during
the workshop.

## Workshop outcome

In 60-75 minutes, participants will:

1. Run a local MCP server.
2. Inspect its generated tool schema.
3. Call a real public API through an MCP tool.
4. Add and test a second MCP tool.
5. Optionally connect the server to an AI client.
6. Identify how to replace the sample API with a customer business API.

```text
User -> Agent Builder or MCP Inspector -> MCP server -> api.weather.gov
```

MCP Inspector tests tools directly and requires no AI model. Agent Builder adds a
model so participants can observe tool selection and orchestration.

## Prerequisites

- Python 3.10 or later
- Node.js 22.19 or later
- Visual Studio Code
- Microsoft Python and Python Debugger extensions
- Foundry Toolkit only for the optional Agent Builder exercise

Exercises 1-4 run entirely locally and require no Azure subscription, resource
group, Foundry project, or deployed GPT model. Exercise 5 requires an available
chat model configured in Agent Builder. A model deployed in a Microsoft Foundry
project is one supported option and requires the corresponding Azure resources.

Verify the command-line prerequisites:

```powershell
python --version
# If missing: winget install --id Python.Python.3.13 --exact
node --version
# If missing: winget install --id OpenJS.NodeJS.LTS --exact
npm --version
# npm is installed with Node.js.
```

## Lab setup

Complete this section before Exercise 1.

### 1. Open the correct folder

Open the `pythonWeatherMCP` folder directly in VS Code. It contains the launch and
task configurations used by this workshop. Run all commands in this README from
that folder.

### 2. Install the VS Code extensions

Open **Extensions** with **Ctrl+Shift+X** and install or enable:

- Python by Microsoft
- Python Debugger by Microsoft
- Foundry Toolkit only if Exercise 5 will be used

The first two can also be installed from a terminal:

```powershell
code --install-extension ms-python.python
code --install-extension ms-python.debugpy
```

Completely close and reopen VS Code after installing them.

### 3. Create the Python environment

Run these commands from the open `pythonWeatherMCP` project folder:

```powershell
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
# -e keeps the project editable; [dev] installs the debugger dependency.
pip install -e ".[dev]"
```

If PowerShell blocks activation, run this once in that terminal and activate again:

```powershell
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
.\.venv\Scripts\Activate.ps1
```

The `(.venv)` prefix confirms that the environment is active in the terminal. The
committed `.vscode/settings.json` also points VS Code to this interpreter. If the
status bar does not show `.venv`:

1. Press **Ctrl+Shift+P**.
2. Run **Python: Select Interpreter**.
3. Select the interpreter ending in `.venv\Scripts\python.exe`.

These are VS Code actions, not PowerShell commands.

### 4. Install MCP Inspector

```powershell
Set-Location inspector
# Install the exact dependencies pinned in package-lock.json.
npm ci
# Confirm @modelcontextprotocol/inspector@2.0.0 is installed.
npm ls @modelcontextprotocol/inspector
Set-Location ..
```

The project pins MCP Inspector 2.0.0. Do not run `npm audit fix --force`, because
it can replace the pinned dependencies with incompatible major versions.

In `src/server.py`, replace `workshop@example.com` in `USER_AGENT` with a real
facilitator or organization contact address before calling the NWS API. The NWS
asks API clients to send an identifiable user agent.

### 5. Verify the starter project

Open `src/server.py`. Confirm that it contains `get_forecast` and does not contain
`get_alerts`; you will add `get_alerts` in Exercise 3. If it is already present,
ask the facilitator for a fresh starter copy before continuing.

Start the server and verify it before the workshop:

1. Press **Ctrl+Shift+P** in VS Code.
2. Run **Debug: Select and Start Debugging**.
3. Select **Debug in Inspector**.
4. Wait for Inspector to open at `http://localhost:6274`.
5. Select `pythonweathermcp_http`, verify that the transport is **Streamable HTTP**
	and the URL is `http://localhost:3001/mcp`, then select **Connect**.
6. Open **Tools**, select **List Tools**, and run `get_forecast` with:

```json
{
  "latitude": 47.6062,
  "longitude": -122.3321
}
```

The expected result is five forecast periods for Seattle. Complete the cleanup
steps below after verification.

## Stop the workshop processes

1. Press **Ctrl+Shift+P**.
2. Run **Tasks: Run Task**.
3. Select **Terminate All Tasks**.
4. Close the Inspector browser tab after the task reports that the processes
	stopped.

Use this cleanup task before changing from Inspector to Agent Builder. Refreshing
the browser does not reload Python tool registrations; restart the debug session
after adding or changing a tool.

## Participant lab

### Exercise 1: Understand the server

Open `src/server.py` and find these four parts:

1. `FastMCP(...)` creates the MCP server.
2. `make_nws_request(...)` calls the external API.
3. `@server.tool(...)` registers a Python function as an MCP tool.
4. Type hints and the docstring become the tool input schema and description.

Open `src/__init__.py`. It starts the same server with one of two transports:

- `streamable-http` for network clients, Inspector, and Agent Builder
- `stdio` for clients that launch the server as a local subprocess

### Exercise 2: Inspect and call the forecast tool

1. Press **Ctrl+Shift+P** and run **Debug: Select and Start Debugging**.
2. Select **Debug in Inspector**.
3. Wait for the browser to open at `http://localhost:6274`.
4. Select `pythonweathermcp_http` and verify:
   - Transport: **Streamable HTTP**
   - URL: `http://localhost:3001/mcp`
5. Select **Connect** and confirm the status is **Connected**.
6. Open **Tools** and select **List Tools**.
7. Select `get_forecast`, enter the following input, and select **Run Tool**:

```json
{
  "latitude": 47.6062,
  "longitude": -122.3321
}
```

Expected result: five forecast periods for Seattle. Observe that Inspector created
numeric inputs from the Python type hints. The caller did not need to know about the
NWS `/points` request or the second forecast URL.

Test a controlled validation failure:

```json
{
  "latitude": 200,
  "longitude": -122.3321
}
```

Expected result: a latitude and longitude validation message, not a downstream
request failure.

### Exercise 3: Add a second MCP tool

Stop the workshop processes using the cleanup steps above. Open `src/server.py` and
add this function at the end of the file:

```python
@server.tool(
    name="get_alerts",
    title="Get Active Weather Alerts",
    description="Get active NWS weather alerts for a two-letter US state code",
)
async def get_alerts(state: str) -> str:
    """Get active weather alerts for a US state.

    Args:
        state: Two-letter US state code, such as WA or FL.
    """
    state_code = state.strip().upper()
    if len(state_code) != 2 or not state_code.isalpha():
        return "State must be a two-letter US state code, such as WA or FL."

    data = await make_nws_request(
        f"{NWS_API_BASE}/alerts/active/area/{state_code}"
    )
    if not data:
        return "Unable to retrieve alerts from the NWS."

    features = data.get("features", [])
    if not features:
        return f"No active weather alerts for {state_code}."

    alerts = [
        {
            "event": feature.get("properties", {}).get("event"),
            "area": feature.get("properties", {}).get("areaDesc"),
            "severity": feature.get("properties", {}).get("severity"),
            "headline": feature.get("properties", {}).get("headline"),
        }
        for feature in features[:10]
    ]
    return json.dumps(alerts)
```

This is a separate tool because it has a distinct purpose and a smaller input
contract. Its description helps a model choose it correctly, and its response is
bounded to ten alerts.

### Exercise 4: Restart and verify both tools

FastMCP registers tools when the Python process starts. Saving the file or refreshing
Inspector does not reload the server.

1. Save `src/server.py`.
2. Press **Ctrl+Shift+P** and run **Tasks: Run Task**.
3. Select **Terminate All Tasks**.
4. Close the old Inspector browser tab.
5. Run **Debug: Select and Start Debugging** and select the Inspector configuration.
6. In the newly opened Inspector tab, connect and select **List Tools**.
7. Confirm that both `get_forecast` and `get_alerts` appear.
8. Run `get_alerts` with:

```json
{
  "state": "WA"
}
```

No active alerts is a successful result. The important check is that the tool
executes and returns a clear response. Test validation with `Washington`; the tool
should ask for a two-letter state code.

### Exercise 5: Let an agent choose the tools (optional)

This exercise requires the Foundry Toolkit extension and an available chat model
configured in Agent Builder. A Foundry model deployment is one option; Exercises
1-4 do not require Azure resources or a model.

1. Stop Inspector with **Tasks: Run Task > Terminate All Tasks**.
2. Close the Inspector browser tab.
3. Press **Ctrl+Shift+P** and run **Debug: Select and Start Debugging**.
4. Select **Debug in Agent Builder**.
5. In Agent Builder, select or configure an available chat model.
6. Confirm that `local-server-pythonweathermcp` is connected.
7. Use this system instruction:

```text
You are a concise US weather assistant. Use the available MCP tools for forecast and alert facts. State when the source cannot answer a request.
```

Try these prompts:

```text
What is the forecast for Seattle at latitude 47.6062 and longitude -122.3321?
Are there active weather alerts in Washington state?
Check the forecast and alerts, then summarize any travel concerns.
```

The last prompt demonstrates orchestration: the model can select and combine two
independently defined tools. Stop the workshop processes when finished.

## Continue to the Foundry Local labs

Use two separate VS Code windows for the next phase. Keep this
`pythonWeatherMCP` folder open in Window 1 and open the `Foundry-Local` repository
root in Window 2. Each repository must keep its own virtual environment and
terminal.

Before Foundry Local calls the MCP tools:

1. In Window 1, press **Ctrl+Shift+P** and run **Tasks: Run Task**.
2. Select **Terminate All Tasks** and close the Inspector browser tab.
3. Open a new terminal in Window 1 and confirm its current folder is
    `pythonWeatherMCP`.
4. Start only the MCP server:

```powershell
.\.venv\Scripts\python.exe src\__init__.py http
```

5. Leave that terminal running. The endpoint is
    `http://127.0.0.1:3001/mcp`.
6. In Window 2, follow the Foundry Local labs. For the MCP integration, use the
    Part 11 guide at `labs\part11-tool-calling\README.md` in that repository.

Do not start **Debug in Inspector** while the Foundry Local bridge is using the
server; both launch paths use port 3001. If Parts 1-10 will be completed before
Part 11, press **Ctrl+C** to stop the server and restart it immediately before the
Part 11 MCP integration exercise.

## Customer application exercise

Ask each participant to identify one operation from their environment that follows:

```text
verb_business_object(small typed input) -> approved, bounded result
```

Good examples include `get_order_status(order_id)`,
`search_knowledge_base(query, max_results)`, `get_service_health(service_name)`,
and `create_support_ticket(summary, severity)`.

Avoid broad tools such as `run_sql`, `call_any_url`, or `execute_command`. To adapt
this sample:

1. Replace `make_nws_request` with a client for the approved system.
2. Store credentials in environment variables or a managed secret store.
3. Rename the tool for the specific business operation.
4. Use constrained input types and validate identifiers.
5. Return only fields the intended user is allowed to see.
6. Add timeouts, bounded results, error handling, and audit-safe logging.
7. Test directly in Inspector before connecting an agent.

## Production discussion

This workshop server is local and unauthenticated. A production implementation
must address authentication and authorization, least-privilege downstream identity,
secret management, input validation, output filtering, rate limits, retries,
timeouts, audit-safe logging, failure tests, HTTPS hosting, and an appropriate
remote transport.

## Project layout

| Path | Purpose |
| ---- | ------- |
| `src/server.py` | FastMCP server and weather tools |
| `src/__init__.py` | Streamable HTTP and stdio entry point |
| `inspector/` | Pinned local MCP Inspector configuration |
| `.aitk/mcp.json` | Agent Builder MCP connection |
| `.vscode/` | Debug, task, interpreter, and cleanup configuration |

## Ports

| Process | Port |
| ------- | ---- |
| Weather MCP Streamable HTTP server | 3001 |
| Python debug adapter | 5678 |
| MCP Inspector 2 web application | 6274 |

## Troubleshooting

**VS Code tries to debug this Markdown file**

Select **Cancel**. Press **Ctrl+Shift+P**, run **Debug: Select and Start Debugging**,
and select the Inspector configuration. Do not use a play button attached to this
README.

**`Python: Select Interpreter` is not recognized in PowerShell**

It is a VS Code Command Palette action. Press **Ctrl+Shift+P** and run it there. If
it is missing, install or enable the Microsoft Python extension and restart VS Code.

**`ModuleNotFoundError` appears**

Activate `.venv`, select its interpreter in VS Code, and rerun:

```powershell
pip install -e ".[dev]"
```

**Inspector does not open**

Confirm Node.js is 22.19 or later, run `npm ci` inside `inspector`, terminate the
workshop tasks, and retry. Inspector 2 opens at `http://localhost:6274`; old
instructions referring to ports 5173 and 3000 do not apply.

**Inspector reports `Failed to fetch` or a port is already in use**

Run **Tasks: Run Task > Terminate All Tasks**, close old Inspector tabs, and start
one new Inspector debug session.

**The new tool is missing**

Browser refresh is insufficient. Terminate all workshop tasks, restart the Inspector
debug configuration, reconnect, and select **List Tools**.

**`npm` reports deprecations, funding messages, or vulnerabilities**

Deprecation and funding messages do not mean installation failed. Do not run
`npm audit fix --force`. Use the pinned lockfile with `npm ci`, keep Inspector bound
to localhost, and do not enter secrets or customer data.

**The NWS request fails**

Confirm internet access, use US coordinates, verify the `USER_AGENT` contact, and
check the [NWS API status](https://www.weather.gov/documentation/services-web-status).

## End-of-workshop reset

1. Run **Tasks: Run Task > Terminate All Tasks**.
2. Close Inspector and Agent Builder tabs.
3. Do not retain prompts, credentials, tokens, or customer data from the session.

## References

- [Official MCP server tutorial](https://modelcontextprotocol.io/docs/develop/build-server)
- [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk)
- [MCP Inspector](https://github.com/modelcontextprotocol/inspector)
- [NWS API documentation](https://www.weather.gov/documentation/services-web-api)
- [Foundry Toolkit documentation](https://learn.microsoft.com/windows/ai/toolkit/)