mcp-app-openweather
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-app-openweatherwhat's the weather in San Francisco?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
MCP App: OpenWeather Forecast Visualizer
A proof-of-concept MCP App — an MCP server whose tool renders an interactive chart inside the chat instead of returning only text. Ask for a forecast, get a Recharts graph you can click; clicking a control calls back into the server for fresh data.
Built as a readable reference for the MCP Apps
extension, written
against @modelcontextprotocol/ext-apps@1.7.5 and
@modelcontextprotocol/sdk@1.30.0. The extension is under active development;
_meta.ui shapes may shift, so treat the pinned versions as part of the API.
What an MCP App is, in one screen
A normal MCP tool returns content the model reads. An MCP App additionally ships a UI resource that the host renders in a sandboxed iframe. Two registrations tied together by one URI:
// server.ts
const RESOURCE_URI = "ui://weather-forecast/mcp-app.html";
registerAppTool(server, TOOL_NAME, {
inputSchema: { location: z.string(), units: ..., days: ... },
_meta: { ui: { resourceUri: RESOURCE_URI } }, // <-- the link
}, handler);
registerAppResource(server, "Weather Forecast Chart", RESOURCE_URI, {
mimeType: RESOURCE_MIME_TYPE,
}, () => /* the built HTML */);Drop _meta.ui.resourceUri and everything still "works" — the tool silently
degrades to plain text with no UI. That failure is invisible, which is why
tests/server/server.test.ts asserts on it explicitly.
Three things worth internalizing from this codebase:
The payload split. The handler returns
content[0].text(a short summary — cheap tokens for the model) andstructuredContent(the full typed series — for the chart). Never make the model pay for 40 data points it won't read. Seeserver.ts.The UI is a client, not a template.
src/mcp-app.tsxusesuseApp()and callsapp.callServerTool(...)when you change units or day range. That round trip is the entire reason to reach for MCP Apps.ui://resources declare their own CSP. Everything is inlined into one HTML file except the weather icons, so the resource declares_meta.ui.csp.resourceDomains: ["https://openweathermap.org"]— a small deliberate example of the field.
Related MCP server: Open Weather13 MCP Server
Setup
npm install
cp .env.example .env # then paste your key in
npm run build # typechecks, then bundles the UI into dist/mcp-app.htmlThe free OpenWeather tier is enough. New keys take ~10 minutes to activate; until then every call returns 401.
Running it
Two transports, one entry point, to show both ends of the spectrum:
npm run serve:stdio # stdio -- simplest local wiring, host spawns the process
npm run serve # Streamable HTTP on :3001/mcp -- shareable, tunnelable
npm run dev # rebuild UI on change + serve over HTTPstdio gotcha: stdout is the JSON-RPC channel. One stray console.log
corrupts the stream and the host reports an unparseable message. Every
diagnostic in main.ts uses console.error.
The HTTP mode is stateless — sessionIdGenerator: undefined, with a fresh
server and transport per request, closed on res.close. Nothing to reap.
Sharing the HTTP server as a custom connector:
npx cloudflared tunnel --url http://localhost:3001
# add the resulting https URL + /mcp as a custom connectorAdding it to the Claude desktop app
Written for someone who has never set up an MCP server. A Claude Pro account is enough. Claude Desktop supports the MCP Apps extension, so the chart renders inline rather than falling back to text.
You'll wire this up as a local stdio server: Claude Desktop starts the process itself, so there's no hosting, no tunnel, and no login. Roughly 10 minutes.
1. Install the prerequisites
Claude Desktop — https://claude.ai/download. If it's already installed, use Check for Updates… in the Claude menu.
Node.js 20+ — https://nodejs.org (the LTS build). Verify in a terminal:
node --version
2. Set the project up
git clone https://github.com/kasza-introhive/mcp-app-openweather.git
cd mcp-app-openweather
npm install
npm run build # REQUIRED -- creates dist/mcp-app.html, which IS the chartSkipping npm run build is the most common mistake: the server starts fine and
the tool returns data, but asking for the UI errors out because the bundle it
serves doesn't exist yet.
3. Add your OpenWeather API key
Sign up at https://openweathermap.org/api and copy your key from https://home.openweathermap.org/api_keys — the free tier is all this needs.
cp .env.example .envOpen .env and paste the key after OPENWEATHER_API_KEY=. A brand-new key can
take ~10 minutes to activate; until then every request returns 401.
4. Find your absolute paths
Claude Desktop doesn't run with your shell's PATH, so both the Node binary and
the project directory must be spelled out in full. Print them:
command -v node # e.g. /usr/local/bin/node (Windows: where node)
pwd # e.g. /Users/you/code/mcp-app-openweather5. Edit the Claude Desktop config
Open the Claude menu in your OS menu bar (not the settings inside the chat window) → Settings… → Developer tab → Edit Config. That creates or opens:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.json
Add this, substituting the two paths from step 4:
{
"mcpServers": {
"openweather-forecast": {
"command": "/absolute/path/to/node",
"args": [
"--env-file-if-exists=.env",
"--import", "tsx",
"main.ts",
"--stdio"
],
"cwd": "/absolute/path/to/mcp-app-openweather"
}
}
}If the file already has an mcpServers block, add "openweather-forecast"
alongside your existing entries rather than replacing them.
Two things here are load-bearing, and both cause failures that look like something else:
Call
nodedirectly, notnpm run serve:stdio.npmprints a two-line banner to stdout, and under stdio stdout is the JSON-RPC channel — the banner corrupts the first message and the server appears to fail for no reason. The npm scripts are for humans in a terminal.cwdis required.--import tsxresolvestsxfrom the working directory, so without it Node exits withERR_MODULE_NOT_FOUND.
On Windows, write paths with escaped backslashes:
"C:\\Program Files\\nodejs\\node.exe".
6. Restart and confirm
Quit Claude Desktop completely (⌘Q / right-click → Quit — closing the window isn't enough) and reopen it. Config is only read at startup.
Click the Add files, connectors, and more (/) control at the bottom-left of
the message box, hover Connectors → Manage connectors, and look for
openweather-forecast with get-forecast listed under it.
7. Ask for a forecast
"What's the weather forecast for Halifax this week?"
Claude will ask permission to run get-forecast the first time — approve it. An
interactive chart appears in the conversation. Click Humidity to reshape the
data instantly (no server call), or °F / 1 day to trigger a fresh
tools/call.
If it doesn't show up
Read the logs first — they name the actual cause:
# macOS
tail -n 50 -f ~/Library/Logs/Claude/mcp-server-openweather-forecast.log
# Windows
type "%APPDATA%\Claude\logs\mcp-server-openweather-forecast.log"Symptom | Cause |
Connector absent entirely | Invalid JSON in the config (a trailing comma is the usual culprit), or Claude wasn't fully quit |
|
|
| The |
Unparseable-message errors | Launched via |
Tool errors mentioning |
|
Chart never renders but text works |
|
To confirm the server itself is healthy, independent of Claude, run it by hand from the project directory — it should print one line of JSON:
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"c","version":"1"}}}' \
| node --env-file-if-exists=.env --import tsx main.ts --stdioTry it
"What's the weather forecast for Halifax this week?"
The chart appears in the chat. Switching metric (temp / humidity / wind /
pressure) reshapes the data already on the client — no server call. Switching
units or day range issues a real tools/call, because only the server
has that data.
Layout
main.ts transport selection: --stdio | Streamable HTTP
server.ts createServer(): the tool + the ui:// resource
src/openweather.ts the ONLY module that talks to OpenWeather
src/mcp-app.tsx React root, useApp(), callServerTool
src/components/ ForecastChart (Recharts), Controls
tests/unit/ openweather: transform, schemas, error mapping
tests/server/ the MCP boundary, over an in-memory transport
tests/e2e/ built bundle + a fake host bridge
tests/contract/ opt-in live API call (schema drift)src/openweather.ts validates both upstream responses with zod before use —
it's a trust boundary — and maps failures to messages that never interpolate the
response body, since OpenWeather can echo the appid back. There's a test
asserting the key never appears in an error message.
Testing
npm test # unit + server. Hermetic: MSW intercepts all HTTP.
npm run test:e2e # Playwright against dist/mcp-app.html (run npm run build first)
npm run test:live # opt-in: one REAL API call, asserts schema onlyMSW runs with onUnhandledRequest: "error", so an accidental real network call
fails the suite rather than passing quietly.
tests/e2e/fake-host.ts is a ~100-line host implementation and doubles as
protocol documentation: it answers ui/initialize, pushes
ui/notifications/tool-result, and serves tools/call from a queue while
recording each call. Three things that cost real debugging time:
McpUiInitializeResultrequiresprotocolVersion,hostInfo,hostCapabilities(notcapabilities), andhostContext. Get one wrong and the app renders "Could not connect to the host".The tool-result notification's
paramsis the tool result —content/structuredContentsit at the top level ofparams, not nested under aresultkey.The app must run in an iframe. It posts to
window.parent, so loaded top-level it just talks to itself. Hencetests/e2e/harness.html. And becauseaddInitScriptruns in every frame, the fake host bails out withif (window.parent !== window) return;or the two frames ping-pong forever.
On a restricted machine where Chromium can't do its normal multi-process launch,
set PW_SINGLE_PROCESS=1 (see playwright.config.ts). Note that under
--single-process the browser doesn't survive closing a context, so only the
first test per worker runs — pass -g "<test title>" to run them one at a time.
Verifying against a real host
The fake bridge proves this app's logic, not host compatibility — it doesn't enforce the iframe sandbox or CSP. For that:
git clone https://github.com/modelcontextprotocol/ext-apps
cd ext-apps/examples/basic-host && npm install
SERVERS='["http://localhost:3001/mcp"]' npm start # then open localhost:8080Confirm the chart draws, the icons load (that's the CSP allowance working), and a
control change shows a new tools/call in the server log.
Why forecast and not history
The original goal was historical weather. OpenWeather's historical endpoints
(One Call 3.0 timemachine and day_summary) need a separate paid "One Call by
Call" subscription and return 401 on a free-tier key. So this uses the free
5 day / 3 hour forecast instead. Don't spend an afternoon rediscovering that.
POC caveats
Bundle size.
vite-plugin-singlefileinlines React and Recharts into one ~920 KB HTML resource. Fine for a demo, not a production recommendation — a real app would trim the chart library or load from a CDN with the rightresourceDomains.updateModelContextis deliberately unimplemented.src/mcp-app.tsxcarries a commentedreportExploration()stub marking where the app would tell the model what the user explored. Left as an exercise.No auth, no caching, no in-app location search.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityDmaintenanceAn MCP server that provides real-time weather data, hourly forecasts, and daily summaries using the free Open-Meteo API with no API key required. It enables users to search for weather conditions by specific coordinates or city names across multiple measurement units.1MIT
- FlicenseCqualityDmaintenanceAn MCP server that provides access to current weather data and 5-day forecasts via the Open Weather13 API. It enables users to retrieve weather information using city names or geographic coordinates across various languages.3
- Flicense-qualityCmaintenanceAn MCP server that provides current weather conditions and forecasts via OpenWeatherMap API to AI agents.
- Flicense-qualityBmaintenanceAn MCP server that provides real-time weather data and forecasts via Open-Meteo, with tools for current conditions, multi-day forecasts, umbrella predictions, and travel recommendations.
Related MCP Connectors
This MCP server provides seamless access to Malaysia's government open data, including datasets, w…
An MCP server that integrates with Discord to provide AI-powered features.
MCP server for AI dialogue using various LLM models via AceDataCloud
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/kasza-introhive/mcp-app-openweather'
If you have feedback or need assistance with the MCP directory API, please join our Discord server