@cyanheads/wsdot-mcp-server 0.2.6 → 0.4.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.
Files changed (66) hide show
  1. package/AGENTS.md +1 -1
  2. package/CLAUDE.md +1 -1
  3. package/README.md +32 -23
  4. package/changelog/0.3.x/0.3.0.md +30 -0
  5. package/changelog/0.4.x/0.4.0.md +26 -0
  6. package/dist/index.js +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/mcp-server/tools/definitions/get-ferry-routes.tool.d.ts +9 -2
  9. package/dist/mcp-server/tools/definitions/get-ferry-routes.tool.d.ts.map +1 -1
  10. package/dist/mcp-server/tools/definitions/get-ferry-routes.tool.js +40 -5
  11. package/dist/mcp-server/tools/definitions/get-ferry-routes.tool.js.map +1 -1
  12. package/dist/mcp-server/tools/definitions/get-ferry-schedule.tool.d.ts +10 -3
  13. package/dist/mcp-server/tools/definitions/get-ferry-schedule.tool.d.ts.map +1 -1
  14. package/dist/mcp-server/tools/definitions/get-ferry-schedule.tool.js +96 -20
  15. package/dist/mcp-server/tools/definitions/get-ferry-schedule.tool.js.map +1 -1
  16. package/dist/mcp-server/tools/definitions/get-ferry-terminals.tool.d.ts +2 -2
  17. package/dist/mcp-server/tools/definitions/get-ferry-terminals.tool.d.ts.map +1 -1
  18. package/dist/mcp-server/tools/definitions/get-ferry-terminals.tool.js +12 -5
  19. package/dist/mcp-server/tools/definitions/get-ferry-terminals.tool.js.map +1 -1
  20. package/dist/mcp-server/tools/definitions/get-mountain-passes.tool.d.ts +2 -2
  21. package/dist/mcp-server/tools/definitions/get-mountain-passes.tool.d.ts.map +1 -1
  22. package/dist/mcp-server/tools/definitions/get-mountain-passes.tool.js +9 -4
  23. package/dist/mcp-server/tools/definitions/get-mountain-passes.tool.js.map +1 -1
  24. package/dist/mcp-server/tools/definitions/get-terminal-space.tool.d.ts.map +1 -1
  25. package/dist/mcp-server/tools/definitions/get-terminal-space.tool.js +2 -0
  26. package/dist/mcp-server/tools/definitions/get-terminal-space.tool.js.map +1 -1
  27. package/dist/mcp-server/tools/definitions/get-toll-rates.tool.d.ts +4 -0
  28. package/dist/mcp-server/tools/definitions/get-toll-rates.tool.d.ts.map +1 -1
  29. package/dist/mcp-server/tools/definitions/get-toll-rates.tool.js +123 -79
  30. package/dist/mcp-server/tools/definitions/get-toll-rates.tool.js.map +1 -1
  31. package/dist/mcp-server/tools/definitions/get-travel-times.tool.d.ts.map +1 -1
  32. package/dist/mcp-server/tools/definitions/get-travel-times.tool.js +103 -101
  33. package/dist/mcp-server/tools/definitions/get-travel-times.tool.js.map +1 -1
  34. package/dist/mcp-server/tools/definitions/search-alerts.tool.d.ts +10 -0
  35. package/dist/mcp-server/tools/definitions/search-alerts.tool.d.ts.map +1 -1
  36. package/dist/mcp-server/tools/definitions/search-alerts.tool.js +152 -130
  37. package/dist/mcp-server/tools/definitions/search-alerts.tool.js.map +1 -1
  38. package/dist/mcp-server/tools/definitions/search-cameras.tool.d.ts +12 -0
  39. package/dist/mcp-server/tools/definitions/search-cameras.tool.d.ts.map +1 -1
  40. package/dist/mcp-server/tools/definitions/search-cameras.tool.js +118 -67
  41. package/dist/mcp-server/tools/definitions/search-cameras.tool.js.map +1 -1
  42. package/dist/mcp-server/tools/page-budget.d.ts +56 -0
  43. package/dist/mcp-server/tools/page-budget.d.ts.map +1 -0
  44. package/dist/mcp-server/tools/page-budget.js +76 -0
  45. package/dist/mcp-server/tools/page-budget.js.map +1 -0
  46. package/dist/services/ferry/ferry-service.d.ts +20 -0
  47. package/dist/services/ferry/ferry-service.d.ts.map +1 -1
  48. package/dist/services/ferry/ferry-service.js +186 -49
  49. package/dist/services/ferry/ferry-service.js.map +1 -1
  50. package/dist/services/ferry/types.d.ts +52 -4
  51. package/dist/services/ferry/types.d.ts.map +1 -1
  52. package/dist/services/text-field.d.ts +31 -0
  53. package/dist/services/text-field.d.ts.map +1 -0
  54. package/dist/services/text-field.js +41 -0
  55. package/dist/services/text-field.js.map +1 -0
  56. package/dist/services/traffic/traffic-service.d.ts.map +1 -1
  57. package/dist/services/traffic/traffic-service.js +66 -86
  58. package/dist/services/traffic/traffic-service.js.map +1 -1
  59. package/dist/services/traffic/types.d.ts +3 -3
  60. package/dist/services/traffic/types.d.ts.map +1 -1
  61. package/dist/services/wcf-date.d.ts +6 -4
  62. package/dist/services/wcf-date.d.ts.map +1 -1
  63. package/dist/services/wcf-date.js +8 -4
  64. package/dist/services/wcf-date.js.map +1 -1
  65. package/package.json +2 -2
  66. package/server.json +3 -3
package/AGENTS.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** @cyanheads/wsdot-mcp-server
4
- **Version:** 0.2.6
4
+ **Version:** 0.4.0
5
5
  **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.6`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
7
  **MCP SDK:** `@modelcontextprotocol/server` ^2.0.0 (protocol revisions 2026-07-28 and 2025-*)
package/CLAUDE.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** @cyanheads/wsdot-mcp-server
4
- **Version:** 0.2.6
4
+ **Version:** 0.4.0
5
5
  **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.6`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
7
  **MCP SDK:** `@modelcontextprotocol/server` ^2.0.0 (protocol revisions 2026-07-28 and 2025-*)
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  <div align="center">
9
9
 
10
- [![Version](https://img.shields.io/badge/Version-0.2.6-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/wsdot-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.0.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/wsdot-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/wsdot-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.0%2B-blueviolet.svg?style=flat-square)](https://bun.sh/)
10
+ [![Version](https://img.shields.io/badge/Version-0.4.0-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/wsdot-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.0.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/wsdot-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/wsdot-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.0%2B-blueviolet.svg?style=flat-square)](https://bun.sh/)
11
11
 
12
12
  </div>
13
13
 
@@ -34,12 +34,12 @@ Washington State transportation data from the WSDOT Traveler API and the WSF Fer
34
34
  | `wsdot_get_mountain_passes` | Current conditions for all WA mountain passes: status, road condition, traction laws, temperature, elevation. |
35
35
  | `wsdot_search_alerts` | Active highway alerts — incidents, construction, closures — filterable by state route, WSDOT region, and milepost range. |
36
36
  | `wsdot_get_travel_times` | Current vs. average travel times for named WA highway corridors (I-5, I-90, SR 520, etc.) with congestion delay. |
37
- | `wsdot_get_toll_rates` | Dynamic toll rates for WA express lanes and tolled facilities: SR 99, SR 167 HOT, I-405 Express, SR 509, SR 520. |
37
+ | `wsdot_get_toll_rates` | Dynamic toll rates for WA express lanes and tolled facilities — SR 99, SR 167 HOT, I-405 Express, SR 509, SR 520 — filterable by route. |
38
38
  | `wsdot_get_border_waits` | Current vehicle wait times at all WA/Canada land border crossings. |
39
- | `wsdot_search_cameras` | Highway camera metadata and image URLs, filterable by state route, region, and milepost range. |
40
- | `wsdot_get_ferry_terminals` | All WSF ferry terminals with numeric IDs needed for schedule and space lookups. |
41
- | `wsdot_get_ferry_routes` | WSF routes operating on a given date — route ID, abbreviation, and description for each, for route discovery and ferry-alert cross-reference. |
42
- | `wsdot_get_ferry_schedule` | Departure times for a specific WSF route — today-remaining or full-day future mode. |
39
+ | `wsdot_search_cameras` | Highway camera metadata and image URLs, filterable by state route, region, milepost range, and title words. |
40
+ | `wsdot_get_ferry_terminals` | All WSF ferry terminals with numeric IDs needed for schedule and space lookups, plus coordinates. |
41
+ | `wsdot_get_ferry_routes` | WSF routes operating on a given date — route ID, abbreviation, description, and the terminal pairs each serves, for route discovery, schedule lookups, and ferry-alert cross-reference. |
42
+ | `wsdot_get_ferry_schedule` | Departure times for a specific WSF route — today-remaining or full-day future mode — with each sailing's vessel, loading rule, and notes. |
43
43
  | `wsdot_get_vessel_locations` | Real-time AIS positions, speed, heading, ETA, and dock status for all active WSF vessels. |
44
44
  | `wsdot_get_terminal_space` | Drive-up and reservable vehicle space available at WSF terminals for upcoming sailings. |
45
45
  | `wsdot_get_ferry_alerts` | Active WSF service disruptions and bulletins with impacted route IDs. |
@@ -57,21 +57,21 @@ Washington State transportation data from the WSDOT Traveler API and the WSF Fer
57
57
  ### `wsdot_search_alerts` <sub>tool</sub>
58
58
 
59
59
  - Filter by state route — natural forms all work: `"I-90"`, `"90"`, `"090"`, or `"SR 520"` / `"520"`
60
- - Filter by WSDOT region: Northwest, Olympic, Southwest, South Central, North Central, Eastern
61
- - Filter by milepost range to scope to a corridor — an alert matches when its extent overlaps the range, so a closure that spans the boundary is returned
62
- - Omit all filters to return all current statewide alerts
60
+ - Filter by WSDOT region name: Northwest, Olympic, Southwest, South Central, North Central, Eastern (case-insensitive; any other value is rejected with `invalid_region`)
61
+ - Filter by milepost range to scope to a corridor — an alert matches when its extent overlaps the range, so a closure that spans the boundary is returned. Either bound may be given alone; a start above the end is rejected with `invalid_milepost_range`
62
+ - Omit all filters to return all current statewide alerts; `stateRoute` and `region` accept at most 200 characters
63
63
  - Descriptions are normalized to plain text; a link renders inline as `link text (url)`
64
- - Results ordered by `alertId` and paged (default 50, max 500) — pass `offset`/`limit`; the notice reports the next offset
64
+ - Results ordered by `alertId` and paged (default 20, max 500) — pass `offset`/`limit`; a page also ends early at a 24,000-byte response budget, and the notice reports the next offset
65
65
 
66
66
  ---
67
67
 
68
68
  ### `wsdot_get_travel_times` <sub>tool</sub>
69
69
 
70
70
  - Covers I-5, I-90, SR 520, SR 99, I-405, SR 167, and others
71
- - Filter by route (`"I-5"`, `"5"`, `"SR 520"`) to get every corridor measured on it, or by any text to match corridor names (`"Everett"`)
71
+ - Filter by route (`"I-5"`, `"5"`, `"SR 520"`) to get every corridor measured on it, or by any text to match corridor names (`"Everett"`); `route` accepts at most 200 characters
72
72
  - When current time exceeds average, the corridor is congested; the delta is the delay
73
73
  - Reversible express-lane corridors report no travel time while closed in the queried direction — those figures are omitted rather than reported as zero minutes
74
- - Results are paged (default 50, max 500) — pass `offset`/`limit`; the notice reports the next offset
74
+ - Results are paged (default 50, max 500) — pass `offset`/`limit`; a page also ends early at a 24,000-byte response budget, and the notice reports the next offset
75
75
 
76
76
  ---
77
77
 
@@ -79,9 +79,11 @@ Washington State transportation data from the WSDOT Traveler API and the WSF Fer
79
79
 
80
80
  - Covers SR 99 (WSDOT Tunnel), SR 167 HOT Lanes, I-405 Express Lanes, the SR 509 tolled segment, and the SR 520 Bridge
81
81
  - Rates are time-banded and change dynamically based on traffic conditions
82
- - `stateRoute` is a bare, zero-padded route number (`"099"`, `"405"`) with no route type; the rendered text resolves the posted designation, so I-405 reads as `I-405` rather than `SR 405`
82
+ - Filter to one facility with `stateRoute` — `"SR 520"`, `"520"`, `"0520"`, `"I-405"`, and `"405"` all work, matched against the posted designation, so `"SR 405"` matches nothing. A route with no tolled facility returns an empty page whose notice names the tolled routes; the filter (at most 200 characters) is applied before paging and echoed in `appliedFilters`
83
+ - Each row's `stateRoute` is the bare, zero-padded route number the feed carries (`"099"`, `"405"`) with no route type; the rendered text resolves the posted designation, so I-405 reads as `I-405` rather than `SR 405`
84
+ - `travelDirection` is the feed's code, not the direction of travel: SR 99, SR 509, and SR 520 carry one fixed code per facility although trips run both ways — read direction from the segment's start and end
83
85
  - Each entry leads with its readable `startLocationName → endLocationName` segment; the opaque upstream trip key stays available as `tripName`
84
- - Results are paged (default 50, max 500) — pass `offset`/`limit`; the notice reports the next offset
86
+ - Results are paged (default 50, max 500) — pass `offset`/`limit`; a page also ends early at a 24,000-byte response budget, and the notice reports the next offset
85
87
 
86
88
  ---
87
89
 
@@ -96,10 +98,13 @@ Washington State transportation data from the WSDOT Traveler API and the WSF Fer
96
98
 
97
99
  ### `wsdot_search_cameras` <sub>tool</sub>
98
100
 
99
- - Filter by state route (`"I-90"`, `"90"`, `"SR 520"`, or `"520"` all work), WSDOT region, or milepost range
101
+ - Filter by state route (`"I-90"`, `"90"`, `"SR 520"`, or `"520"` all work), WSDOT region code, milepost range, or words in the camera title; `stateRoute`, `region`, and `titleContains` accept at most 200 characters
100
102
  - Camera road names carry a route-type prefix, so `"SR 26"` excludes US 26 and `"US 97"` excludes US 97A; a bare `"26"` returns both
103
+ - Region codes: `NW`, `SW`, `OL`, `ER`, `SC`, `NC`, `OS` (Oregon — the TripCheck cameras around Portland), and `WA` (airport cameras plus a few ferry-terminal cameras — most ferry-terminal cameras sit in `NW` and `OL`). Case-insensitive; any other value is rejected with `invalid_region`
104
+ - `titleContains` matches the title as WSDOT wrote it — case-insensitive, every word must appear in any order — so `"Snoqualmie"` returns Snoqualmie Summit and East Snoqualmie Summit but not Hyak. Titles lead with route and milepost, so filter a route with `stateRoute`
105
+ - Either milepost bound may be given alone; a start above the end is rejected with `invalid_milepost_range`
101
106
  - Returns metadata and image URLs — camera images are copyright WSDOT, not fetched as bytes
102
- - Results are ordered by `cameraId` and paged (default 50, max 500) — pass `offset`/`limit`; the notice reports the next offset
107
+ - Results are ordered by `cameraId` and paged (default 50, max 500) — pass `offset`/`limit`; a page also ends early at a 24,000-byte response budget, and the notice reports the next offset
103
108
 
104
109
  ---
105
110
 
@@ -107,27 +112,31 @@ Washington State transportation data from the WSDOT Traveler API and the WSF Fer
107
112
 
108
113
  - No input parameters — returns all 20 WSF ferry terminals; the list rarely changes
109
114
  - Call this first to resolve human-readable names (e.g. "Bainbridge Island", "Seattle", "Kingston") to the numeric IDs required by `wsdot_get_ferry_schedule` and `wsdot_get_terminal_space`
110
- - Each terminal also carries an abbreviation and coordinates when reported
115
+ - Each terminal also carries its abbreviation and latitude/longitude
111
116
 
112
117
  ---
113
118
 
114
119
  ### `wsdot_get_ferry_routes` <sub>tool</sub>
115
120
 
116
121
  - Optional `tripDate` (ISO 8601 `YYYY-MM-DD`); defaults to today
117
- - Returns each route's ID, abbreviation, and description
122
+ - Returns each route's ID, abbreviation, and description, plus `terminalPairs`: the directed departing → arriving terminal pairs (IDs and names) the route serves that day. These are exactly the pairs `wsdot_get_ferry_schedule` accepts for that date; a route serving none carries an empty list
118
123
  - Route IDs correspond to `impactedRouteIds` in `wsdot_get_ferry_alerts` — use this tool to resolve alert route IDs to route names
119
- - Use to discover which routes are running; for the numeric terminal IDs that schedule and space lookups need, call `wsdot_get_ferry_terminals`
124
+ - A date outside the range WSF has published (before today, or past the posted schedule) returns a typed `invalid_date` error stating WSF's range. A date inside that range with no sailings loaded yet returns an empty list and a notice
125
+ - The pairs cost one lookup per route, cached per date until WSF signals a schedule change; if any lookup fails, the whole call fails
120
126
 
121
127
  ---
122
128
 
123
129
  ### `wsdot_get_ferry_schedule` <sub>tool</sub>
124
130
 
125
- - Requires numeric `departingTerminalId` and `arrivingTerminalId` — use `wsdot_get_ferry_terminals` first
126
- - Optional `tripDate` (defaults to today) and `remainingOnly: true` (only future departures for today; ignored for future dates)
131
+ - Requires `departingTerminalId` and `arrivingTerminalId`, both positive integers — use `wsdot_get_ferry_terminals` first, or pick a pair from `terminalPairs` on `wsdot_get_ferry_routes`
132
+ - Optional `tripDate` (defaults to today) and `remainingOnly: true` (only future departures for today; ignored for any other date, and the response then reports `remainingOnly: false`)
133
+ - Each sailing carries `vesselId` (the ID `wsdot_get_vessel_locations` reports), `loadingRule`, `vesselHandicapAccessible`, and `annotationIndexes` into the pair's `annotations`, notes such as "No interisland vehicles. Foot passenger and bikes okay." The rendered text lists each sailing's notes under it. WSF does not document `loadingRule`: 3 appears on nearly every sailing and 1 only on vehicle-restricted ones, so read the notes for the restriction itself
134
+ - `annotations` and the pair-wide `sailingNotes` arrive from WSF as HTML and are returned as plain text, with links kept as `link text (url)`
127
135
  - `departureTime` and `arrivalTime` are ISO 8601 **UTC**, while `tripDate` is the Pacific service day — an evening sailing therefore carries the following UTC calendar date and will not match `tripDate`. Convert to `America/Los_Angeles` before quoting a clock time
128
136
  - `arrivalTime` is populated on some routes and absent on others
129
137
  - No cancellation status — WSF drops a cancelled sailing from the schedule rather than flagging it, so a listed sailing is not confirmation it will run; check `wsdot_get_ferry_alerts`, which reports disruptions at route level
130
- - An invalid or non-through terminal pair returns a typed `invalid_terminal_pair` error rather than an empty schedule
138
+ - An invalid or non-through terminal pair returns a typed `invalid_terminal_pair` error rather than an empty schedule; its recovery hint points at `terminalPairs` on `wsdot_get_ferry_routes` for the same date
139
+ - A date WSF has no schedule for returns `invalid_date` instead — whether it falls outside WSF's published range or inside it with no routes loaded
131
140
 
132
141
  ---
133
142
 
@@ -174,7 +183,7 @@ Agent-friendly output:
174
183
  - Typed failure — `invalid_access_code` and `api_unavailable` errors carry an explicit recovery hint distinguishing configuration faults from transient upstream ones
175
184
  - `driveUpSpaceCount: 0` and congestion delta fields (`delayInMinutes`) give agents actionable signal without string parsing
176
185
  - Partial data preserved — sparse upstream payloads surface `null`/`undefined` rather than synthetic defaults (e.g. an omitted `waitTimeInMinutes`, an absent `arrivalTime`)
177
- - `content[]` and `structuredContent` carry the same values, not just the same fields — a `false` flag, an empty list, and one populated half of a coordinate pair all render rather than dropping out of the markdown surface that some clients read
186
+ - `content[]` and `structuredContent` carry the same values, not just the same fields — a `false` flag, an empty list, and one populated half of a coordinate pair all render rather than dropping out of the markdown surface that some clients read; a blank or whitespace-only upstream string is absent from both, and a populated one arrives trimmed
178
187
 
179
188
  ## Getting started
180
189
 
@@ -0,0 +1,30 @@
1
+ ---
2
+ summary: "Traffic tools reject invalid regions and reversed milepost ranges, toll rates gain a stateRoute filter and cameras a titleContains filter, and each traffic page now ends at a 24,000-byte budget per surface."
3
+ breaking: true
4
+ security: false
5
+ ---
6
+
7
+ # 0.3.0 — 2026-09-24
8
+
9
+ ## Added
10
+
11
+ - **`wsdot_get_toll_rates` `stateRoute` filter** ([#44](https://github.com/cyanheads/wsdot-mcp-server/issues/44)) — `"SR 520"`, `"520"`, `"0520"`, `"I-405"`, and `"405"` all work, matched against the posted designation before paging and echoed in a new `appliedFilters` enrichment. A route with no tolled facility returns an empty page whose notice names the tolled routes.
12
+ - **`wsdot_search_cameras` `titleContains` filter** ([#45](https://github.com/cyanheads/wsdot-mcp-server/issues/45)) — case-insensitive, every whitespace-separated word must appear in the camera title; composes with the other filters and is echoed in `appliedFilters`.
13
+ - **Page byte budget** ([#43](https://github.com/cyanheads/wsdot-mcp-server/issues/43)) — `wsdot_search_alerts`, `wsdot_search_cameras`, `wsdot_get_travel_times`, and `wsdot_get_toll_rates` end a page at `limit` or at 24,000 bytes per surface (`structuredContent` and `content[]`), whichever comes first; a budget-ended page says so in its notice.
14
+
15
+ ## Changed
16
+
17
+ - **Invalid traffic filters fail instead of returning an empty page** ([#39](https://github.com/cyanheads/wsdot-mcp-server/issues/39)) — on `wsdot_search_alerts` and `wsdot_search_cameras`, a `region` outside the tool's vocabulary (names on alerts, codes on cameras; case-insensitive) raises `invalid_region`, and `startMilepost > endMilepost` raises `invalid_milepost_range`. Blank filters are still treated as omitted.
18
+ - **Ferry terminal IDs must be positive integers** — `departingTerminalId` on `wsdot_get_terminal_space` and both IDs on `wsdot_get_ferry_schedule`; anything else is an `InvalidParams` rejection that never reaches the upstream.
19
+ - **Free-text traffic filters accept at most 200 characters** — `stateRoute`, `region`, `route`, and `titleContains`.
20
+ - **`wsdot_search_alerts` default `limit`** drops from 50 to 20; the other three paged traffic tools stay at 50.
21
+ - **Migration:** a region outside the tool's vocabulary, a reversed milepost range, a non-positive or fractional terminal ID, or a filter over 200 characters now errors instead of returning an empty page; an alerts page defaults to 20 rows; and any traffic page may end before `limit` at the byte budget, reported through `hasMore` / `nextOffset` and the notice.
22
+
23
+ ## Fixed
24
+
25
+ - **`wsdot_get_toll_rates` output descriptions** — `stateRoute` is stated as the bare, zero-padded feed value, and `travelDirection` as one fixed code per facility on SR 99, SR 509, and SR 520 rather than the direction of travel.
26
+ - **`wsdot_search_cameras` `region` description** — `OS` is the Oregon TripCheck cameras around Portland and `WA` the airport cameras plus a few ferry-terminal cameras, not "Olympic South" and "statewide".
27
+
28
+ ## Dependencies
29
+
30
+ - `@types/node` `^26.6.1` → `^26.6.2` (dev)
@@ -0,0 +1,26 @@
1
+ ---
2
+ summary: "Ferry routes list the terminal pairs each one serves and schedules carry per-sailing vessel and annotation detail; dates WSF has no schedule for fail as invalid_date, and blank upstream strings are absent instead of empty."
3
+ breaking: true
4
+ security: false
5
+ ---
6
+
7
+ # 0.4.0 — 2026-09-24
8
+
9
+ ## Added
10
+
11
+ - **`wsdot_get_ferry_routes` `terminalPairs`** ([#41](https://github.com/cyanheads/wsdot-mcp-server/issues/41)) — each route lists the directed departing → arriving terminal IDs and names it serves on the date, the exact pairs `wsdot_get_ferry_schedule` accepts; `[]` when it serves none. Pairs are cached per date until WSF's `cacheflushdate` changes, and a failed pair lookup fails the call.
12
+ - **`wsdot_get_ferry_schedule` sailing detail** ([#50](https://github.com/cyanheads/wsdot-mcp-server/issues/50)) — each sailing gains `vesselId`, `loadingRule`, `vesselHandicapAccessible`, and `annotationIndexes` into a new pair-level `annotations` list, plus a pair-wide `sailingNotes`. Both note fields are HTML upstream and returned as plain text with link URLs kept.
13
+
14
+ ## Changed
15
+
16
+ - **Ferry dates without a published schedule fail as `invalid_date`** ([#40](https://github.com/cyanheads/wsdot-mcp-server/issues/40)) — on `wsdot_get_ferry_routes` and `wsdot_get_ferry_schedule`, a date outside WSF's window is non-retryable `invalid_date` with WSF's accepted range in the message, not `api_unavailable` or `invalid_terminal_pair`. The schedule tool also returns `invalid_date` for an in-window date with no routes loaded; the routes tool returns `[]` with a notice that no longer advises retrying.
17
+ - **`invalid_terminal_pair` recovery hint** points at `terminalPairs` on `wsdot_get_ferry_routes` for the same date.
18
+ - **Identity fields are optional** ([#46](https://github.com/cyanheads/wsdot-mcp-server/issues/46)) — `mountainPassId` and `mountainPassName` on `wsdot_get_mountain_passes`, and `terminalId` and `terminalName` on `wsdot_get_ferry_terminals`, are absent when upstream omits them instead of `0` / `'Unknown'`.
19
+ - **Migration:** read the four identity fields above as optional, and expect `invalid_date` where an out-of-window ferry date previously returned `api_unavailable` or `invalid_terminal_pair`.
20
+ - **Internal:** `docs/design.md` gives the ferry `TripDate` path format as `YYYY-MM-DD` and records decisions 13 (pair cache) and 14 (date rejection over a `validdaterange` pre-check); service tests reject any unmocked `fetch`.
21
+
22
+ ## Fixed
23
+
24
+ - **Blank upstream strings** ([#46](https://github.com/cyanheads/wsdot-mcp-server/issues/46)) — an empty, whitespace-only, or markup-only upstream string is absent from both `structuredContent` and `content[]` on every tool, and a populated one is trimmed; a blank WCF date yields no date field. A pass's blank `weatherCondition` was the visible case.
25
+ - **`wsdot_get_ferry_terminals` coordinates** ([#42](https://github.com/cyanheads/wsdot-mcp-server/issues/42)) — the service reads `Terminals/rest/terminallocations`, so `latitude` and `longitude` populate on all 20 terminals.
26
+ - **`wsdot_get_ferry_schedule` `remainingOnly`** — reports the effective value, `false` for any date other than today.
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ await createApp({
36
36
  ],
37
37
  resources: [],
38
38
  prompts: [],
39
- instructions: 'WSDOT Traveler Information server for Washington State. Traffic tools (mountain passes, alerts, travel times, toll rates, border waits, cameras) use the WSDOT Traffic API. Ferry tools use the WSF Ferry API. For ferry schedule and space lookups, first call wsdot_get_ferry_terminals to resolve terminal names to numeric IDs. Ferry route IDs from wsdot_get_ferry_routes correspond to impactedRouteIds in wsdot_get_ferry_alerts.',
39
+ instructions: 'WSDOT Traveler Information server for Washington State. Traffic tools (mountain passes, alerts, travel times, toll rates, border waits, cameras) use the WSDOT Traffic API. Ferry tools use the WSF Ferry API. For ferry schedule and space lookups, first call wsdot_get_ferry_terminals to resolve terminal names to numeric IDs; wsdot_get_ferry_routes lists the terminal pairs each route serves on a date, which are the pairs wsdot_get_ferry_schedule accepts. Ferry route IDs from wsdot_get_ferry_routes correspond to impactedRouteIds in wsdot_get_ferry_alerts.',
40
40
  landing: { requireAuth: false },
41
41
  setup(core) {
42
42
  initTrafficApiService(core.config, core.storage);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EACL,cAAc,EACd,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,aAAa,GACd,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,kBAAkB;IACxB,KAAK,EAAE,kBAAkB;IACzB,4FAA4F;IAC5F,yFAAyF;IACzF,sCAAsC;IACtC,WAAW,EAAE,WAAW;IACxB,qFAAqF;IACrF,kFAAkF;IAClF,UAAU,EAAE;QACV,YAAY,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE;QACzD,iBAAiB,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE;KAC/D;IACD,KAAK,EAAE;QACL,iBAAiB;QACjB,YAAY;QACZ,cAAc;QACd,YAAY;QACZ,cAAc;QACd,aAAa;QACb,iBAAiB;QACjB,cAAc;QACd,gBAAgB;QAChB,kBAAkB;QAClB,gBAAgB;QAChB,cAAc;KACf;IACD,SAAS,EAAE,EAAE;IACb,OAAO,EAAE,EAAE;IACX,YAAY,EACV,2aAA2a;IAC7a,OAAO,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE;IAC/B,KAAK,CAAC,IAAI;QACR,qBAAqB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACjD,mBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EACL,cAAc,EACd,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,aAAa,GACd,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,kBAAkB;IACxB,KAAK,EAAE,kBAAkB;IACzB,4FAA4F;IAC5F,yFAAyF;IACzF,sCAAsC;IACtC,WAAW,EAAE,WAAW;IACxB,qFAAqF;IACrF,kFAAkF;IAClF,UAAU,EAAE;QACV,YAAY,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE;QACzD,iBAAiB,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE;KAC/D;IACD,KAAK,EAAE;QACL,iBAAiB;QACjB,YAAY;QACZ,cAAc;QACd,YAAY;QACZ,cAAc;QACd,aAAa;QACb,iBAAiB;QACjB,cAAc;QACd,gBAAgB;QAChB,kBAAkB;QAClB,gBAAgB;QAChB,cAAc;KACf;IACD,SAAS,EAAE,EAAE;IACb,OAAO,EAAE,EAAE;IACX,YAAY,EACV,8iBAA8iB;IAChjB,OAAO,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE;IAC/B,KAAK,CAAC,IAAI;QACR,qBAAqB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACjD,mBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;CACF,CAAC,CAAC"}
@@ -11,6 +11,12 @@ export declare const getFerryRoutes: import("@cyanheads/mcp-ts-core").ToolDefini
11
11
  routeId: z.ZodOptional<z.ZodNumber>;
12
12
  routeAbbrev: z.ZodOptional<z.ZodString>;
13
13
  description: z.ZodOptional<z.ZodString>;
14
+ terminalPairs: z.ZodOptional<z.ZodArray<z.ZodObject<{
15
+ departingTerminalId: z.ZodNumber;
16
+ departingTerminalName: z.ZodOptional<z.ZodString>;
17
+ arrivingTerminalId: z.ZodNumber;
18
+ arrivingTerminalName: z.ZodOptional<z.ZodString>;
19
+ }, z.core.$strip>>>;
14
20
  }, z.core.$strip>>;
15
21
  }, z.core.$strip>, readonly [{
16
22
  readonly reason: "api_unavailable";
@@ -29,8 +35,9 @@ export declare const getFerryRoutes: import("@cyanheads/mcp-ts-core").ToolDefini
29
35
  }, {
30
36
  readonly reason: "invalid_date";
31
37
  readonly code: JsonRpcErrorCode.ValidationError;
32
- readonly when: "The provided tripDate is not a valid ISO 8601 date.";
33
- readonly recovery: "Provide a valid date in YYYY-MM-DD format, such as 2026-05-23.";
38
+ readonly when: "The tripDate is not a valid YYYY-MM-DD date, or WSF rejects it as outside the dates it has published a schedule for.";
39
+ readonly retryable: false;
40
+ readonly recovery: "Use a YYYY-MM-DD date from today (Pacific time) through the end of the posted schedule; when WSF rejects the date, the message states the range it accepts.";
34
41
  }], {
35
42
  readonly tripDate: z.ZodString;
36
43
  readonly totalCount: z.ZodNumber;
@@ -1 +1 @@
1
- {"version":3,"file":"get-ferry-routes.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-ferry-routes.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGjE,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyHzB,CAAC"}
1
+ {"version":3,"file":"get-ferry-routes.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-ferry-routes.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGjE,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyKzB,CAAC"}
@@ -7,10 +7,12 @@ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
7
  import { FerryApiService, getFerryApiService } from '../../../services/ferry/ferry-service.js';
8
8
  export const getFerryRoutes = tool('wsdot_get_ferry_routes', {
9
9
  title: 'Get Ferry Routes',
10
- description: 'Returns the main WSF ferry routes operating on a given date. ' +
10
+ description: 'Returns the main WSF ferry routes operating on a given date, each with the directed terminal ' +
11
+ 'pairs it serves that day (terminalPairs) — departing and arriving terminal IDs and names, which ' +
12
+ 'are exactly the pairs wsdot_get_ferry_schedule accepts for that date. ' +
11
13
  'Route IDs correspond to impactedRouteIds in ferry alerts from wsdot_get_ferry_alerts, though some ' +
12
14
  'alert route IDs (seasonal, San Juan interisland, or Sidney B.C.) may not appear in this list. ' +
13
- 'To get terminal IDs for schedule and space lookups, use wsdot_get_ferry_terminals.',
15
+ 'For the full terminal list with coordinates, use wsdot_get_ferry_terminals.',
14
16
  annotations: { readOnlyHint: true },
15
17
  input: z.object({
16
18
  tripDate: z
@@ -35,6 +37,28 @@ export const getFerryRoutes = tool('wsdot_get_ferry_routes', {
35
37
  .string()
36
38
  .optional()
37
39
  .describe('Full route description (e.g. "Seattle/Bainbridge Island").'),
40
+ terminalPairs: z
41
+ .array(z
42
+ .object({
43
+ departingTerminalId: z
44
+ .number()
45
+ .describe('Departing terminal ID — the departingTerminalId to pass to wsdot_get_ferry_schedule.'),
46
+ departingTerminalName: z
47
+ .string()
48
+ .optional()
49
+ .describe('Departing terminal name. Absent when upstream omits it.'),
50
+ arrivingTerminalId: z
51
+ .number()
52
+ .describe('Arriving terminal ID — the arrivingTerminalId to pass to wsdot_get_ferry_schedule.'),
53
+ arrivingTerminalName: z
54
+ .string()
55
+ .optional()
56
+ .describe('Arriving terminal name. Absent when upstream omits it.'),
57
+ })
58
+ .describe('One direction of service between two terminals on the requested date.'))
59
+ .optional()
60
+ .describe('Directed terminal pairs this route serves on the requested date; empty when it serves none. ' +
61
+ 'Absent only when upstream omits the route ID the lookup needs.'),
38
62
  })
39
63
  .describe('A WSF ferry route operating on the requested date.'))
40
64
  .describe('Ferry routes operating on the requested date.'),
@@ -67,8 +91,9 @@ export const getFerryRoutes = tool('wsdot_get_ferry_routes', {
67
91
  {
68
92
  reason: 'invalid_date',
69
93
  code: JsonRpcErrorCode.ValidationError,
70
- when: 'The provided tripDate is not a valid ISO 8601 date.',
71
- recovery: 'Provide a valid date in YYYY-MM-DD format, such as 2026-05-23.',
94
+ when: 'The tripDate is not a valid YYYY-MM-DD date, or WSF rejects it as outside the dates it has published a schedule for.',
95
+ retryable: false,
96
+ recovery: 'Use a YYYY-MM-DD date from today (Pacific time) through the end of the posted schedule; when WSF rejects the date, the message states the range it accepts.',
72
97
  },
73
98
  ],
74
99
  async handler(input, ctx) {
@@ -85,7 +110,7 @@ export const getFerryRoutes = tool('wsdot_get_ferry_routes', {
85
110
  ctx.log.info('Ferry routes fetched', { tripDate, count: routes.length });
86
111
  ctx.enrich({ tripDate, totalCount: routes.length });
87
112
  if (routes.length === 0) {
88
- ctx.enrich.notice(`No ferry routes found for ${tripDate}. The WSF API may be temporarily unavailable or no routes operate on this date — retry in 30 seconds.`);
113
+ ctx.enrich.notice(`WSF lists no routes for ${tripDate}. The date is inside the window WSF accepts, but no sailings are loaded for it yet — typically a season whose schedule has not been posted. Try an earlier date.`);
89
114
  }
90
115
  return { routes };
91
116
  },
@@ -93,6 +118,7 @@ export const getFerryRoutes = tool('wsdot_get_ferry_routes', {
93
118
  if (result.routes.length === 0) {
94
119
  return [{ type: 'text', text: 'No ferry routes found for this date.' }];
95
120
  }
121
+ const terminal = (id, name) => name ? `${name} (${id})` : `terminal ${id}`;
96
122
  const lines = [];
97
123
  for (const r of result.routes) {
98
124
  const name = r.description ?? r.routeAbbrev ?? 'Unknown route';
@@ -101,6 +127,15 @@ export const getFerryRoutes = tool('wsdot_get_ferry_routes', {
101
127
  lines.push(`**Abbrev:** ${r.routeAbbrev}`);
102
128
  if (r.routeId != null)
103
129
  lines.push(`**Route ID:** ${r.routeId}`);
130
+ if (r.terminalPairs?.length === 0) {
131
+ lines.push('**Terminal pairs:** none served on this date');
132
+ }
133
+ else if (r.terminalPairs) {
134
+ lines.push('**Terminal pairs** (departing → arriving):');
135
+ for (const p of r.terminalPairs) {
136
+ lines.push(`- ${terminal(p.departingTerminalId, p.departingTerminalName)} → ${terminal(p.arrivingTerminalId, p.arrivingTerminalName)}`);
137
+ }
138
+ }
104
139
  lines.push('');
105
140
  }
106
141
  return [{ type: 'text', text: lines.join('\n') }];
@@ -1 +1 @@
1
- {"version":3,"file":"get-ferry-routes.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-ferry-routes.tool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"get-ferry-routes.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-ferry-routes.tool.ts"],"names":[],"mappings":"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"}
@@ -12,10 +12,16 @@ export declare const getFerrySchedule: import("@cyanheads/mcp-ts-core").ToolDefi
12
12
  }, z.core.$strip>, z.ZodObject<{
13
13
  departingTerminalName: z.ZodOptional<z.ZodString>;
14
14
  arrivingTerminalName: z.ZodOptional<z.ZodString>;
15
+ annotations: z.ZodOptional<z.ZodArray<z.ZodString>>;
16
+ sailingNotes: z.ZodOptional<z.ZodString>;
15
17
  sailings: z.ZodArray<z.ZodObject<{
16
18
  departureTime: z.ZodOptional<z.ZodString>;
17
19
  arrivalTime: z.ZodOptional<z.ZodString>;
18
20
  vesselName: z.ZodOptional<z.ZodString>;
21
+ vesselId: z.ZodOptional<z.ZodNumber>;
22
+ loadingRule: z.ZodOptional<z.ZodNumber>;
23
+ vesselHandicapAccessible: z.ZodOptional<z.ZodBoolean>;
24
+ annotationIndexes: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
19
25
  }, z.core.$strip>>;
20
26
  }, z.core.$strip>, readonly [{
21
27
  readonly reason: "api_unavailable";
@@ -35,12 +41,13 @@ export declare const getFerrySchedule: import("@cyanheads/mcp-ts-core").ToolDefi
35
41
  readonly reason: "invalid_terminal_pair";
36
42
  readonly code: JsonRpcErrorCode.ValidationError;
37
43
  readonly when: "The terminal ID pair is invalid or does not form a valid ferry route.";
38
- readonly recovery: "Use wsdot_get_ferry_terminals to list valid terminal IDs and wsdot_get_ferry_routes to find valid pairs.";
44
+ readonly recovery: "Call wsdot_get_ferry_routes for the same tripDate and pick a departing → arriving pair from a route’s terminalPairs; wsdot_get_ferry_terminals maps terminal names to IDs.";
39
45
  }, {
40
46
  readonly reason: "invalid_date";
41
47
  readonly code: JsonRpcErrorCode.ValidationError;
42
- readonly when: "The provided tripDate is not a valid ISO 8601 date.";
43
- readonly recovery: "Provide a valid date in YYYY-MM-DD format, such as 2026-05-23.";
48
+ readonly when: "The tripDate is not a valid YYYY-MM-DD date, WSF rejects it as outside the dates it has published a schedule for, or WSF lists no routes on it.";
49
+ readonly retryable: false;
50
+ readonly recovery: "Use a YYYY-MM-DD date from today (Pacific time) through the end of the posted schedule, on which wsdot_get_ferry_routes lists routes; when WSF rejects the date, the message states the range it accepts.";
44
51
  }], {
45
52
  readonly tripDate: z.ZodString;
46
53
  readonly remainingOnly: z.ZodBoolean;
@@ -1 +1 @@
1
- {"version":3,"file":"get-ferry-schedule.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-ferry-schedule.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAY,MAAM,+BAA+B,CAAC;AAI3E,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiM3B,CAAC"}
1
+ {"version":3,"file":"get-ferry-schedule.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-ferry-schedule.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAY,MAAM,+BAA+B,CAAC;AAI3E,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8R3B,CAAC"}