onebusaway-mcp-server 0.1.1

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 (85) hide show
  1. package/CLAUDE.md +367 -0
  2. package/Dockerfile +99 -0
  3. package/LICENSE +201 -0
  4. package/README.md +322 -0
  5. package/changelog/0.1.x/0.1.0.md +15 -0
  6. package/changelog/0.1.x/0.1.1.md +17 -0
  7. package/changelog/template.md +119 -0
  8. package/dist/config/server-config.d.ts +13 -0
  9. package/dist/config/server-config.d.ts.map +1 -0
  10. package/dist/config/server-config.js +26 -0
  11. package/dist/config/server-config.js.map +1 -0
  12. package/dist/index.d.ts +7 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +53 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/mcp-server/resources/definitions/route.resource.d.ts +9 -0
  17. package/dist/mcp-server/resources/definitions/route.resource.d.ts.map +1 -0
  18. package/dist/mcp-server/resources/definitions/route.resource.js +33 -0
  19. package/dist/mcp-server/resources/definitions/route.resource.js.map +1 -0
  20. package/dist/mcp-server/resources/definitions/stop.resource.d.ts +9 -0
  21. package/dist/mcp-server/resources/definitions/stop.resource.d.ts.map +1 -0
  22. package/dist/mcp-server/resources/definitions/stop.resource.js +33 -0
  23. package/dist/mcp-server/resources/definitions/stop.resource.js.map +1 -0
  24. package/dist/mcp-server/tools/definitions/find-routes.tool.d.ts +24 -0
  25. package/dist/mcp-server/tools/definitions/find-routes.tool.d.ts.map +1 -0
  26. package/dist/mcp-server/tools/definitions/find-routes.tool.js +72 -0
  27. package/dist/mcp-server/tools/definitions/find-routes.tool.js.map +1 -0
  28. package/dist/mcp-server/tools/definitions/find-stops.tool.d.ts +28 -0
  29. package/dist/mcp-server/tools/definitions/find-stops.tool.d.ts.map +1 -0
  30. package/dist/mcp-server/tools/definitions/find-stops.tool.js +76 -0
  31. package/dist/mcp-server/tools/definitions/find-stops.tool.js.map +1 -0
  32. package/dist/mcp-server/tools/definitions/get-arrivals.tool.d.ts +49 -0
  33. package/dist/mcp-server/tools/definitions/get-arrivals.tool.d.ts.map +1 -0
  34. package/dist/mcp-server/tools/definitions/get-arrivals.tool.js +176 -0
  35. package/dist/mcp-server/tools/definitions/get-arrivals.tool.js.map +1 -0
  36. package/dist/mcp-server/tools/definitions/get-route.tool.d.ts +25 -0
  37. package/dist/mcp-server/tools/definitions/get-route.tool.d.ts.map +1 -0
  38. package/dist/mcp-server/tools/definitions/get-route.tool.js +60 -0
  39. package/dist/mcp-server/tools/definitions/get-route.tool.js.map +1 -0
  40. package/dist/mcp-server/tools/definitions/get-schedule-for-route.tool.d.ts +31 -0
  41. package/dist/mcp-server/tools/definitions/get-schedule-for-route.tool.d.ts.map +1 -0
  42. package/dist/mcp-server/tools/definitions/get-schedule-for-route.tool.js +107 -0
  43. package/dist/mcp-server/tools/definitions/get-schedule-for-route.tool.js.map +1 -0
  44. package/dist/mcp-server/tools/definitions/get-schedule-for-stop.tool.d.ts +31 -0
  45. package/dist/mcp-server/tools/definitions/get-schedule-for-stop.tool.d.ts.map +1 -0
  46. package/dist/mcp-server/tools/definitions/get-schedule-for-stop.tool.js +105 -0
  47. package/dist/mcp-server/tools/definitions/get-schedule-for-stop.tool.js.map +1 -0
  48. package/dist/mcp-server/tools/definitions/get-stop.tool.d.ts +28 -0
  49. package/dist/mcp-server/tools/definitions/get-stop.tool.d.ts.map +1 -0
  50. package/dist/mcp-server/tools/definitions/get-stop.tool.js +57 -0
  51. package/dist/mcp-server/tools/definitions/get-stop.tool.js.map +1 -0
  52. package/dist/mcp-server/tools/definitions/get-trip.tool.d.ts +42 -0
  53. package/dist/mcp-server/tools/definitions/get-trip.tool.d.ts.map +1 -0
  54. package/dist/mcp-server/tools/definitions/get-trip.tool.js +143 -0
  55. package/dist/mcp-server/tools/definitions/get-trip.tool.js.map +1 -0
  56. package/dist/mcp-server/tools/definitions/get-vehicles.tool.d.ts +34 -0
  57. package/dist/mcp-server/tools/definitions/get-vehicles.tool.d.ts.map +1 -0
  58. package/dist/mcp-server/tools/definitions/get-vehicles.tool.js +117 -0
  59. package/dist/mcp-server/tools/definitions/get-vehicles.tool.js.map +1 -0
  60. package/dist/mcp-server/tools/definitions/list-agencies.tool.d.ts +23 -0
  61. package/dist/mcp-server/tools/definitions/list-agencies.tool.d.ts.map +1 -0
  62. package/dist/mcp-server/tools/definitions/list-agencies.tool.js +58 -0
  63. package/dist/mcp-server/tools/definitions/list-agencies.tool.js.map +1 -0
  64. package/dist/mcp-server/tools/definitions/list-routes-for-agency.tool.d.ts +25 -0
  65. package/dist/mcp-server/tools/definitions/list-routes-for-agency.tool.d.ts.map +1 -0
  66. package/dist/mcp-server/tools/definitions/list-routes-for-agency.tool.js +71 -0
  67. package/dist/mcp-server/tools/definitions/list-routes-for-agency.tool.js.map +1 -0
  68. package/dist/mcp-server/tools/definitions/search-routes.tool.d.ts +26 -0
  69. package/dist/mcp-server/tools/definitions/search-routes.tool.d.ts.map +1 -0
  70. package/dist/mcp-server/tools/definitions/search-routes.tool.js +68 -0
  71. package/dist/mcp-server/tools/definitions/search-routes.tool.js.map +1 -0
  72. package/dist/mcp-server/tools/definitions/search-stops.tool.d.ts +25 -0
  73. package/dist/mcp-server/tools/definitions/search-stops.tool.d.ts.map +1 -0
  74. package/dist/mcp-server/tools/definitions/search-stops.tool.js +61 -0
  75. package/dist/mcp-server/tools/definitions/search-stops.tool.js.map +1 -0
  76. package/dist/services/onebusaway/onebusaway-service.d.ts +64 -0
  77. package/dist/services/onebusaway/onebusaway-service.d.ts.map +1 -0
  78. package/dist/services/onebusaway/onebusaway-service.js +441 -0
  79. package/dist/services/onebusaway/onebusaway-service.js.map +1 -0
  80. package/dist/services/onebusaway/types.d.ts +147 -0
  81. package/dist/services/onebusaway/types.d.ts.map +1 -0
  82. package/dist/services/onebusaway/types.js +6 -0
  83. package/dist/services/onebusaway/types.js.map +1 -0
  84. package/package.json +90 -0
  85. package/server.json +127 -0
package/README.md ADDED
@@ -0,0 +1,322 @@
1
+ <div align="center">
2
+ <h1>onebusaway-mcp-server</h1>
3
+ <p><b>Query stops, routes, real-time arrivals, vehicle positions, and schedules from OneBusAway transit APIs via MCP. STDIO or Streamable HTTP.</b>
4
+ <div>13 Tools • 2 Resources</div>
5
+ </p>
6
+ </div>
7
+
8
+ <div align="center">
9
+
10
+ [![Version](https://img.shields.io/badge/Version-0.1.1-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/onebusaway-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.29.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/onebusaway-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/onebusaway-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^5.9.3-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun->=1.3.0-blueviolet.svg?style=flat-square)](https://bun.sh/)
11
+
12
+ </div>
13
+
14
+ <div align="center">
15
+
16
+ [![Install in Claude Desktop](https://img.shields.io/badge/Install_in-Claude_Desktop-D97757?style=for-the-badge&logo=anthropic&logoColor=white)](https://github.com/cyanheads/onebusaway-mcp-server/releases/latest/download/onebusaway-mcp-server.mcpb) [![Install in Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=onebusaway-mcp-server&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIm9uZWJ1c2F3YXktbWNwLXNlcnZlciJdLCJlbnYiOnsiT05FQlVTQVdBWV9BUElfS0VZIjoiVEVTVCJ9fQ==) [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=for-the-badge&logo=visualstudiocode&logoColor=white)](https://vscode.dev/redirect?url=vscode:mcp/install?%7B%22name%22%3A%22onebusaway-mcp-server%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22onebusaway-mcp-server%22%5D%2C%22env%22%3A%7B%22ONEBUSAWAY_API_KEY%22%3A%22TEST%22%7D%7D)
17
+
18
+ [![Framework](https://img.shields.io/badge/Built%20on-@cyanheads/mcp--ts--core-67E8F9?style=flat-square)](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
19
+
20
+ </div>
21
+
22
+ ---
23
+
24
+ ## Tools
25
+
26
+ 13 tools covering the full OneBusAway transit data surface — discovery, real-time operations, and schedules:
27
+
28
+ | Tool | Description |
29
+ |:---|:---|
30
+ | `onebusaway_list_agencies` | List all transit agencies on this OneBusAway instance with IDs, contact info, and geographic coverage |
31
+ | `onebusaway_find_stops` | Find bus stops near a lat/lon within a configurable radius, optionally filtered by stop code |
32
+ | `onebusaway_search_stops` | Search stops by name or code string to resolve a human-readable name to a stop ID |
33
+ | `onebusaway_get_stop` | Fetch details for a specific stop by agency-prefixed ID |
34
+ | `onebusaway_find_routes` | Find transit routes near a lat/lon, optionally filtered by name or number |
35
+ | `onebusaway_search_routes` | Search routes by name or number to resolve a route short name to a route ID |
36
+ | `onebusaway_get_route` | Fetch details for a specific route by agency-prefixed ID |
37
+ | `onebusaway_list_routes_for_agency` | List all routes operated by an agency |
38
+ | `onebusaway_get_arrivals` | Real-time arrivals and departures at a stop — GPS-tracked predictions, schedule deviation, vehicle positions, and active alerts |
39
+ | `onebusaway_get_trip` | Real-time status and full stop sequence for an active trip |
40
+ | `onebusaway_get_vehicles` | Real-time positions of all active vehicles for an agency, optionally filtered to one route |
41
+ | `onebusaway_get_schedule_for_stop` | Full-day departure schedule for a stop by route and direction |
42
+ | `onebusaway_get_schedule_for_route` | Full-day schedule for a route — all trips and stop sequences |
43
+
44
+ ### `onebusaway_find_stops`
45
+
46
+ Find bus stops near a geographic location.
47
+
48
+ - Configurable search radius (default 300m, max ~1600m before results degrade)
49
+ - Optional stop code filter (the number printed on the sign, e.g. `75403`)
50
+ - Returns stop ID, code, name, direction, served route IDs, and wheelchair boarding status
51
+ - `limitExceeded` flag signals when more stops exist beyond the returned set
52
+ - Stop IDs returned here feed directly into `onebusaway_get_arrivals`
53
+
54
+ ---
55
+
56
+ ### `onebusaway_get_arrivals`
57
+
58
+ Real-time arrivals and departures at a stop.
59
+
60
+ - Configurable time window (`minutesBefore`, `minutesAfter` — defaults 5/35)
61
+ - `predicted` boolean distinguishes GPS-tracked estimates from schedule-only projections
62
+ - Schedule deviation in seconds (positive = late, negative = early)
63
+ - Vehicle position and stops-away count when available
64
+ - Active service alert summaries included inline
65
+ - `tripId` on each arrival feeds `onebusaway_get_trip` for vehicle tracking
66
+
67
+ ---
68
+
69
+ ### `onebusaway_get_trip`
70
+
71
+ Real-time status and stop sequence for a specific trip.
72
+
73
+ - Journey phase (`in_progress`, `layover_before`, `layover_during`)
74
+ - Vehicle GPS position, heading, and schedule deviation
75
+ - Full stop sequence with GTFS arrival/departure times and distance along trip
76
+ - Optional `serviceDateMs` for looking up trips from a prior service day
77
+
78
+ ---
79
+
80
+ ### `onebusaway_get_vehicles`
81
+
82
+ Real-time positions of all active vehicles for an agency.
83
+
84
+ - Returns GPS coordinates, heading, schedule deviation, and current trip for every active vehicle
85
+ - Optional `routeId` filter (applied client-side after fetching all agency vehicles)
86
+ - Phase and `predicted` flag distinguish actively-reporting vehicles from stale entries
87
+
88
+ ---
89
+
90
+ ### `onebusaway_search_routes`
91
+
92
+ Search for routes by name or number across the instance.
93
+
94
+ - Falls back gracefully: the Puget Sound instance returns 404 on the search endpoint — `onebusaway_find_routes` (lat/lon) or `onebusaway_list_routes_for_agency` are the alternatives
95
+ - Error contract surfaces this with structured recovery hints
96
+
97
+ ---
98
+
99
+ ### `onebusaway_get_schedule_for_stop` and `onebusaway_get_schedule_for_route`
100
+
101
+ Static schedule lookups — full-day timetables without real-time data.
102
+
103
+ - Date parameter (ISO 8601) defaults to today in the agency's timezone
104
+ - `onebusaway_get_schedule_for_stop`: all departures grouped by route and direction, with trip IDs for follow-up calls
105
+ - `onebusaway_get_schedule_for_route`: all trips for the route with full stop sequences and GTFS stop times
106
+
107
+ ## Resources
108
+
109
+ | Type | Name | Description |
110
+ |:---|:---|:---|
111
+ | Resource | `onebusaway://stop/{stopId}` | Stop metadata — name, coordinates, served routes, and wheelchair accessibility |
112
+ | Resource | `onebusaway://route/{routeId}` | Route metadata — short name, description, agency, and schedule URL |
113
+
114
+ All resource data is also reachable via `onebusaway_get_stop` and `onebusaway_get_route`. Stop and route IDs use agency-prefixed format: `{agencyId}_{localId}` (e.g. `1_75403`, `1_100259`).
115
+
116
+ ## Features
117
+
118
+ Built on [`@cyanheads/mcp-ts-core`](https://www.npmjs.com/package/@cyanheads/mcp-ts-core):
119
+
120
+ - Declarative tool and resource definitions — single file per primitive, framework handles registration and validation
121
+ - Unified error handling — handlers throw, framework catches, classifies, and formats
122
+ - Pluggable auth: `none`, `jwt`, `oauth`
123
+ - Swappable storage backends: `in-memory`, `filesystem`, `Supabase`, `Cloudflare KV/R2/D1`
124
+ - Structured logging with optional OpenTelemetry tracing
125
+ - STDIO and Streamable HTTP transports
126
+
127
+ OneBusAway-specific:
128
+
129
+ - Wraps [`onebusaway-sdk`](https://www.npmjs.com/package/onebusaway-sdk) with typed error classification (`NotFound`, `RateLimited`, `ServiceUnavailable`)
130
+ - Defaults to the Puget Sound instance (`api.pugetsound.onebusaway.org`) — works with `ONEBUSAWAY_API_KEY=TEST` for development
131
+ - Configurable `ONEBUSAWAY_BASE_URL` for any OneBusAway-compatible instance (NYC, Washington DC, Tampa, etc.)
132
+ - Server-level instructions guide agents through stop ID format, recommended workflows, and OneBusAway's limitations (no trip planning)
133
+
134
+ Agent-friendly output:
135
+
136
+ - `predicted` boolean on every arrival and vehicle distinguishes GPS-tracked data from schedule-only projections — agents branch on data, not string parsing
137
+ - Schedule deviation in seconds on arrivals, trips, and vehicle positions — structured for countdown timer math
138
+ - Cross-tool linkage: `tripId` from arrivals feeds `onebusaway_get_trip`; `stopId` from searches feeds arrivals; `agencyId` from list feeds vehicles and route listing
139
+ - Structured error contracts with recovery hints (`onebusaway_search_routes` 404 → fallback to `onebusaway_find_routes` or `onebusaway_list_routes_for_agency`)
140
+
141
+ ## Getting started
142
+
143
+ Add the following to your MCP client configuration file. `ONEBUSAWAY_API_KEY=TEST` works on the Puget Sound instance without registration.
144
+
145
+ ```json
146
+ {
147
+ "mcpServers": {
148
+ "onebusaway": {
149
+ "type": "stdio",
150
+ "command": "bunx",
151
+ "args": ["onebusaway-mcp-server@latest"],
152
+ "env": {
153
+ "MCP_TRANSPORT_TYPE": "stdio",
154
+ "MCP_LOG_LEVEL": "info",
155
+ "ONEBUSAWAY_API_KEY": "TEST"
156
+ }
157
+ }
158
+ }
159
+ }
160
+ ```
161
+
162
+ Or with npx (no Bun required):
163
+
164
+ ```json
165
+ {
166
+ "mcpServers": {
167
+ "onebusaway": {
168
+ "type": "stdio",
169
+ "command": "npx",
170
+ "args": ["-y", "onebusaway-mcp-server@latest"],
171
+ "env": {
172
+ "MCP_TRANSPORT_TYPE": "stdio",
173
+ "MCP_LOG_LEVEL": "info",
174
+ "ONEBUSAWAY_API_KEY": "TEST"
175
+ }
176
+ }
177
+ }
178
+ }
179
+ ```
180
+
181
+ Or with Docker:
182
+
183
+ ```json
184
+ {
185
+ "mcpServers": {
186
+ "onebusaway": {
187
+ "type": "stdio",
188
+ "command": "docker",
189
+ "args": [
190
+ "run", "-i", "--rm",
191
+ "-e", "MCP_TRANSPORT_TYPE=stdio",
192
+ "-e", "ONEBUSAWAY_API_KEY=TEST",
193
+ "ghcr.io/cyanheads/onebusaway-mcp-server:latest"
194
+ ]
195
+ }
196
+ }
197
+ }
198
+ ```
199
+
200
+ For Streamable HTTP, set the transport and start the server:
201
+
202
+ ```sh
203
+ MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 ONEBUSAWAY_API_KEY=TEST bun run start:http
204
+ # Server listens at http://localhost:3010/mcp
205
+ ```
206
+
207
+ ### Prerequisites
208
+
209
+ - [Bun v1.3.0](https://bun.sh/) or higher (or Node.js v24+).
210
+ - An OneBusAway API key. `TEST` works on the Puget Sound instance for development. For production use or other instances, register at the relevant agency's developer portal.
211
+
212
+ ### Installation
213
+
214
+ 1. **Clone the repository:**
215
+
216
+ ```sh
217
+ git clone https://github.com/cyanheads/onebusaway-mcp-server.git
218
+ ```
219
+
220
+ 2. **Navigate into the directory:**
221
+
222
+ ```sh
223
+ cd onebusaway-mcp-server
224
+ ```
225
+
226
+ 3. **Install dependencies:**
227
+
228
+ ```sh
229
+ bun install
230
+ ```
231
+
232
+ 4. **Configure environment:**
233
+
234
+ ```sh
235
+ cp .env.example .env
236
+ # edit .env — set ONEBUSAWAY_API_KEY if needed
237
+ ```
238
+
239
+ ## Configuration
240
+
241
+ All configuration is validated at startup via Zod schemas. Key environment variables:
242
+
243
+ | Variable | Description | Default |
244
+ |:---|:---|:---|
245
+ | `ONEBUSAWAY_API_KEY` | OneBusAway API key. `TEST` works on Puget Sound for development. | `TEST` |
246
+ | `ONEBUSAWAY_BASE_URL` | Base URL for the OneBusAway instance. | `https://api.pugetsound.onebusaway.org` |
247
+ | `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
248
+ | `MCP_HTTP_PORT` | HTTP server port. | `3010` |
249
+ | `MCP_HTTP_ENDPOINT_PATH` | HTTP endpoint path. | `/mcp` |
250
+ | `MCP_AUTH_MODE` | Auth mode: `none`, `jwt`, or `oauth`. | `none` |
251
+ | `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `notice`, `warning`, `error`). | `info` |
252
+ | `LOGS_DIR` | Directory for log files (Node.js only). | `<project-root>/logs` |
253
+ | `STORAGE_PROVIDER_TYPE` | Storage backend: `in-memory`, `filesystem`, `supabase`, `cloudflare-kv/r2/d1`. | `in-memory` |
254
+ | `OTEL_ENABLED` | Enable OpenTelemetry instrumentation. | `false` |
255
+
256
+ See [`.env.example`](./.env.example) for the full list of optional overrides.
257
+
258
+ ## Running the server
259
+
260
+ ### Local development
261
+
262
+ - **Build and run the production version:**
263
+
264
+ ```sh
265
+ # One-time build
266
+ bun run rebuild
267
+
268
+ # Run the built server
269
+ bun run start:http
270
+ # or
271
+ bun run start:stdio
272
+ ```
273
+
274
+ - **Run checks and tests:**
275
+
276
+ ```sh
277
+ bun run devcheck # Lint, format, typecheck, security, changelog sync
278
+ bun run test # Vitest test suite
279
+ bun run lint:mcp # Validate MCP definitions against spec
280
+ ```
281
+
282
+ ### Docker
283
+
284
+ ```sh
285
+ docker build -t onebusaway-mcp-server .
286
+ docker run --rm -e ONEBUSAWAY_API_KEY=TEST -p 3010:3010 onebusaway-mcp-server
287
+ ```
288
+
289
+ The Dockerfile defaults to HTTP transport, stateless session mode, and logs to `/var/log/onebusaway-mcp-server`. OpenTelemetry peer dependencies are installed by default — build with `--build-arg OTEL_ENABLED=false` to omit them.
290
+
291
+ ## Project structure
292
+
293
+ | Directory | Purpose |
294
+ |:---|:---|
295
+ | `src/index.ts` | `createApp()` entry point — registers tools/resources and inits the OneBusAway service. |
296
+ | `src/config/server-config.ts` | Server-specific env var parsing: `ONEBUSAWAY_API_KEY`, `ONEBUSAWAY_BASE_URL`. |
297
+ | `src/mcp-server/tools` | Tool definitions (`*.tool.ts`). 13 tools across discovery, real-time, and schedule operations. |
298
+ | `src/mcp-server/resources` | Resource definitions (`*.resource.ts`). Stop and route metadata resources. |
299
+ | `src/services/onebusaway` | OneBusAway service — wraps `onebusaway-sdk`, typed error classification, domain types. |
300
+ | `tests/` | Unit and integration tests mirroring `src/`. 77 tests covering all tools and resources. |
301
+
302
+ ## Development guide
303
+
304
+ See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rules. The short version:
305
+
306
+ - Handlers throw, framework catches — no `try/catch` in tool logic
307
+ - Use `ctx.log` for request-scoped logging, `ctx.state` for tenant-scoped storage
308
+ - Register new tools and resources in the `createApp()` arrays in `src/index.ts`
309
+ - Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
310
+
311
+ ## Contributing
312
+
313
+ Issues and pull requests are welcome. Run checks and tests before submitting:
314
+
315
+ ```sh
316
+ bun run devcheck
317
+ bun run test
318
+ ```
319
+
320
+ ## License
321
+
322
+ Apache-2.0 — see [LICENSE](LICENSE) for details.
@@ -0,0 +1,15 @@
1
+ ---
2
+ summary: "Initial release — 13 tools and 2 resources for OneBusAway transit data via onebusaway-sdk"
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.1.0 — 2026-05-23
8
+
9
+ ## Added
10
+
11
+ - **13 tools**: `onebusaway_list_agencies`, `onebusaway_find_stops`, `onebusaway_get_stop`, `onebusaway_search_stops`, `onebusaway_find_routes`, `onebusaway_get_route`, `onebusaway_list_routes_for_agency`, `onebusaway_search_routes`, `onebusaway_get_arrivals`, `onebusaway_get_trip`, `onebusaway_get_vehicles`, `onebusaway_get_schedule_for_stop`, `onebusaway_get_schedule_for_route`.
12
+ - **2 resources**: `onebusaway://stop/{stopId}`, `onebusaway://route/{routeId}`.
13
+ - **`OneBusAwayService`** wrapping `onebusaway-sdk` with typed error classification (`NotFound`, `RateLimited`, `ServiceUnavailable`).
14
+ - **`ONEBUSAWAY_API_KEY`** and **`ONEBUSAWAY_BASE_URL`** env vars; defaults to Puget Sound instance with `TEST` key.
15
+ - 77 unit tests covering all tools and resources (happy path, error paths, format output, sparse payloads).
@@ -0,0 +1,17 @@
1
+ ---
2
+ summary: "OneBusAway transit API — 13 tools, 2 resources, real-time arrivals, routes, stops, and vehicles"
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.1.1 — 2026-05-23
8
+
9
+ ## Added
10
+
11
+ - **13 tools**: `onebusaway_list_agencies`, `onebusaway_find_stops`, `onebusaway_search_stops`, `onebusaway_get_stop`, `onebusaway_find_routes`, `onebusaway_search_routes`, `onebusaway_get_route`, `onebusaway_list_routes_for_agency`, `onebusaway_get_arrivals`, `onebusaway_get_trip`, `onebusaway_get_vehicles`, `onebusaway_get_schedule_for_stop`, `onebusaway_get_schedule_for_route`.
12
+ - **2 resources**: `onebusaway://stop/{stopId}`, `onebusaway://route/{routeId}`.
13
+ - **`OneBusAwayService`** wrapping `onebusaway-sdk` with typed error classification (`NotFound`, `RateLimited`, `ServiceUnavailable`).
14
+ - **`ONEBUSAWAY_API_KEY`** and **`ONEBUSAWAY_BASE_URL`** env vars; defaults to Puget Sound instance with `TEST` key.
15
+ - 77 unit tests covering all tools and resources (happy path, error paths, format output, sparse payloads).
16
+ - README with install badges (Claude Desktop, Cursor, VS Code), tool table, and getting-started config snippets.
17
+ - Docker image published to `ghcr.io/cyanheads/onebusaway-mcp-server`.
@@ -0,0 +1,119 @@
1
+ ---
2
+ # FORMAT REFERENCE — do not edit. Copy this file to
3
+ # `changelog/<major.minor>.x/<version>.md` (e.g. `changelog/0.8.x/0.8.6.md`)
4
+ # to author a new release. Set that file's H1 to `# <version> — YYYY-MM-DD`
5
+ # with a concrete date.
6
+
7
+ # Required. One-line GitHub Release-style headline. 350 character cap.
8
+ # Default short and scannable. Don't pad, don't stitch unrelated changes with
9
+ # semicolons — pick the headline. Quotes required: unquoted YAML treats `: `
10
+ # inside the value as a key separator and fails GitHub's strict parser.
11
+ summary: ""
12
+
13
+ # Set `true` when consumers must change code to upgrade: API removals,
14
+ # signature changes, config renames, behavior changes that break existing
15
+ # usage. Flagged as `Breaking` in the rollup.
16
+ breaking: false
17
+
18
+ # Set `true` if this release contains any security fix. Pairs with the
19
+ # `## Security` section below. Flagged as `Security` in the rollup so
20
+ # users can triage upgrade urgency at a glance.
21
+ security: false
22
+ ---
23
+
24
+ # <version> — YYYY-MM-DD
25
+
26
+ <!--
27
+ AUTHORING GUIDE — applies to the new per-version file you create from this
28
+ template.
29
+
30
+ Audience: someone scanning release notes to decide what affects them. Lead
31
+ each bullet with the symbol or concept name in **bold** so they can skip
32
+ what's irrelevant and zoom in on what's not.
33
+
34
+ Tone: terse, fact-dense, not verbose. Default to one sentence per bullet —
35
+ name the symbol, state what changed, stop. Use a second sentence only when
36
+ it carries weight. If a bullet feels long, it is.
37
+
38
+ Cut: mechanism walkthroughs (those belong in JSDoc, AGENTS.md, or the
39
+ relevant skill), ceremonial framings ("This release introduces…",
40
+ backwards-compat paragraphs), file-by-file test enumerations, internal
41
+ implementation notes. Prefer code/symbol names over English re-explanations.
42
+
43
+ Narrative intro: skip by default. Add one short sentence only when the
44
+ release theme genuinely needs framing the bullets can't carry.
45
+
46
+ Sections: Keep a Changelog order — Added, Changed, Deprecated, Removed,
47
+ Fixed, Security. Include only sections with entries; delete the rest
48
+ (including the commented-out scaffolding below). Don't ship empty headers.
49
+
50
+ Include: every distinct fact a reader needs to adopt or audit the release —
51
+ new exports, signatures, lint rule IDs, env vars, breaking changes, version
52
+ bumps on shipped skills. Nothing more.
53
+
54
+ Links: link issues, PRs, docs, or skills where they help a reader jump to
55
+ context. Once per item per entry — don't re-link the same issue in summary,
56
+ narrative, and bullet. Skip links for inline symbol names; code spans speak
57
+ for themselves.
58
+
59
+ Issue/PR URLs: use full URLs. GitHub's bare `#NN` auto-link only resolves
60
+ inside its own UI, not in npm reads or local editors.
61
+
62
+ [#38](https://github.com/cyanheads/mcp-ts-core/issues/38) ← issue
63
+ [#42](https://github.com/cyanheads/mcp-ts-core/pull/42) ← PR
64
+
65
+ Verify numbers exist before linking (`gh issue view NN`, `gh pr view NN`).
66
+ Never speculate on a future number — `#42` for an upcoming PR silently
67
+ resolves to whatever real item already owns 42, and timeline previews pull
68
+ in that unrelated item's metadata.
69
+
70
+ TAG ANNOTATIONS — the annotated tag body renders as the GitHub Release body
71
+ via `gh release create --notes-from-tag`. The tag is a derivative of this
72
+ changelog entry — a condensed, scannable version, not a copy. Format:
73
+
74
+ <theme — omit version number, GitHub prepends it>
75
+ ← blank line
76
+ <1-2 sentence context: what this release does>
77
+ ← blank line
78
+ Dependency bumps: ← section header
79
+ ← blank line
80
+ - `@cyanheads/mcp-ts-core` ^0.9.1 → ^0.9.6 ← bullet
81
+ ← blank line
82
+ Changed: ← only sections with entries
83
+ ← blank line
84
+ - `format()` output includes `query` in text mode
85
+ ← blank line
86
+ Added:
87
+ ← blank line
88
+ - `manifest.json` scaffolded for MCPB bundle support
89
+ - Install badges (Claude Desktop, Cursor, VS Code)
90
+ ← blank line
91
+ <N> tests pass; `bun run devcheck` clean. ← footer
92
+
93
+ Never a flat comma-separated string. Always structured markdown with
94
+ sections. The tag must scan well as a rendered GitHub Release page.
95
+ -->
96
+
97
+ ## Added
98
+
99
+ -
100
+
101
+ ## Changed
102
+
103
+ -
104
+
105
+ <!-- ## Deprecated
106
+
107
+ - -->
108
+
109
+ <!-- ## Removed
110
+
111
+ - -->
112
+
113
+ ## Fixed
114
+
115
+ -
116
+
117
+ <!-- ## Security
118
+
119
+ - -->
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @fileoverview Server-specific configuration for onebusaway-mcp-server.
3
+ * @module config/server-config
4
+ */
5
+ import { z } from '@cyanheads/mcp-ts-core';
6
+ declare const ServerConfigSchema: z.ZodObject<{
7
+ apiKey: z.ZodDefault<z.ZodString>;
8
+ baseUrl: z.ZodDefault<z.ZodString>;
9
+ }, z.core.$strip>;
10
+ export type ServerConfig = z.infer<typeof ServerConfigSchema>;
11
+ export declare function getServerConfig(): ServerConfig;
12
+ export {};
13
+ //# sourceMappingURL=server-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAK3C,QAAA,MAAM,kBAAkB;;;iBAStB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,wBAAgB,eAAe,IAAI,YAAY,CAM9C"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @fileoverview Server-specific configuration for onebusaway-mcp-server.
3
+ * @module config/server-config
4
+ */
5
+ import { z } from '@cyanheads/mcp-ts-core';
6
+ import { parseEnvConfig } from '@cyanheads/mcp-ts-core/config';
7
+ const PUGET_SOUND_BASE_URL = 'https://api.pugetsound.onebusaway.org';
8
+ const ServerConfigSchema = z.object({
9
+ apiKey: z
10
+ .string()
11
+ .default('TEST')
12
+ .describe('OneBusAway API key. TEST works on Puget Sound for development.'),
13
+ baseUrl: z
14
+ .string()
15
+ .default(PUGET_SOUND_BASE_URL)
16
+ .describe('Base URL for the OneBusAway instance.'),
17
+ });
18
+ let _config;
19
+ export function getServerConfig() {
20
+ _config ??= parseEnvConfig(ServerConfigSchema, {
21
+ apiKey: 'ONEBUSAWAY_API_KEY',
22
+ baseUrl: 'ONEBUSAWAY_BASE_URL',
23
+ });
24
+ return _config;
25
+ }
26
+ //# sourceMappingURL=server-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-config.js","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAE/D,MAAM,oBAAoB,GAAG,uCAAuC,CAAC;AAErE,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,OAAO,CAAC,MAAM,CAAC;SACf,QAAQ,CAAC,gEAAgE,CAAC;IAC7E,OAAO,EAAE,CAAC;SACP,MAAM,EAAE;SACR,OAAO,CAAC,oBAAoB,CAAC;SAC7B,QAAQ,CAAC,uCAAuC,CAAC;CACrD,CAAC,CAAC;AAIH,IAAI,OAAiC,CAAC;AAEtC,MAAM,UAAU,eAAe;IAC7B,OAAO,KAAK,cAAc,CAAC,kBAAkB,EAAE;QAC7C,MAAM,EAAE,oBAAoB;QAC5B,OAAO,EAAE,qBAAqB;KAC/B,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @fileoverview onebusaway-mcp-server MCP server entry point.
4
+ * @module index
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG"}
package/dist/index.js ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @fileoverview onebusaway-mcp-server MCP server entry point.
4
+ * @module index
5
+ */
6
+ import { createApp } from '@cyanheads/mcp-ts-core';
7
+ import { getServerConfig } from './config/server-config.js';
8
+ // Resources
9
+ import { routeResource } from './mcp-server/resources/definitions/route.resource.js';
10
+ import { stopResource } from './mcp-server/resources/definitions/stop.resource.js';
11
+ // Tools
12
+ import { findRoutes } from './mcp-server/tools/definitions/find-routes.tool.js';
13
+ import { findStops } from './mcp-server/tools/definitions/find-stops.tool.js';
14
+ import { getArrivals } from './mcp-server/tools/definitions/get-arrivals.tool.js';
15
+ import { getRoute } from './mcp-server/tools/definitions/get-route.tool.js';
16
+ import { getScheduleForRoute } from './mcp-server/tools/definitions/get-schedule-for-route.tool.js';
17
+ import { getScheduleForStop } from './mcp-server/tools/definitions/get-schedule-for-stop.tool.js';
18
+ import { getStop } from './mcp-server/tools/definitions/get-stop.tool.js';
19
+ import { getTrip } from './mcp-server/tools/definitions/get-trip.tool.js';
20
+ import { getVehicles } from './mcp-server/tools/definitions/get-vehicles.tool.js';
21
+ import { listAgencies } from './mcp-server/tools/definitions/list-agencies.tool.js';
22
+ import { listRoutesForAgency } from './mcp-server/tools/definitions/list-routes-for-agency.tool.js';
23
+ import { searchRoutes } from './mcp-server/tools/definitions/search-routes.tool.js';
24
+ import { searchStops } from './mcp-server/tools/definitions/search-stops.tool.js';
25
+ import { initOneBusAwayService } from './services/onebusaway/onebusaway-service.js';
26
+ await createApp({
27
+ tools: [
28
+ listAgencies,
29
+ findStops,
30
+ findRoutes,
31
+ searchStops,
32
+ searchRoutes,
33
+ getStop,
34
+ getRoute,
35
+ listRoutesForAgency,
36
+ getArrivals,
37
+ getTrip,
38
+ getVehicles,
39
+ getScheduleForStop,
40
+ getScheduleForRoute,
41
+ ],
42
+ resources: [stopResource, routeResource],
43
+ prompts: [],
44
+ instructions: 'OneBusAway MCP server — real-time transit data for Puget Sound (King County Metro, Sound Transit, Pierce Transit, Community Transit, and more) and other OneBusAway instances.\n' +
45
+ '- Stop IDs use agency-prefixed format: {agencyId}_{localId} (e.g. "1_75403" for Metro Transit stop 75403)\n' +
46
+ '- To get arrivals: use onebusaway_find_stops or onebusaway_search_stops to resolve a location/name to a stopId, then call onebusaway_get_arrivals\n' +
47
+ '- For "where is the 44?": use onebusaway_search_routes to get routeId, then onebusaway_get_vehicles\n' +
48
+ '- OneBusAway does not include trip planning — direct users to Google Maps or Transit app for routing',
49
+ setup() {
50
+ initOneBusAwayService(getServerConfig());
51
+ },
52
+ });
53
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,YAAY;AACZ,OAAO,EAAE,aAAa,EAAE,MAAM,sDAAsD,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,MAAM,qDAAqD,CAAC;AACnF,QAAQ;AACR,OAAO,EAAE,UAAU,EAAE,MAAM,oDAAoD,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,mDAAmD,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,qDAAqD,CAAC;AAClF,OAAO,EAAE,QAAQ,EAAE,MAAM,kDAAkD,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,+DAA+D,CAAC;AACpG,OAAO,EAAE,kBAAkB,EAAE,MAAM,8DAA8D,CAAC;AAClG,OAAO,EAAE,OAAO,EAAE,MAAM,iDAAiD,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,iDAAiD,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,qDAAqD,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,sDAAsD,CAAC;AACpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,+DAA+D,CAAC;AACpG,OAAO,EAAE,YAAY,EAAE,MAAM,sDAAsD,CAAC;AACpF,OAAO,EAAE,WAAW,EAAE,MAAM,qDAAqD,CAAC;AAClF,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AAEpF,MAAM,SAAS,CAAC;IACd,KAAK,EAAE;QACL,YAAY;QACZ,SAAS;QACT,UAAU;QACV,WAAW;QACX,YAAY;QACZ,OAAO;QACP,QAAQ;QACR,mBAAmB;QACnB,WAAW;QACX,OAAO;QACP,WAAW;QACX,kBAAkB;QAClB,mBAAmB;KACpB;IACD,SAAS,EAAE,CAAC,YAAY,EAAE,aAAa,CAAC;IACxC,OAAO,EAAE,EAAE;IACX,YAAY,EACV,kLAAkL;QAClL,6GAA6G;QAC7G,qJAAqJ;QACrJ,uGAAuG;QACvG,sGAAsG;IACxG,KAAK;QACH,qBAAqB,CAAC,eAAe,EAAE,CAAC,CAAC;IAC3C,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @fileoverview OneBusAway route metadata resource.
3
+ * @module mcp-server/resources/definitions/route.resource
4
+ */
5
+ import { z } from '@cyanheads/mcp-ts-core';
6
+ export declare const routeResource: import("@cyanheads/mcp-ts-core").ResourceDefinition<z.ZodObject<{
7
+ routeId: z.ZodString;
8
+ }, z.core.$strip>, undefined, undefined>;
9
+ //# sourceMappingURL=route.resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/route.resource.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAGrD,eAAO,MAAM,aAAa;;wCA4BxB,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @fileoverview OneBusAway route metadata resource.
3
+ * @module mcp-server/resources/definitions/route.resource
4
+ */
5
+ import { resource, z } from '@cyanheads/mcp-ts-core';
6
+ import { getOneBusAwayService } from '../../../services/onebusaway/onebusaway-service.js';
7
+ export const routeResource = resource('onebusaway://route/{routeId}', {
8
+ name: 'onebusaway-route',
9
+ title: 'OneBusAway Route',
10
+ description: 'Route metadata — short name, description, agency, schedule URL. Route IDs use agency-prefixed format: {agencyId}_{localId} (e.g. "1_100259").',
11
+ mimeType: 'application/json',
12
+ params: z.object({
13
+ routeId: z
14
+ .string()
15
+ .describe('Agency-prefixed route ID (e.g. "1_100259" for Metro Transit route 100259).'),
16
+ }),
17
+ async handler(params, ctx) {
18
+ ctx.log.debug('routeResource handler', { routeId: params.routeId });
19
+ const route = await getOneBusAwayService().getRoute(params.routeId, ctx);
20
+ return route;
21
+ },
22
+ list: async () => ({
23
+ resources: [
24
+ {
25
+ uri: 'onebusaway://route/1_100259',
26
+ name: 'Example: Metro Transit Route 100259',
27
+ mimeType: 'application/json',
28
+ description: 'Route metadata for Metro Transit route 100259 (Puget Sound instance).',
29
+ },
30
+ ],
31
+ }),
32
+ });
33
+ //# sourceMappingURL=route.resource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/route.resource.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6CAA6C,CAAC;AAEnF,MAAM,CAAC,MAAM,aAAa,GAAG,QAAQ,CAAC,8BAA8B,EAAE;IACpE,IAAI,EAAE,kBAAkB;IACxB,KAAK,EAAE,kBAAkB;IACzB,WAAW,EACT,+IAA+I;IACjJ,QAAQ,EAAE,kBAAkB;IAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,OAAO,EAAE,CAAC;aACP,MAAM,EAAE;aACR,QAAQ,CAAC,4EAA4E,CAAC;KAC1F,CAAC;IAEF,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG;QACvB,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,uBAAuB,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,MAAM,oBAAoB,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACzE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QACjB,SAAS,EAAE;YACT;gBACE,GAAG,EAAE,6BAA6B;gBAClC,IAAI,EAAE,qCAAqC;gBAC3C,QAAQ,EAAE,kBAAkB;gBAC5B,WAAW,EAAE,uEAAuE;aACrF;SACF;KACF,CAAC;CACH,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @fileoverview OneBusAway stop metadata resource.
3
+ * @module mcp-server/resources/definitions/stop.resource
4
+ */
5
+ import { z } from '@cyanheads/mcp-ts-core';
6
+ export declare const stopResource: import("@cyanheads/mcp-ts-core").ResourceDefinition<z.ZodObject<{
7
+ stopId: z.ZodString;
8
+ }, z.core.$strip>, undefined, undefined>;
9
+ //# sourceMappingURL=stop.resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/stop.resource.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAGrD,eAAO,MAAM,YAAY;;wCA4BvB,CAAC"}