Skip to main content
Glama

🌫️ sg-haze-rain-mcp

Ask your AI agent "how's the haze?" and get Singapore's live PSI, PM2.5 and rain, straight from NEA, for exactly where you are.

CI License: MIT Node ≥ 18.17 MCP Data: NEA via data.gov.sg PRs welcome

English · 简体中文

Install in Cursor Install in VS Code


Every haze season, Singaporeans refresh the NEA app, squint at five regional numbers and try to remember what "Unhealthy" means for a 6 pm run. sg-haze-rain-mcp puts that knowledge inside the AI tools you already have open. It is a zero-config Model Context Protocol server for Claude Code, Codex, Claude Desktop, Cursor, VS Code, Windsurf, Zed and any other MCP client.

  • 🧭 Knows where you are. Geolocates your public IP and picks the nearest of NEA's five PSI regions. Or pass a region, coordinates or an IP explicitly.

  • 🏥 Speaks NEA's language. Every reading comes with the official band and the three-tier health advisory (general public · elderly, pregnant women and children · chronic lung or heart conditions).

  • 🌧️ Rain, not just haze. Nearest three rain gauges with 5-minute totals, the 2-hour area nowcast, and how many of ~90 gauges are wet island-wide.

  • 🔑 No API key, no account. Pure public data. One npx command and you are done.

  • 🌏 English and Chinese labels and advisories, per call or by default.

  • 🤖 Agent-friendly. Human-readable text plus structuredContent JSON in every result, so an agent can quote the summary or branch on the numbers.

> how's the haze right now, and can I run at 6?

🌫️ Singapore haze — 28 Sept 2026, 21:46 SGT
Location: Bishan, Singapore → nearest PSI region: central (located via public IP)

24-hr PSI: 108 — Unhealthy (101–200)
1-hr PM2.5: 119 µg/m³ — Elevated (Band II)
24-hr PM2.5: 63 µg/m³ · 24-hr PM10: 72 µg/m³

Health advisory (Unhealthy):
• General public: Reduce prolonged or strenuous outdoor physical exertion.
• Elderly, pregnant women, children: Minimise prolonged or strenuous outdoor physical exertion.
• Chronic lung / heart conditions: Avoid prolonged or strenuous outdoor physical exertion.

All regions (24-hr PSI): north 75 · south 105 · east 90 · west 101 · central 108

☀️ No rain nearby — 21:45 SGT
• Macritchie Reservoir (1.9 km): 0 mm  • Lower Peirce Reservoir (2.1 km): 0 mm  • Upper Peirce (2.6 km): 0 mm
2-hour forecast Bishan: Cloudy — 9.30 pm to 11.30 pm
Island-wide: 0/89 stations reporting rain

Quick start

Requires Node.js 18.17+ (node --version). Nothing else: no sign-up, no key.

claude mcp add --scope user sg-haze-rain -- npx -y github:ericwang915/sg-haze-rain-mcp

Prefer Chinese labels by default:

claude mcp add --scope user --env SG_HAZE_LANG=zh sg-haze-rain -- npx -y github:ericwang915/sg-haze-rain-mcp

Run /mcp inside Claude Code to confirm the server is connected with five tools. To share with a team, commit a project-level .mcp.json instead.

codex mcp add sg-haze-rain -- npx -y github:ericwang915/sg-haze-rain-mcp

or in ~/.codex/config.toml:

[mcp_servers.sg_haze_rain]
command = "npx"
args = ["-y", "github:ericwang915/sg-haze-rain-mcp"]
# env = { SG_HAZE_LANG = "zh" }

Add to claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\):

{
  "mcpServers": {
    "sg-haze-rain": {
      "command": "npx",
      "args": ["-y", "github:ericwang915/sg-haze-rain-mcp"]
    }
  }
}

Use the one-click badges at the top, or paste the same command / args pair into that client's MCP config (.cursor/mcp.json, .vscode/mcp.json, …):

{ "command": "npx", "args": ["-y", "github:ericwang915/sg-haze-rain-mcp"] }
# pin to a release tag
npx -y "github:ericwang915/sg-haze-rain-mcp#v0.1.0"

# install once, then reference the binary
npm install -g github:ericwang915/sg-haze-rain-mcp
claude mcp add sg-haze-rain -- sg-haze-rain-mcp

# hack on it
git clone https://github.com/ericwang915/sg-haze-rain-mcp.git && cd sg-haze-rain-mcp
npm install            # builds dist/ via prepare
claude mcp add sg-haze-rain -- node "$PWD/dist/index.js"

The first npx run downloads and builds (about 20 s); later starts come from the npx cache and are instant.

Things to ask

  • "How's the haze right now?"

  • "Is it safe for my kids to play outside this afternoon?"

  • "Will it rain in the next two hours in Jurong?"

  • "Compare PSI across all regions."

  • "What's the 4-day outlook?"

  • "Should I bring an umbrella to Changi at 5 pm?"

Related MCP server: data-gov-sg

Tools

Tool

Purpose

Key fields in structuredContent

weather_now

One call: haze + rain + 2-hour forecast for your spot. Start here.

haze, rain

get_haze

24-hr PSI, 1-hr PM2.5, 24-hr PM2.5 / PM10, NEA band, advisory, all five regions, pollutant sub-indices.

psi24h, psiBand, pm25_1h, advisory, allRegions, subIndices

get_rain

Nearest three gauges (5-minute totals), 2-hour area forecast, island-wide wet-station count.

rainingNearby, rainExpectedWithin2h, nearestStations, twoHourForecast

get_forecast

horizon: "2h" area nowcast · "24h" (default) general + your region per period · "4d" outlook.

periods, days, nearestAreas

locate_me

Shows the location and PSI region the other tools will use, and how it was decided.

location, region

All tools take the same optional inputs: region (north · south · east · west · central), lat + lon, ip, lang (en · zh).

One prompt ships too: haze_check (activity: "run 5 km at 6pm") asks the assistant for a plain-language go / no-go based on the current readings.

How it works

flowchart LR
    A[MCP client<br/>Claude Code · Codex · Cursor …] -- stdio --> S[sg-haze-rain-mcp]
    S --> L{Where is the caller?}
    L -- region given --> R[Region centroid]
    L -- lat/lon given --> C[Coordinates]
    L -- otherwise --> IP[Public IP → city<br/>ipwho.is ⟶ ipapi.co]
    R & C & IP --> N[Nearest PSI region<br/>+ nearest gauges / area]
    N --> F[NEA real-time feeds<br/>data.gov.sg v2]
    F --> B[NEA bands +<br/>health advisory]
    B --> A

Location resolution is strictly ordered: an explicit region wins, then lat/lon, then a given ip, then the machine's own public IP. If the IP lands outside Singapore or the lookup fails, the server falls back to a default region and says so in the reply, so the agent never silently reports the wrong place.

Feeds are cached in-process for 60 s. data.gov.sg rate-limits bursts with HTTP 429; the client retries with backoff, honours Retry-After, and if the feed still refuses it serves the last good copy it holds rather than failing a "how is the haze" question.

Data

Everything comes from the National Environment Agency (NEA), published on Singapore's open-data platform data.gov.sg. No key, same data as the NEA app.

Feed

Endpoint (https://api-open.data.gov.sg/v2/real-time/api/…)

Refresh

24-hr PSI, 24-hr PM2.5 / PM10, pollutant sub-indices, 5 regions

psi

hourly

1-hr PM2.5, 5 regions

pm25

hourly

Rainfall, 5-minute totals, ~90 gauges

rainfall

every 5 min

2-hour area forecast, 47 areas

two-hr-forecast

every 30 min

24-hour forecast, general + regional periods

twenty-four-hr-forecast

several times daily

4-day outlook

four-day-outlook

daily

NEA bands, as reported

24-hr PSI

PSI

Band

General public

0–50

Good

Normal activities

51–100

Moderate

Normal activities

101–200

Unhealthy

Reduce prolonged or strenuous outdoor exertion

201–300

Very Unhealthy

Avoid prolonged or strenuous outdoor exertion

> 300

Hazardous

Minimise outdoor activity; N95 for anyone outdoors for hours

Replies also include the stricter guidance NEA gives for the elderly, pregnant women, children, and people with chronic lung or heart disease.

1-hr PM2.5 (µg/m³), NEA's indicator for the coming hours: 0–55 Normal (I) · 56–150 Elevated (II) · 151–250 High (III) · > 250 Very High (IV).

Privacy

With no location inputs, the server asks a public IP-geolocation service where your public IP is: ipwho.is first, ipapi.co as fallback. Both are free and keyless. Only the returned coordinates and city name are used; nothing is stored. Resolution is city-level, and a VPN or corporate egress will move you.

Do not want that call at all?

  • SG_HAZE_IP_LOOKUP=off (optionally with SG_HAZE_DEFAULT_REGION=west), or

  • always pass region or lat / lon from the client.

Requests to NEA / data.gov.sg carry no information about you; the feeds are identical for everyone.

Configuration

Variable

Default

Effect

SG_HAZE_IP_LOOKUP

on

off disables IP geolocation entirely

SG_HAZE_DEFAULT_REGION

central

Region used when location is unknown or outside Singapore

SG_HAZE_LANG

en

zh for Chinese labels and advisories; per-call lang overrides

SG_HAZE_CACHE_SECONDS

60

How long a fetched feed is reused

SG_HAZE_API_BASE

data.gov.sg v2 URL

Point at a mirror or a test server

Pass them with --env KEY=value in claude mcp add, an env table in Codex's config.toml, or an env object in JSON configs.

Development

npm install          # installs and builds
npm run build        # tsc → dist/
npm test             # build + stdio smoke test against the live feeds
node dist/index.js --help

test/smoke.mjs speaks raw JSON-RPC to the built server, lists the tools and calls each one with explicit regions or coordinates. It sets SG_HAZE_IP_LOOKUP=off, so running the tests never geolocates your machine. CI runs it on Node 18, 20 and 22.

src/index.ts    stdio entry point, --help / --version
src/server.ts   tool + prompt definitions, text formatting (en / zh)
src/nea.ts      NEA feed client: camel/snake normalisation, cache, 429 handling
src/geo.ts      region / coords / IP resolution, haversine nearest-neighbour
src/bands.ts    PSI and PM2.5 bands and NEA health advisories

Roadmap

  • UV index, air temperature, humidity and wind from the same NEA feed family

  • Haze trend: compare the current PSI with 3 and 6 hours ago

  • Optional HTTP / SSE transport for hosted deployments

  • More languages for labels and advisories (Malay, Tamil)

Have a use case? Open an issue or see CONTRIBUTING.md.

FAQ

Does it need npm? It needs Node.js 18.17+. npx ships with Node, so if you can run Claude Code or Codex you already have everything.

Why five regions and not my street? NEA publishes PSI for north, south, east, west and central only. Rain, however, is gauge-level, so the nearest three gauges are usually within 2–3 km.

Is this official? No. It relays NEA's published data unchanged. For official advisories see haze.gov.sg and nea.gov.sg.

I am outside Singapore. The server notices, uses the default region, and tells the agent so. Pass region to pick one deliberately.

License

MIT. Data © National Environment Agency, Singapore, under the Singapore Open Data Licence.

If this saves you a trip to the NEA app, a ⭐ helps other Singaporeans find it.

Made by the team behind Audiee.ai — private AI meeting notes for Mac that also hand your meetings to Claude Code and Cursor as MCP tools.

Available Tools

5 tools
get_forecastSingapore weather forecastA

NEA forecast for the caller's area/region. horizon: '2h' (area nowcast), '24h' (default; general + regional periods) or '4d' (island-wide outlook).

Inputs: ip, lat, lon, lang, +2 more.

get_hazeSingapore haze (PSI / PM2.5)A

Current haze for the caller's nearest PSI region: 24-hr PSI, 1-hr PM2.5, 24-hr PM2.5/PM10, the NEA band and health advisory, plus all five regions for comparison. Data: NEA via data.gov.sg, refreshed hourly.

Inputs: ip, lat, lon, lang, +1 more.

get_rainSingapore rain nowA

Is it raining near the caller? Uses NEA's 5-minute rain-gauge network (nearest three stations) plus the 2-hour area forecast; also counts wet stations island-wide.

Inputs: ip, lat, lon, lang, +1 more.

locate_meLocate caller in SingaporeA

Resolve the caller's location (public IP by default) to coordinates and the nearest NEA PSI region. Useful to check what the other tools will use before calling them.

Inputs: ip, lat, lon, lang.

weather_nowSingapore weather now (haze + rain)A

One-call summary for the caller's location in Singapore: 24-hr PSI and 1-hr PM2.5 with NEA's health advisory, whether it is raining nearby, and the 2-hour forecast. Location comes from the caller's public IP unless region / lat+lon / ip is given.

Inputs: ip, lat, lon, lang, +1 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.8/5.0

    Scored across 5 tools

    Disambiguation3/5

    The three get_* tools target distinct data (haze/PSI, rain, forecast), but weather_now is a composite summary that duplicates all of them, creating genuine overlap about when to call the aggregate vs. the specific tools. Descriptions do clarify intent, keeping it from being worse than moderate ambiguity.

    Naming Consistency4/5

    Four tools follow a verb_object pattern (get_haze, get_rain, get_forecast, locate_me), but weather_now breaks the convention with a noun_first form. The deviation is minor and all names remain readable.

    Tool Count5/5

    Five tools is well-scoped for a focused Singapore weather/haze server, with a clear summary tool plus targeted drill-downs. Each tool earns its place.

    Completeness4/5

    The surface covers location resolution, haze/PSI, rain, forecast, and an aggregate summary, giving good lifecycle coverage for the domain. Only marginal extras (e.g. historical data or alert subscriptions) are absent, which agents can work around.

    Maintenance

    ActivitySlowing
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers