Weather App Sample
June 1, 2026 · View on GitHub
A sample MCP App that displays weather information with an interactive UI.
What Are MCP Apps?
MCP Apps let tools return interactive interfaces instead of plain text. When a tool declares a UI resource, the host renders it in a sandboxed iframe where users can interact directly.
MCP Apps = Tool + UI Resource
The architecture relies on two MCP primitives:
- Tools with UI metadata pointing to a resource URI
- Resources containing bundled HTML/JavaScript served via the
ui://scheme
Azure Functions makes it easy to build both.
Prerequisites
- JDK 17 (or newer)
- Apache Maven
- Node.js (for building the UI)
- Azure Functions Core Tools v4
- An MCP-compatible host (VS Code with GitHub Copilot, Claude Desktop, etc.)
Getting Started
1. Build the UI
The UI must be bundled before running the function app:
cd samples/McpWeatherApp/app
npm install
npm run build
cd ..
This creates a bundled app/dist/index.html file that the function serves.
2. Build and Run the Function App
mvn clean package
mvn azure-functions:run
The MCP server will be available at http://localhost:7071/runtime/webhooks/mcp.
3. Connect from VS Code
Open .vscode/mcp.json at the repo root. Find the server called local-mcp-function and click Start above the name.
4. Prompt the Agent
Ask Copilot: "What's the weather in Seattle?"
Deploy to Azure
1. Sign in and create an environment
azd auth login
# This also becomes the resource group name
azd env new <environment-name>
2. Configure Entra authentication
Pre-authorize VS Code to request access tokens from Microsoft Entra:
azd env set PRE_AUTHORIZED_CLIENT_IDS aebc6443-996d-45c2-90f0-388ff96faa56
Optional: Enable VNet isolation:
azd env set VNET_ENABLED true
3. Deploy
azd up
4. Connect to the remote MCP server
After deployment, open .vscode/mcp.json and click Start above remote-mcp-function. Enter the functionapp-name from your azd output (or /.azure/*/.env). You'll be prompted to authenticate with Microsoft — click Allow and sign in with your Azure subscription email.
Tip
A successful connection shows the number of tools the server has. Click More... -> Show Output for details on the VS Code ↔ server interactions.
Redeploy and clean up
Redeploy: Run azd up as many times as needed to deploy code updates.
Clean up: Delete all Azure resources when done:
azd down
Source Code
The source code is in WeatherFunction.java. The key concept is how tools connect to resources via metadata.
The Tool with UI Metadata
The GetWeather tool uses @McpMetadata to declare it has an associated UI:
private static final String TOOL_METADATA = """
{
"ui": {
"resourceUri": "ui://weather/index.html"
}
}
""";
@FunctionName("GetWeather")
public String getWeather(
@McpToolTrigger(
name = "GetWeather",
description = "Returns current weather for a location via Open-Meteo.")
@McpMetadata(
name = "GetWeather",
json = TOOL_METADATA)
String context,
@McpToolProperty(
name = "location",
propertyType = "string",
description = "City name to check weather for (e.g., Seattle, New York, Miami)")
String location,
final ExecutionContext executionContext) {
// Fetches weather data and returns JSON
Object result = weatherService.getCurrentWeather(location);
return MAPPER.writeValueAsString(result);
}
The resourceUri points to ui://weather/index.html — this tells the MCP host that when this tool is invoked, there's an interactive UI available at that resource URI.
The Resource Serving the UI
The GetWeatherWidget function serves the bundled HTML at the matching URI:
private static final String RESOURCE_METADATA = """
{
"ui": {
"prefersBorder": true
}
}
""";
@FunctionName("GetWeatherWidget")
public String getWeatherWidget(
@McpResourceTrigger(
name = "context",
uri = "ui://weather/index.html",
resourceName = "Weather Widget",
description = "Interactive weather display for MCP Apps",
mimeType = "text/html;profile=mcp-app")
@McpMetadata(
name = "context",
json = RESOURCE_METADATA)
String context,
final ExecutionContext executionContext) {
// Reads and returns app/dist/index.html
return Files.readString(new File("app/dist/index.html").toPath());
}
How It Works Together
- User asks: "What's the weather in Seattle?"
- Agent calls the
GetWeathertool - Tool returns weather data (JSON) and the host sees the
ui.resourceUrimetadata - Host fetches the UI resource from
ui://weather/index.html - Host renders the HTML in a sandboxed iframe, passing the tool result as context
- User sees an interactive weather widget instead of plain text
The UI (TypeScript)
The frontend in app/src/weather-app.ts receives the tool result and renders the weather display. It uses the @modelcontextprotocol/ext-apps SDK and is bundled with Vite into a single index.html that the resource serves.
Project Structure
| Path | Description |
|---|---|
| WeatherFunction.java | MCP tool + resource definitions |
| WeatherService.java | Open-Meteo API client (geocoding + weather) |
| WeatherResult.java | Weather data POJO |
| WeatherError.java | Error response POJO |
| app/ | Vite + TypeScript weather widget UI |
| app/src/weather-app.ts | Frontend entry point |
Troubleshooting
| Problem | Solution |
|---|---|
azd up provision succeeded but deploy immediately failed: unable to find a resource tagged with 'azd-service-name: mcp' | The tag was provisioned but not propagated yet when azd deploy looked it up — run azd deploy again |
azd deploy fails with Kudu restart error: deployment was partially successful: [KuduSpecializer] Kudu has been restarted after package deployed | Transient error — run azd deploy again |