Skip to main content

Build a Weather Service Servlet with Python

Just looking for code examples?

If you want to start with some working servlets, see our official servlet repo: dylibso/mcp.run-servlets

Welcome! In this tutorial, you'll build your first servlet using Python. We'll create a weather service that AI models can use to fetch real-time weather data. By the end, you'll have a working servlet that can tell you the weather anywhere in the world!

What Are We Building?

A servlet is a tool that AI models can use to interact with external services. Our weather servlet will:

  • Connect to the WeatherAPI service
  • Fetch current weather conditions for any location
  • Format the data in a user-friendly way

Here's what it looks like in action:

Pre-requisites

Before starting, ensure you have the following:

Create an account on mcp.run

1. Create an account on mcp.run:

Visit https://mcp.run and click "Sign Up".

2. Register your first servlet:

Click "Publish" and go through the Registration flow. You can make your servlet "Private" while it is under development.

Use weather-service as the name, and you can leave the rest of the fields blank. We'll fill these out later!

Once you're done, click "Register Servlet" and you'll see some instructions to follow on your servlet page. Be sure to follow those closely.

Project Setup

First, let's create our project:

  1. Use the XTP CLI to genearate a new Servlet
xtp plugin init \
--extension-point ext_01je4jj1tteaktf0zd0anm8854 \
--feature stub-with-code-samples \
--path weather-service \
--name weather-service
Important!

The --name parameter (weather-service) MUST match what you'll use when publishing to mcp.run later.

  1. Choose "Python" when prompted

  2. Open the project in VS Code

Create a new file called plugin/weather_api.py:

from dataclasses import dataclass
from typing import Optional
from dataclass_wizard import JSONWizard
from extism import Http

@dataclass
class WeatherLocation:
name: str
country: str
lat: float
lon: float

@dataclass
class WeatherCondition:
text: str

@dataclass
class CurrentWeather:
temp_c: float
temp_f: float
condition: WeatherCondition
humidity: int

@dataclass
class WeatherResponse(JSONWizard):
location: WeatherLocation
current: CurrentWeather

class Meta:
key_transform_with_load = 'SNAKE'

class WeatherAPI:
def __init__(self, api_key: str):
self.api_key = api_key

def get_current_weather(self, location: str) -> WeatherResponse:
"""
Get current weather for a given location.

Args:
location: City name, latitude/longitude, or other location identifier

Returns:
WeatherResponse object containing weather data

Raises:
Exception: If the API request fails
"""
url = f"http://api.weatherapi.com/v1/current.json?key={self.api_key}&q={location}&aqi=no"

response = Http.request(
url=url,
meth="GET"
)

if response.status_code != 200:
raise Exception(
f"Weather API error: {response.status_code} - {response.data_str()}"
)

return WeatherResponse.from_json(response.data_str())

And then edit plugin/plugin.py to use our WeatherAPI.

Replace the call and describe functions with:

from typing import Dict, Any  # noqa: F401
from weather_api import WeatherAPI # noqa: F401

def handle_get_weather(api: WeatherAPI, args: Dict[str, Any]) -> CallToolResult:
location = args.get('location') if args else None
if not location:
return CallToolResult(
isError=True,
content=[Content(
type=ContentType.Text,
text="location parameter is required"
)]
)

try:
weather = api.get_current_weather(location)

response = (
f"Current weather in {weather.location.name}, {weather.location.country}:\n"
f"Temperature: {weather.current.temp_c:.1f}°C ({weather.current.temp_f:.1f}°F)\n"
f"Condition: {weather.current.condition.text}\n"
f"Humidity: {weather.current.humidity}%"
)

return CallToolResult(
content=[Content(
type=ContentType.Text,
text=response
)]
)

except Exception as err:
return CallToolResult(
isError=True,
content=[Content(
type=ContentType.Text,
text=str(err)
)]
)

# Called when the tool is invoked.
# If you support multiple tools, you must switch on the input.params.name to detect which tool is being called.
def call(input: CallToolRequest) -> CallToolResult:
# Get API key from config
api_key = extism.Config.get_str("WEATHER_API_KEY")
if not api_key:
return CallToolResult(
isError=True,
content=[Content(
type=ContentType.Text,
text="WeatherAPI key not configured"
)]
)

weather_api = WeatherAPI(api_key)

if input.params.name == "get-weather":
return handle_get_weather(weather_api, input.params.arguments)
else:
return CallToolResult(
isError=True,
content=[Content(
type=ContentType.Text,
text=f"Unknown tool: {input.params.name}"
)]
)


# Called by mcpx to understand how and why to use this tool.
# Note: Your servlet configs will not be set when this function is called,
# so do not rely on config in this function
def describe() -> ListToolsResult:
return ListToolsResult(
tools=[ToolDescription(
name="get-weather",
description="Get current weather conditions for any location",
inputSchema={
"type": "object",
"required": ["location"],
"properties": {
"location": {
"type": "string",
"description": "Location name, city, zip/postal code, or coordinates"
}
}
}
)]
)

This file defines how AI models interact with our weather tool. A servlet can have multiple tools, but we're keeping it simple with just one: get-weather. There are two main functions:

  • describe(): Tells AI models what our Servlet can do
  • call(): Handles incoming requests from AI models

Build and test your Servlet

You can use the XTP CLI to test your Servlet locally:

# Build your servlet
xtp plugin build

# Set your API key as an environment variable
export WEATHER_API_KEY="your-key-here"

# Test out calling the get-weather tool
xtp plugin call plugin.wasm call --input='{
"params": {
"name": "get-weather",
"arguments": {
"location": "London"
}
}
}' \
--wasi \
--allow-host api.weatherapi.com \
--config "WEATHER_API_KEY=$WEATHER_API_KEY"

You should see something like:

{
"content": [{
"text": "Current weather in London, United Kingdom:\nTemperature: 9.2°C (48.6°F)\nCondition: Mist\nHumidity: 93%",
"type": "text"
}]
}

Publish your Servlet on mcp.run

  1. Register your servlet at https://mcp.run/publish
  • Name: weather-service (MUST match the name from earlier!)
  • Description: "Get real-time weather data for any location"
  • Configuration:
    • Add WEATHER_API_KEY variable for WeatherAPI key.

      It's also a nice touch to include an example value and some instructions or a URL in the description about where to find their API key, so it's easier for installers to get started.

  • Domain permissions:
    • Add api.weatherapi.com

Click Register Servlet

  1. Upload:
xtp plugin push

That's it! Your servlet is now available for AI models to use.

Using Your Servlet

  1. Click on the Install button on your Servlet page
  2. Provide your WeatherAPI key
  3. Now the AI model can use it you have installed the mcpx client, see this page for more details.

Support

If you get stuck and need some help, please reach out! Visit our support page to learn how best to get in touch.