zip-codes-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # Zip Codes MCP
2
+
3
+ Look up US postal codes, cities, and states from the public Zippopotam API. No key required.
4
+
5
+ This file is self contained. It reads public data only and never writes to the machine. All output is bounded and honest about what could not be fetched.
6
+
7
+ ## Tools
8
+
9
+
10
+ * `zip_lookup` City and state for a zip.
11
+ * `city_lookup` Zip codes for a city.
12
+
13
+ ## Usage
14
+
15
+ ```bash
16
+ npm install
17
+ npm run build
18
+ node dist/index.js
19
+ ```
20
+
21
+ Data comes from the public Zippopotam API.
package/dist/api.js ADDED
@@ -0,0 +1,35 @@
1
+ const BASE = "https://api.zippopotam.us/us";
2
+ const UA = "mrfentmen-zip-codes-mcp/1.0 (https://github.com/mrfentmen)";
3
+ export class ZipError extends Error {
4
+ }
5
+ async function get(url) {
6
+ const res = await fetch(url, { headers: { "User-Agent": UA, Accept: "application/json" }, signal: AbortSignal.timeout(20000) });
7
+ if (res.status === 429)
8
+ throw new ZipError("Zippopotam rate limit hit, wait and retry");
9
+ if (!res.ok)
10
+ throw new ZipError(`Zippopotam error ${res.status}`);
11
+ return (await res.json());
12
+ }
13
+ export async function zipLookup(args) {
14
+ const zip = (args.zip ?? "").trim();
15
+ if (!/^\d{5}$/.test(zip))
16
+ throw new ZipError("Provide a five digit US zip code");
17
+ const d = await get(`${BASE}/${zip}`);
18
+ const places = d?.places ?? [];
19
+ if (!places.length)
20
+ return `No data for ${zip}`;
21
+ const postCode = d?.["post code"] ?? zip;
22
+ return places.map((p) => `${postCode} | ${p?.["place name"] ?? "n/a"}, ${p?.["state abbreviation"] ?? ""} ${p?.longitude ?? ""}, ${p?.latitude ?? ""}`).join("\n");
23
+ }
24
+ export async function cityLookup(args) {
25
+ const city = (args.city ?? "").trim().toLowerCase();
26
+ const state = (args.state ?? "").trim().toLowerCase();
27
+ if (!city || !state)
28
+ throw new ZipError("Provide a city and two letter state");
29
+ const d = await get(`${BASE}/${encodeURIComponent(city)}/${state}`);
30
+ const places = d?.places ?? [];
31
+ if (!places.length)
32
+ return `No data for ${city}, ${state}`;
33
+ const zips = [...new Set(places.map((p) => p?.["post code"]).filter(Boolean))];
34
+ return `${city[0].toUpperCase()}${city.slice(1)}, ${state.toUpperCase()}\nZip codes: ${zips.join(", ")}`;
35
+ }
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2
+ import { createServer } from "./server.js";
3
+ const main = async () => { const server = createServer(); await server.connect(new StdioServerTransport()); };
4
+ main().catch((error) => { console.error("Fatal error:", error); process.exit(1); });
package/dist/server.js ADDED
@@ -0,0 +1,38 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { cityLookup } from "./api.js";
4
+ import { zipLookup } from "./api.js";
5
+ const text = (value) => ({ content: [{ type: "text", text: value }] });
6
+ const textError = (t) => ({ content: [{ type: "text", text: t }], isError: true });
7
+ const READ_ONLY = { readOnlyHint: true, openWorldHint: true };
8
+ const error = (e) => `Error: ${e instanceof Error ? e.message : String(e)}`;
9
+ export function createServer() {
10
+ const server = new McpServer({ name: "zip-codes-mcp", version: "1.0.0" });
11
+ server.registerTool("zip_lookup", {
12
+ title: "Zip lookup",
13
+ description: "Get the city and state for a US zip code.",
14
+ inputSchema: z.object({ zip: z.string().describe("Five digit US zip code.") }),
15
+ annotations: READ_ONLY,
16
+ }, async (args) => {
17
+ try {
18
+ return text(await zipLookup(args));
19
+ }
20
+ catch (e) {
21
+ return textError(error(e));
22
+ }
23
+ });
24
+ server.registerTool("city_lookup", {
25
+ title: "City lookup",
26
+ description: "List zip codes for a city and state.",
27
+ inputSchema: z.object({ city: z.string().describe("City name."), state: z.string().describe("Two letter state code.") }),
28
+ annotations: READ_ONLY,
29
+ }, async (args) => {
30
+ try {
31
+ return text(await cityLookup(args));
32
+ }
33
+ catch (e) {
34
+ return textError(error(e));
35
+ }
36
+ });
37
+ return server;
38
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "type": "module",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "https://github.com/mrfentmen/zip-codes-mcp.git"
7
+ },
8
+ "bin": {
9
+ "zip-codes-mcp": "./dist/index.js"
10
+ },
11
+ "main": "./dist/index.js",
12
+ "files": [
13
+ "dist",
14
+ "server.json",
15
+ "README.md"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc -p tsconfig.json",
19
+ "start": "node dist/index.js",
20
+ "dev": "npm run build && node dist/index.js"
21
+ },
22
+ "license": "MIT",
23
+ "dependencies": {
24
+ "@modelcontextprotocol/sdk": "^1.0.4",
25
+ "zod": "^3.23.8"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^22.0.0",
29
+ "typescript": "^5.6.0"
30
+ },
31
+ "name": "zip-codes-mcp",
32
+ "description": "Look up US postal codes, cities, and states from Zippopotam. No key required.",
33
+ "mcpName": "io.github.mrfentmen/zip-codes-mcp",
34
+ "keywords": [
35
+ "mcp",
36
+ "zip",
37
+ "postal",
38
+ "location",
39
+ "address"
40
+ ],
41
+ "engines": {
42
+ "node": ">=20"
43
+ }
44
+ }
package/server.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.mrfentmen/zip-codes-mcp",
4
+ "description": "Look up US postal codes, cities, and states from Zippopotam. No key required.",
5
+ "repository": {
6
+ "url": "https://github.com/mrfentmen/zip-codes-mcp",
7
+ "source": "github"
8
+ },
9
+ "version": "1.0.0",
10
+ "packages": [
11
+ {
12
+ "registryType": "npm",
13
+ "identifier": "zip-codes-mcp",
14
+ "version": "1.0.0",
15
+ "transport": {
16
+ "type": "stdio"
17
+ }
18
+ }
19
+ ]
20
+ }