MCP server
The DPIP MCP server is a read-only MCP (Model Context Protocol) server. It wraps ExpTech earthquake, weather, rainfall, lightning, typhoon, tsunami, and disaster-event data as 25 tools. After you add it to an MCP client such as Claude Code or Claude Desktop, the model can query live and historical data directly, without handling each API’s format itself.
Data license
Section titled “Data license”Where to show it, examples and commercial-use requests: Data licensing and attribution.
Authentication
Section titled “Authentication”No API Token is required. Every tool is read-only and does not modify any data.
Connection
Section titled “Connection”Content-Typeapplication/json
Acceptapplication/json, text/event-stream
bodyAn MCP message, for example initialize, tools/list, or tools/call
curl "https://api.core.exptech.dev/mcp" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ --data '{"jsonrpc":"2.0","id":1,"method":"<method>","params":{}}'curl "https://api.core-tnn1.exptech.dev/mcp" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ --data '{"jsonrpc":"2.0","id":1,"method":"<method>","params":{}}'curl "https://api.core-tyo1.exptech.dev/mcp" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ --data '{"jsonrpc":"2.0","id":1,"method":"<method>","params":{}}'const res = await fetch("https://api.core.exptech.dev/mcp", { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json, text/event-stream", }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "<method>", params: {} }),});if (!res.ok) throw new Error(`HTTP ${res.status}`);const data = await res.json();console.log(data);const res = await fetch("https://api.core-tnn1.exptech.dev/mcp", { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json, text/event-stream", }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "<method>", params: {} }),});if (!res.ok) throw new Error(`HTTP ${res.status}`);const data = await res.json();console.log(data);const res = await fetch("https://api.core-tyo1.exptech.dev/mcp", { method: "POST", headers: { "Content-Type": "application/json", Accept: "application/json, text/event-stream", }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "<method>", params: {} }),});if (!res.ok) throw new Error(`HTTP ${res.status}`);const data = await res.json();console.log(data);import requests
res = requests.post( "https://api.core.exptech.dev/mcp", headers={ "Content-Type": "application/json", "Accept": "application/json, text/event-stream", }, json={"jsonrpc": "2.0", "id": 1, "method": "<method>", "params": {}}, timeout=10,)res.raise_for_status()print(res.json())import requests
res = requests.post( "https://api.core-tnn1.exptech.dev/mcp", headers={ "Content-Type": "application/json", "Accept": "application/json, text/event-stream", }, json={"jsonrpc": "2.0", "id": 1, "method": "<method>", "params": {}}, timeout=10,)res.raise_for_status()print(res.json())import requests
res = requests.post( "https://api.core-tyo1.exptech.dev/mcp", headers={ "Content-Type": "application/json", "Accept": "application/json, text/event-stream", }, json={"jsonrpc": "2.0", "id": 1, "method": "<method>", "params": {}}, timeout=10,)res.raise_for_status()print(res.json())Success (a tools/call response)
{ "jsonrpc": "2.0", "id": 2, "result": { "content": [ { "type": "text", "text": "與 structuredContent 相同內容的 JSON 字串" } ], "structuredContent": { "fetched_at": "2026-11-14T14:00:05+08:00", "data": { "updateTime": 1794636120000, "forecast": [ { "time": "15:00", "temperature": 24, "apparentTemp": 26, "humidity": 75, "weather": "多雲", "weatherCode": 200, "pop": 20, "wind": { "direction": "NE", "speed": 3, "beaufort": 2 } } ] } } }}A cross-origin request from a browser was rejectedNo response body
| Item | Value |
|---|---|
| URL | https://api.core.exptech.dev/mcp. You can also pick a node: https://api.core-tnn1.exptech.dev/mcp, https://api.core-tyo1.exptech.dev/mcp |
| Transport | Streamable HTTP (MCP spec from 2025-03-26). The same URL also supports legacy HTTP + SSE (spec 2024-11-05) |
| Session | Streamable HTTP does not use a session (stateless). The server does not send Mcp-Session-Id. Each request is handled on its own |
| Server name | dpip (display name “DPIP 防災資訊”) |
| Authentication | None |
| Connection timeout | 3600 seconds |
Streamable HTTP: send JSON-RPC messages with POST /mcp. Accept must accept both application/json and text/event-stream. The response may be JSON, or it may be SSE (the JSON-RPC message is in a data: line). Ordinary MCP clients and SDKs handle this.
Legacy HTTP + SSE: open the event stream with GET /mcp (the request omits the Mcp-Session-Id and MCP-Protocol-Version headers). The stream tells you the URL for later POST requests, /mcp?sessionid=.... A session in this mode lives on a single node, while DNS assigns api.core.exptech.dev to a node, so a later request can land on a different node. When you use legacy SSE, pick api.core-tnn1 or api.core-tyo1. New integrations should use Streamable HTTP.
Add an MCP client
Section titled “Add an MCP client”Claude Code
Section titled “Claude Code”claude mcp add --transport http dpip https://api.core.exptech.dev/mcpAdd --scope user to use it in every project. After adding it, run claude mcp list to check the connection.
Claude Desktop
Section titled “Claude Desktop”Open Settings, then Connectors, then add a custom connector. Set the name to DPIP and the URL to https://api.core.exptech.dev/mcp. Whether a custom connector is available depends on the Claude plan.
Other clients
Section titled “Other clients”Clients that support remote MCP (for example Cursor, or a Claude Code project’s .mcp.json) usually accept the following. Field names depend on the client:
{ "mcpServers": { "dpip": { "type": "http", "url": "https://api.core.exptech.dev/mcp" } }}A client that supports only stdio can bridge through mcp-remote:
{ "mcpServers": { "dpip": { "command": "npx", "args": ["-y", "mcp-remote", "https://api.core.exptech.dev/mcp"] } }}For a client that supports only legacy SSE, pick a node:
{ "mcpServers": { "dpip": { "type": "sse", "url": "https://api.core-tnn1.exptech.dev/mcp" } }}Response shape
Section titled “Response shape”Each tool’s result is placed both in structuredContent and in a JSON text block with the same contents. The shape is fixed:
| Field | Description |
|---|---|
source |
Which ExpTech API URL the data came from. Offline tools (township_lookup) omit this field |
fetched_at |
When the data was fetched, RFC 3339, Taiwan time (+08:00) |
note |
Optional. Set when the upstream is temporarily unavailable and a cached copy is returned instead |
data |
The data the tool returned |
{ "source": "https://api.core-tnn1.exptech.dev/api/v5/meteor/weather/realtime/25.033,121.5654", "fetched_at": "2026-11-14T14:00:05+08:00", "data": { "id": "C0A9C0", "station": { "name": "信義", "lat": 25.0378, "lon": 121.5646, "altitude": 26, "distance": 0.55 }, "time": 1794636000, "data": { "weather": "多雲", "weatherCode": 200, "temperature": 24.6, "humidity": 72, "rain": 0, "wind": { "direction": "東北", "speed": 2.1, "beaufort": 2 }, "gust": { "speed": null, "beaufort": null }, "visibility": null, "visibility_text": "無觀測", "pressure": null, "sunshine": null } }}Shared conventions (the server also sends these notes to the client during initialization):
- Times are Unix seconds, unless a field says otherwise:
tson earthquake reports, tsunami information, and RTS is Unix milliseconds, and a TREM detection is an RFC 3339 string. - A township code is a 3-digit postal code (for example
100for Zhongzheng District, Taipei City).township_lookupcan resolve one from a place name or coordinates. - Seismic intensity is a number from 0 to 9:
5is 5-weak,6is 5-strong,7is 6-weak,8is 6-strong, and9is 7. Results also include a_labelfield (for exampleint_label). Use that when you display the value. - The weather missing value
-99is converted tonull. Delta-encoded times are expanded to real times. Columnar snapshots are turned into one row per station, with the station name, county or city, township, and coordinates. - Tools that take a specific time use the
atparameter (Unix seconds). Usetimelinefor the times you can query.
Parameters other than those marked required are optional. The License column is the class of the data each tool returns. See Data license.
Earthquakes and tsunami
Section titled “Earthquakes and tsunami”| Tool | License | Purpose | Parameters |
|---|---|---|---|
cwa_links |
Non-commercial | Links between earthquake reports and TREM detections | cwa_id, cwa_event_id, trem_event_id, limit (1–500) |
earthquake_reports |
Commercial OK | A list of earthquake reports, filtered and sorted by intensity, magnitude, depth, date, and county or city | limit (1–100, default 20), page, min_intensity, max_intensity, min_magnitude, max_magnitude, min_depth, max_depth, start_date, end_date (YYYY-MM-DD), sort (time, intensity, magnitude, depth), order (desc, asc), city, city_min_intensity, city_max_intensity |
earthquake_report |
Commercial OK | One earthquake report, including intensity for each county or city and each township | id (required) |
gdms |
Commercial OK | Strong-motion station records for one earthquake report: PGA, PGV, instrumental intensity, and station coordinates | id (required), min_intensity, min_pga, limit (1–500, default 40) |
tsunami_reports |
Commercial OK | A list of CWA tsunami information | none |
tsunami_report |
Commercial OK | One tsunami information message | id (required) |
TREM seismic network
Section titled “TREM seismic network”TREM data are unofficial observations; the Central Weather Administration's releases always prevail. Under the Meteorological Act, they must not be used to predict earthquakes or to issue earthquake warnings. Read the full notice
| Tool | License | Purpose | Parameters |
|---|---|---|---|
rts |
Non-commercial | Real-time station shaking, about one frame per second, sorted by strength, with station location and township | at, min_intensity, limit (1–1000, default 30) |
trem_reports |
Non-commercial | A list of TREM earthquake detections | month, year, alert, limit (1–500, default 30) |
trem_report |
Non-commercial | Each report in one detection | id (required, 14 digits) |
trem_shakes |
Non-commercial | A list of intensity reports | month, year, limit (1–500) |
trem_shake |
Non-commercial | One intensity report, including instrumental intensity for each township | id (required), min_intensity, town_limit (1–400, default 120) |
trem_triggers |
Non-commercial | Stations that triggered during one detection | id (required), min_intensity, limit (1–500, default 50) |
trem_trust |
Non-commercial | Trust tier of each station | tier (NO_EVIDENCE, MISFIRING, BLOCKED, SEMI, FAST), station, limit (1–500) |
trem_noise |
Non-commercial | Noise multiplier of each station | status (adjusted, normal, insufficient), station, limit (1–500) |
trem_picks |
Non-commercial | Recent P-wave and S-wave picks | range (24h, 7d, default 24h), station, phase (P, S), limit (default 40) |
trem_debug_triggers |
Non-commercial | Recent station triggers, including ones that did not become an earthquake | range (24h, 7d, default 24h), station, coinc_result, event_id, limit (1–200, default 40) |
Weather
Section titled “Weather”| Tool | License | Purpose | Parameters |
|---|---|---|---|
weather_realtime |
Commercial OK | Current weather at the station nearest a coordinate. See current weather | lat (required), lng (required) |
weather_forecast |
Commercial OK | Hourly township forecast. See township hourly forecast | code (required, 3 digits) |
rain_hour_forecast |
Commercial OK | A minute-by-minute rainfall forecast for the next 1 hour for a township | code (required, 3 digits) |
meteor_snapshot |
Commercial OK | A weather, rainfall, or lightning snapshot for every station, one row per station, with filtering and sorting | family (required: weather, rain, lightning), at, station_ids, city, town, sort_by, order (desc, asc), limit (1–5000, default 50) |
meteor_trend |
Commercial OK | The last 24 hours or 7 days for one weather or rainfall station | family (required: weather, rain), station_id (required), range (24h, 7d, default 24h) |
typhoon |
Commercial OK | Typhoon index, track, potential track, strike probability, or warnings. See Typhoons | kind (cyclones, track, potential, probability, warning; default cyclones), at (not supported for cyclones) |
timeline |
Varies | Times you can query for each dataset. Radar, satellite, QPESUMS, and wind also include a tile URL template | source (required: weather, rain, lightning, typhoon_track, typhoon_potential, typhoon_probability, typhoon_warning, radar, satellite, qpesums, wind), channel, style (normal, jma, bd), model (gfs, ecmwf, default gfs), limit (1–1000, default 24) |
Disaster events and utilities
Section titled “Disaster events and utilities”| Tool | License | Purpose | Parameters |
|---|---|---|---|
dpip_events |
Commercial OK | Active or recent disaster events, nationwide or for one township, newest first. text.content and text.description in the response are split by township code |
state (active, history; default active), code (3 digits; omit for nationwide) |
township_lookup |
N/A | Look up a township code, name, and center from a place name, a township code, or coordinates. No network call | query, lat, lng, limit (1–400, default 10) |
Advanced examples
Section titled “Advanced examples”Call a tool with curl
Section titled “Call a tool with curl”Send JSON-RPC messages without an MCP client: initialize first, then tools/call for a tool. A real integration should use an MCP SDK.
curl https://api.core.exptech.dev/mcp \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'
curl https://api.core.exptech.dev/mcp \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"weather_forecast","arguments":{"code":"105"}}}'Use the TypeScript SDK
Section titled “Use the TypeScript SDK”The official SDK (@modelcontextprotocol/sdk) handles the transport and the response format:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const client = new Client({ name: "my-app", version: "1.0.0" });await client.connect(new StreamableHTTPClientTransport(new URL("https://api.core.exptech.dev/mcp")));
const { tools } = await client.listTools();const result = await client.callTool({ name: "weather_forecast", arguments: { code: "105" } });console.log(result.structuredContent.data);- The server caches upstream data. Identical queries at the same time share one upstream request. Cache time depends on the data: 1 second for live RTS; 30 seconds for lists; 1 minute for observations, typhoons, and time lists; 10 minutes for forecasts and earthquake-report bodies; 1 hour for historical data at a chosen time or id, and for the station catalog.
- When the upstream is temporarily unavailable (429, 5xx, or a timeout), the server returns older data from at most 10 times the cache time and explains that in
note.fetched_atis when that copy was actually fetched. - When a parameter is invalid or the query fails, the tool call returns an error message. It does not return empty data.
- A browser cannot call this cross-origin. A cross-origin request is rejected (403). Connect from an MCP client or from a server.
- Streamable HTTP does not keep a session. The server does not push notifications or send requests to the client.
