Building my first MCP server: Spain's weather API and its two-step catch
A backend engineer shares insights on developing their first Model Context Protocol server using Spain's AEMET API, covering two-step retrieval patterns and encoding challenges.

Stock photo for illustration only, not from the actual event
- Backend engineers are moving into AI engineering by building and publishing their own Model Context Protocol (MCP) servers.
- Spain's AEMET open weather API served as a clean, copyright-free sandbox for learning MCP mechanics.
- Key engineering hurdles included a unique two-step data retrieval pattern and ISO-8859-1 character encoding issues.
- The ultimate goal is to build an autonomous incident-investigation agent for Kubernetes diagnostics.
As a backend engineer accustomed to Java, Spring, and Kubernetes, transitioning toward AI engineering meant wanting to actually ship something in the agent ecosystem rather than just reading about it. This led to building and publishing a small Model Context Protocol (MCP) server as the first installment in a planned series of progressively harder projects. The goal was to close the full loop by building, publishing to npm, listing in the official MCP registry, and achieving discoverability.
The chosen subject was AEMET, Spain's national weather agency, which offers a free public API with straightforward authentication via an API key and no legal gray areas. It provided a clean sandbox to learn the mechanics. What was unexpected, however, was that this straightforward API harbored the two most interesting engineering lessons of the entire exercise.
Model Context Protocol (MCP) is an open standard that enables AI clients such as Claude Desktop or IDE agents to interact with external tools through a uniform interface, eliminating the need for custom integrations for every individual platform.
The server exposed a few specific typed tools:
- get_municipality_forecast for fetching forecasts by municipality using INE codes
- get_station_observation for observation data from meteorological stations
- get_weather_warnings for active regional weather warnings
The tech stack utilized Node.js and TypeScript, the official @modelcontextprotocol/sdk, stdio transport, and Zod for input validation.

Stock photo for illustration only, not from the actual event
The standout feature was AEMET's two-step OpenData pattern. The initial request does not return weather data directly; instead, it returns a pointer. The JSON response contains a datos field pointing to a URL such as https://opendata.aemet.es/opendata/sh/abc123 containing zero temperature data. Developers must then execute a second request to that specific URL without an API key to retrieve the actual payload. This mirrors AWS S3 presigned URLs or a manual HTTP 302 redirect embedded inside a JSON body rather than a Location header.
Architecturally, isolating both hops behind a single client function prevents tools from needing to know the underlying pattern. Furthermore, undocumented pitfalls included discrepancies where transport-level HTTP responses returned 200 OK while the JSON body reported "estado": 404 or 401. Another significant hurdle was character encoding: AEMET serves content in ISO-8859-1 (latin1) rather than UTF-8. A naive await response.json() call mangles Spanish accents like Cádiz into Cdiz, requiring developers to read bodies as buffers and decode them explicitly.
Regarding distribution, npm publishing failures typically stem from missing a bin field in package.json pointing to compiled code or omitting the shebang line#!/usr/bin/env node at the entry file. Additionally, stdio transport strictly prohibits writing anything other than JSON-RPC messages to stdout, as stray console.log statements corrupt the message stream and silently break the server, requiring logs to be directed to stderr instead.
Source: Dev.to
Found something wrong in this article? Report an issue with this article
Comments
Leave a Comment