Skip to main content

Build a Weather Service Servlet with TypeScript

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 TypeScript. 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 "TypeScript" when prompted

  2. Open the project in VS Code

Create a new file called weatherapi.ts:

export interface WeatherResponse {
location: {
name: string;
country: string;
lat: number;
lon: number;
};
current: {
temp_c: number;
temp_f: number;
condition: {
text: string;
};
humidity: number;
};
}

export class WeatherAPI {
private apiKey: string;

constructor(apiKey: string) {
this.apiKey = apiKey;
}

getCurrentWeather(location: string): WeatherResponse {
const url =
`http://api.weatherapi.com/v1/current.json?key=${this.apiKey}&q=${location}&aqi=no`;

// Servlets are WebAssembly modules, which means we don't have access
// to platform-specific functionality like a full networking stack.
// Instead, we have helpers like this to make HTTP requests to hostnames
// which are allow-listed by the installer of the servlet on mcp.run
const response = Http.request({
url: url,
method: "GET",
});

if (response.status !== 200) {
throw new Error(
`Weather API error: ${response.status} - ${response.body}`,
);
}

return JSON.parse(response.body);
}
}

And then edit src/main.ts to use our WeatherAPI:

import {
CallToolRequest,
CallToolResult,
ContentType,
ListToolsResult,
} from "./pdk";
import { WeatherAPI } from "./weatherapi";

// Main call handler for the servlet
export function callImpl(input: CallToolRequest): CallToolResult {
// Servlets are WebAssembly modules, which means we don't have access
// to platform-specific functionality like OS environment variables.
// Instead, we have helpers like this to get configuration set by the
// installer of the servlet on mcp.run
const apiKey = Config.get("WEATHER_API_KEY");
if (!apiKey) {
return {
isError: true,
content: [{
type: ContentType.Text,
text: "WeatherAPI key not configured",
}],
};
}

const weatherAPI = new WeatherAPI(apiKey);

switch (input.params.name) {
case "get-weather":
return handleGetWeather(weatherAPI, input.params.arguments);
default:
return {
isError: true,
content: [{
type: ContentType.Text,
text: `Unknown tool: ${input.params.name}`,
}],
};
}
}

function handleGetWeather(api: WeatherAPI, args: any): CallToolResult {
const location = args?.location;
if (!location) {
return {
isError: true,
content: [{
type: ContentType.Text,
text: "location parameter is required",
}],
};
}

try {
const weather = api.getCurrentWeather(location);

const response =
`Current weather in ${weather.location.name}, ${weather.location.country}:
Temperature: ${weather.current.temp_c.toFixed(1)}°C (${
weather.current.temp_f.toFixed(1)
}°F)
Condition: ${weather.current.condition.text}
Humidity: ${weather.current.humidity}%`;

return {
content: [{
type: ContentType.Text,
text: response,
}],
};
} catch (err) {
return {
isError: true,
content: [{
type: ContentType.Text,
text: (err as any).message,
}],
};
}
}

// Tool description for mcp.run
export function describeImpl(): ListToolsResult {
return {
tools: [{
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"

xtp plugin call dist/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
  1. Publish:
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.