mcp-server-simpsons-cities
by cindyhsugit
README.md
# ποΈ mcp-server-simpsons-cities
An [MCP](https://modelcontextprotocol.io) (Model Context Protocol) server that finds real U.S. cities near a given zip code whose names match anything from *The Simpsons* β not just towns like Springfield and Shelbyville, but character names, businesses, and other references from the show. If there's a real town near you called "Maggie," this tool finds it.
Built as an end-to-end MCP integration: a Python server exposing a live tool to [Claude Desktop](https://claude.ai/download), backed by a real geospatial API and a self-scraped reference dataset.
---
## Why this project
This started as a fun, low-stakes way to explore the full MCP development lifecycle β from writing and debugging a Python server, to authenticating against a third-party API, to wiring it into a real LLM client and iterating based on live tool-call failures. It touches:
- **API integration** β authenticated third-party REST API (GeoNames), with real debugging around HTTPS certificate mismatches, auth propagation delays, and rate limits
- **Data engineering** β a standalone scraper that paginates a MediaWiki API, dedupes, and filters noisy category data into a clean reference dataset
- **Protocol-level tool design** β exposing a single well-scoped tool via FastMCP rather than forcing an LLM client to chain multiple raw API calls itself
- **Environment & secrets management** β `.env`-based config, gitignored credentials, environment variables passed cleanly into a subprocess-launched server
- **Client integration & debugging** β wiring the server into Claude Desktop's `claude_desktop_config.json`, and using the MCP Inspector plus stderr logging to diagnose failures that only appeared once the tool was actually called end-to-end
---
## Demo
**Live tool call in Claude Desktop** (used here as the MCP client) β a natural-language question, routed automatically to the right tool, answered with a live API result:
### β
True positive β a real match found

### β Correct negative β no match, and Claude proactively offers next steps

---
## Architecture
```
βββββββββββββββββββββββ
"Any Simpsons- β Claude Desktop β
named cities β (MCP Client) β
near 62701?" ββββΆβ β
ββββββββββββ¬ββββββββββββ
β MCP protocol (stdio)
βΌ
βββββββββββββββββββββββ
β find_simpsons_ β
β cities.py β
β (FastMCP server) β
ββββββββββββ¬ββββββββββββ
β
βββββββββββββββ΄ββββββββββββββ
βΌ βΌ
ββββββββββββββββββββββ βββββββββββββββββββββββββ
β GeoNames API β β data/simpsons_ β
β (nearby cities by β β names.json β
β zip + radius) β β (pre-built, scraped β
ββββββββββββββββββββββ β from Wikisimpsons) β
βββββββββββββββββββββββββ
```
**Request flow:**
1. User asks a natural-language question in Claude Desktop.
2. Claude Desktop, acting as the MCP client, recognizes the intent and calls the `get_simpsons_city` tool over the MCP protocol.
3. The server queries GeoNames for real cities within a radius of the given zip code.
4. Each city name is checked (case-insensitively) against a locally-cached Simpsons name database.
5. Matches β or a clear "no matches" result β are returned to Claude, which formats a natural-language response.
---
## Tools exposed
| Tool | Description |
|---|---|
| `get_simpsons_city(zipcode, radius_km=30)` | Finds real cities near a U.S. zip code that share a name with a character, location, or business from *The Simpsons*. |
---
## Tech stack
- **[FastMCP](https://github.com/modelcontextprotocol/python-sdk)** (`mcp[cli]`, pinned to v1.x) β MCP server framework
- **[httpx](https://www.python-httpx.org/)** β async HTTP client for both the GeoNames API and the Wikisimpsons scraper
- **[uv](https://docs.astral.sh/uv/)** β Python dependency & environment management
- **[GeoNames](https://www.geonames.org/)** β free geospatial API for zip-code-radius city lookups
- **[Wikisimpsons (Fandom)](https://simpsons.fandom.com/)** β source data via the MediaWiki `categorymembers` API
- **[Claude Desktop](https://claude.ai/download)** β MCP client used for testing and live usage
- **[MCP Inspector](https://modelcontextprotocol.io/docs/tools/debugging)** β interactive tool-testing UI during development
---
## Getting started
### Prerequisites
- Python 3.13+
- [`uv`](https://docs.astral.sh/uv/getting-started/installation/)
- A free [GeoNames](https://www.geonames.org/login) account with the free web service **enabled** on your [account page](https://www.geonames.org/manageaccount) β this is a separate step from registering, and easy to miss.
### Install
```bash
git clone https://github.com/cindyhsugit/mcp-server-simpsons-cities.git
cd mcp-server-simpsons-cities
uv sync
```
### Configure
Create a `.env` file in the project root:
```
GEONAMES_USERNAME=your_geonames_username
```
> `.env` is gitignored β never commit real credentials.
### (Optional) Rebuild the Simpsons name database
A pre-built `data/simpsons_names.json` ships with the repo. To refresh it from Wikisimpsons:
```bash
uv run build_data.py
```
This paginates through `Category:Characters`, `Category:Locations`, and `Category:Businesses` via the Fandom MediaWiki API, dedupes results, and writes a clean JSON list.
---
## Running & testing
**Standalone (sanity-check startup):**
```bash
uv run find_simpsons_cities.py
```
**Interactive testing (requires Node.js):**
```bash
uv run mcp dev find_simpsons_cities.py
```
Opens the MCP Inspector in your browser for calling `get_simpsons_city` directly with real inputs and inspecting raw responses.
---
## Connecting to Claude Desktop
This project was built and tested using **Claude Desktop as the MCP client**.
Open your config file:
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
Add:
```json
{
"mcpServers": {
"simpsons-cities": {
"command": "uv",
"args": [
"--directory",
"/absolute/path/to/mcp-server-simpsons-cities",
"run",
"find_simpsons_cities.py"
],
"env": {
"GEONAMES_USERNAME": "your_geonames_username"
}
}
}
}
```
Fully quit and reopen Claude Desktop, then just ask:
> "Are there any Simpsons-named cities near zip code 62701?"
---
## Project structure
```
mcp-server-simpsons-cities/
βββ find_simpsons_cities.py # MCP server β defines get_simpsons_city tool
βββ build_data.py # Standalone scraper β builds data/simpsons_names.json
βββ data/
β βββ simpsons_names.json # Scraped Simpsons character/location/business names
βββ screenshots/
β βββ example-match-springfield.png
β βββ example-no-match-92840.png
βββ pyproject.toml
βββ .env # (gitignored) GeoNames credentials
βββ README.md
```
---
## Engineering notes / lessons learned
A few real issues hit and resolved during development, kept here for transparency:
- **`mcp` v2 breaking change** β a new major SDK release renamed `FastMCP` to `MCPServer`, silently breaking `from mcp.server.fastmcp import FastMCP`. Fixed by pinning `mcp[cli]>=1.28,<2` in `pyproject.toml`.
- **GeoNames certificate mismatch** β `https://api.geonames.org` serves a certificate valid for `geonames.net`, not `.org`, causing browser/TLS warnings. Resolved by using plain HTTP for the free-tier endpoint (the pattern GeoNames' own official examples use).
- **GeoNames "premium-only" trap** β `secure.geonames.org` looks like the natural HTTPS alternative, but it's a premium-tier-only endpoint and returns `401` for free accounts.
- **Silent account activation delay** β enabling the free web service on a GeoNames account doesn't always propagate instantly; requests can 401 for several minutes after enabling before working.
- **Debuggability** β added structured `stderr` logging in the HTTP layer specifically so failures surface in the MCP Inspector / Claude Desktop's developer log instead of collapsing into a generic error message.
---
## Known limitations
- **30 km search radius** β the free-tier cap on GeoNames' `findNearbyPostalCodes` endpoint.
- **Exact-match only** β a city name must match a Simpsons name exactly (case-insensitive). "Springfield Heights" would not match "Springfield." Fuzzy/substring matching is a natural next step.
- **US zip codes only** β scoped to `country=US` for now.
---
## License
MIT
TDQS
A4.5/5.0
Scored across 1 tool
Disambiguation5/5
With only one tool, there is no possibility of confusion or overlap. The tool's purpose is clearly distinct.
Naming Consistency5/5
The single tool name 'get_simpsons_city' follows a clear verb_noun pattern, which is consistent throughout the server.
Tool Count3/5
The server has only 1 tool, which is borderline for scope. While the narrow purpose could justify a single tool, the count is minimal and might feel thin if users expect additional related operations.
Completeness5/5
The server's domain is specifically finding Simpsons-related cities near a zip code. The single tool directly fulfills this purpose with no obvious missing functionality or dead ends.
Maintenance
ActivityStale
ResponsivenessNo issues