@cyanheads/wsdot-mcp-server 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +39 -28
- package/CLAUDE.md +39 -28
- package/Dockerfile +1 -1
- package/LICENSE +1 -1
- package/README.md +5 -3
- package/changelog/0.1.x/0.1.14.md +29 -0
- package/changelog/0.1.x/0.1.15.md +18 -0
- package/dist/mcp-server/tools/definitions/get-border-waits.tool.d.ts +6 -0
- package/dist/mcp-server/tools/definitions/get-border-waits.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-border-waits.tool.js +7 -0
- package/dist/mcp-server/tools/definitions/get-border-waits.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-ferry-alerts.tool.d.ts +6 -0
- package/dist/mcp-server/tools/definitions/get-ferry-alerts.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-ferry-alerts.tool.js +7 -0
- package/dist/mcp-server/tools/definitions/get-ferry-alerts.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-ferry-routes.tool.d.ts +6 -0
- package/dist/mcp-server/tools/definitions/get-ferry-routes.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-ferry-routes.tool.js +7 -0
- package/dist/mcp-server/tools/definitions/get-ferry-routes.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-ferry-schedule.tool.d.ts +6 -0
- package/dist/mcp-server/tools/definitions/get-ferry-schedule.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-ferry-schedule.tool.js +21 -10
- package/dist/mcp-server/tools/definitions/get-ferry-schedule.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-ferry-terminals.tool.d.ts +6 -0
- package/dist/mcp-server/tools/definitions/get-ferry-terminals.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-ferry-terminals.tool.js +7 -0
- package/dist/mcp-server/tools/definitions/get-ferry-terminals.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-mountain-passes.tool.d.ts +6 -0
- package/dist/mcp-server/tools/definitions/get-mountain-passes.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-mountain-passes.tool.js +7 -0
- package/dist/mcp-server/tools/definitions/get-mountain-passes.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-terminal-space.tool.d.ts +6 -0
- package/dist/mcp-server/tools/definitions/get-terminal-space.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-terminal-space.tool.js +7 -0
- package/dist/mcp-server/tools/definitions/get-terminal-space.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-toll-rates.tool.d.ts +6 -0
- package/dist/mcp-server/tools/definitions/get-toll-rates.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-toll-rates.tool.js +7 -0
- package/dist/mcp-server/tools/definitions/get-toll-rates.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-travel-times.tool.d.ts +6 -0
- package/dist/mcp-server/tools/definitions/get-travel-times.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-travel-times.tool.js +28 -9
- package/dist/mcp-server/tools/definitions/get-travel-times.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-vessel-locations.tool.d.ts +6 -0
- package/dist/mcp-server/tools/definitions/get-vessel-locations.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-vessel-locations.tool.js +7 -0
- package/dist/mcp-server/tools/definitions/get-vessel-locations.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/search-alerts.tool.d.ts +6 -0
- package/dist/mcp-server/tools/definitions/search-alerts.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/search-alerts.tool.js +18 -4
- package/dist/mcp-server/tools/definitions/search-alerts.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/search-cameras.tool.d.ts +6 -0
- package/dist/mcp-server/tools/definitions/search-cameras.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/search-cameras.tool.js +8 -1
- package/dist/mcp-server/tools/definitions/search-cameras.tool.js.map +1 -1
- package/dist/services/ferry/ferry-service.d.ts.map +1 -1
- package/dist/services/ferry/ferry-service.js +11 -26
- package/dist/services/ferry/ferry-service.js.map +1 -1
- package/dist/services/traffic/route-match.d.ts +18 -0
- package/dist/services/traffic/route-match.d.ts.map +1 -0
- package/dist/services/traffic/route-match.js +51 -0
- package/dist/services/traffic/route-match.js.map +1 -0
- package/dist/services/traffic/traffic-service.d.ts +0 -11
- package/dist/services/traffic/traffic-service.d.ts.map +1 -1
- package/dist/services/traffic/traffic-service.js +61 -84
- package/dist/services/traffic/traffic-service.js.map +1 -1
- package/dist/services/traffic/types.d.ts +8 -1
- package/dist/services/traffic/types.d.ts.map +1 -1
- package/dist/services/wsdot-http.d.ts +47 -0
- package/dist/services/wsdot-http.d.ts.map +1 -0
- package/dist/services/wsdot-http.js +145 -0
- package/dist/services/wsdot-http.js.map +1 -0
- package/package.json +8 -8
- package/server.json +3 -3
package/AGENTS.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** @cyanheads/wsdot-mcp-server
|
|
4
|
-
**Version:** 0.1.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.
|
|
4
|
+
**Version:** 0.1.15
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.11.0`
|
|
6
6
|
**Engines:** Bun ≥1.3.0, Node ≥24.0.0
|
|
7
7
|
**MCP SDK:** `@modelcontextprotocol/sdk` ^1.29.0
|
|
8
8
|
**Zod:** ^4.4.3
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
- **Logic throws, framework catches.** Tool/resource handlers are pure — throw on failure, no `try/catch`. Plain `Error` is fine; the framework catches, classifies, and formats. Use error factories (`notFound()`, `validationError()`, etc.) when the error code matters.
|
|
17
17
|
- **Use `ctx.log`** for request-scoped logging. No `console` calls.
|
|
18
18
|
- **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
|
|
19
|
-
- **Check `ctx.elicit
|
|
19
|
+
- **Check `ctx.elicit`** for presence before calling.
|
|
20
20
|
- **Secrets in env vars only** — never hardcoded.
|
|
21
21
|
- **Close the loop on issues.** When implementing work tracked by a GitHub issue, comment on the issue with what landed and close it. Do both — a comment without a close leaves stale issues open; a close without a comment leaves no record of what shipped. The comment is for future readers — state the concrete changes, not the conversation that produced them.
|
|
22
22
|
|
|
@@ -81,6 +81,8 @@ export function getServerConfig() {
|
|
|
81
81
|
|
|
82
82
|
`parseEnvConfig` maps Zod schema paths → env var names so errors name the variable (`WSDOT_ACCESS_CODE`) not the path (`accessCode`). Throws `ConfigurationError`, which the framework prints as a clean startup banner.
|
|
83
83
|
|
|
84
|
+
For env booleans use `z.stringbool()`, never `z.coerce.boolean()` — `Boolean("false")` is `true`, so a coerced flag can't be disabled through the environment. `z.stringbool()` parses `true/false/1/0/yes/no/on/off` and rejects anything else, so `=false` actually disables.
|
|
85
|
+
|
|
84
86
|
---
|
|
85
87
|
|
|
86
88
|
## Context
|
|
@@ -90,13 +92,14 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
90
92
|
| Property | Description |
|
|
91
93
|
|:---------|:------------|
|
|
92
94
|
| `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. |
|
|
93
|
-
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
94
|
-
| `ctx.elicit` | Ask user for structured input. **Check for presence first:** `if (ctx.elicit) { ... }` |
|
|
95
|
-
| `ctx.
|
|
95
|
+
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
96
|
+
| `ctx.elicit` | Ask user for structured input — form call `(message, schema)` or `.url(message, url)` for an external link. **Check for presence first:** `if (ctx.elicit) { ... }` |
|
|
97
|
+
| `ctx.enrich` | Success-path agent context (empty-result notices, query echo, pagination totals) — `ctx.enrich(...)` or `.notice()` / `.total()` / `.echo()` / `.truncated()`. Reaches `structuredContent` and `content[]`; lands only when the definition declares an `enrichment` block (no-op otherwise). |
|
|
98
|
+
| `ctx.content` | Non-text content blocks — `.image(data, mimeType)`, `.audio(data, mimeType)`, or `ctx.content(block)` for a raw block. Prepended to `content[]` after `format()`; never enters `structuredContent`. |
|
|
96
99
|
| `ctx.signal` | `AbortSignal` for cancellation. |
|
|
97
100
|
| `ctx.progress` | Task progress (present when `task: true`) — `.setTotal(n)`, `.increment()`, `.update(message)`. |
|
|
98
101
|
| `ctx.requestId` | Unique request ID. |
|
|
99
|
-
| `ctx.tenantId` | Tenant ID from JWT
|
|
102
|
+
| `ctx.tenantId` | Tenant ID from JWT; `'default'` for stdio or HTTP with auth off. |
|
|
100
103
|
|
|
101
104
|
---
|
|
102
105
|
|
|
@@ -104,7 +107,7 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
104
107
|
|
|
105
108
|
Handlers throw — the framework catches, classifies, and formats.
|
|
106
109
|
|
|
107
|
-
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required
|
|
110
|
+
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required (≥ 5 words, lint-validated) — the single source of truth for the agent's next move. Pass `ctx.recoveryFor('reason')` as the throw's data to put it on the wire (`data.recovery.hint`, mirrored into `content[]` text); override with an explicit `{ recovery: { hint: '...' } }` when dynamic runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring.
|
|
108
111
|
|
|
109
112
|
```ts
|
|
110
113
|
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
@@ -116,7 +119,7 @@ errors: [
|
|
|
116
119
|
],
|
|
117
120
|
async handler(input, ctx) {
|
|
118
121
|
const item = await db.find(input.id);
|
|
119
|
-
if (!item) throw ctx.fail('no_match', `No item ${input.id}
|
|
122
|
+
if (!item) throw ctx.fail('no_match', `No item ${input.id}`, ctx.recoveryFor('no_match'));
|
|
120
123
|
return item;
|
|
121
124
|
}
|
|
122
125
|
```
|
|
@@ -189,7 +192,7 @@ src/
|
|
|
189
192
|
|
|
190
193
|
## Skills
|
|
191
194
|
|
|
192
|
-
Skills are modular instructions in `skills/` at the project root. Read them directly when a task matches — e.g., `skills/add-tool/SKILL.md` when adding a tool.
|
|
195
|
+
Skills are modular instructions in `skills/` at the project root. Read them directly when a task matches — e.g., `skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry.
|
|
193
196
|
|
|
194
197
|
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
195
198
|
|
|
@@ -209,54 +212,62 @@ Available skills:
|
|
|
209
212
|
| `tool-defs-analysis` | Read-only audit of MCP definition language across the surface — voice, leaks, defaults, recovery hints, output descriptions |
|
|
210
213
|
| `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
|
|
211
214
|
| `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
|
|
212
|
-
| `devcheck` | Lint, format, typecheck, audit |
|
|
213
215
|
| `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
214
216
|
| `git-wrapup` | Land working-tree changes as a versioned commit + annotated tag — version bump, changelog, verify, tag. Local only. |
|
|
215
217
|
| `release-and-publish` | Push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
216
218
|
| `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
|
|
219
|
+
| `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
|
|
217
220
|
| `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
|
|
218
221
|
| `report-issue-local` | File a bug or feature request against this server's own repo via `gh` CLI |
|
|
222
|
+
| `techniques` | Catalog of response/data-shaping techniques — overflow handling, payload shaping, retrieval patterns |
|
|
219
223
|
| `api-auth` | Auth modes, scopes, JWT/OAuth |
|
|
220
|
-
| `api-linter` | Definition linter rule catalog — invoked by `bun run lint:mcp` and `devcheck` |
|
|
221
224
|
| `api-canvas` | DataCanvas: register tabular data, run SQL, export, plus the `spillover()` helper for big result sets — Tier 3 opt-in |
|
|
222
225
|
| `api-config` | AppConfig, parseConfig, env vars |
|
|
223
226
|
| `api-context` | Context interface, logger, state, progress |
|
|
224
227
|
| `api-errors` | McpError, JsonRpcErrorCode, error patterns |
|
|
228
|
+
| `api-linter` | Definition linter rule catalog — invoked by `bun run lint:mcp` and `devcheck` |
|
|
229
|
+
| `api-mirror` | MirrorService: persistent self-refreshing local mirror (embedded SQLite + FTS5) of a bulk upstream dataset — Tier 3 opt-in |
|
|
225
230
|
| `api-services` | LLM, Speech, Graph services |
|
|
226
231
|
| `api-testing` | createMockContext, test patterns |
|
|
227
232
|
| `api-utils` | Formatting, parsing, security, pagination, scheduling, telemetry helpers |
|
|
228
233
|
| `api-telemetry` | OTel catalog: spans, metrics, completion logs, env config, cardinality rules |
|
|
229
234
|
| `api-workers` | Cloudflare Workers runtime |
|
|
230
235
|
|
|
236
|
+
**Chaining skills into pipelines.** When the user wants a multi-phase effort — build this server out, QA-and-fix the surface, update-and-ship — *and you can spawn sub-agents*, `skills/orchestrations/SKILL.md` sequences the task skills above into a gated pipeline with verification at each step. Read it to drive the run. Optional: skip it if you can't orchestrate sub-agents, and ignore it entirely if you were *spawned* as one — you've already been scoped to a single phase.
|
|
237
|
+
|
|
231
238
|
When you complete a skill's checklist, check the boxes and add a completion timestamp at the end (e.g., `Completed: 2026-03-11`).
|
|
232
239
|
|
|
233
240
|
---
|
|
234
241
|
|
|
235
242
|
## Commands
|
|
236
243
|
|
|
237
|
-
**Runtime:** Scripts use
|
|
244
|
+
**Runtime:** Scripts use Bun's native TypeScript execution — `bun run <cmd>` is the standard invocation. `npm run <cmd>` also works (npm delegates to bun).
|
|
238
245
|
|
|
239
246
|
| Command | Purpose |
|
|
240
247
|
|:--------|:--------|
|
|
241
|
-
| `
|
|
242
|
-
| `
|
|
243
|
-
| `
|
|
244
|
-
| `
|
|
248
|
+
| `bun run build` | Compile TypeScript |
|
|
249
|
+
| `bun run rebuild` | Clean + build |
|
|
250
|
+
| `bun run clean` | Remove build artifacts |
|
|
251
|
+
| `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
|
|
245
252
|
| `bun run audit:refresh` | Delete `bun.lock`, reinstall, and re-run `bun audit`. Use when `devcheck` flags a transitive advisory — Bun's `update` is sticky on transitive resolutions, so the advisory may be a stale-lockfile false positive. If it survives the refresh, it's real. |
|
|
246
|
-
| `
|
|
247
|
-
| `
|
|
248
|
-
| `
|
|
249
|
-
| `
|
|
250
|
-
| `
|
|
251
|
-
| `
|
|
252
|
-
| `
|
|
253
|
-
| `
|
|
253
|
+
| `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
|
|
254
|
+
| `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity (run by devcheck) |
|
|
255
|
+
| `bun run list-skills` | Print the skill registry |
|
|
256
|
+
| `bun run tree` | Generate directory structure doc |
|
|
257
|
+
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
258
|
+
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
|
|
259
|
+
| `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
|
|
260
|
+
| `bun run start:stdio` | Production mode (stdio) |
|
|
261
|
+
| `bun run start:http` | Production mode (HTTP) |
|
|
262
|
+
| `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
|
|
263
|
+
| `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
|
|
264
|
+
| `bun run bundle` | Build, pack, and clean a `.mcpb` for one-click Claude Desktop install |
|
|
254
265
|
|
|
255
266
|
---
|
|
256
267
|
|
|
257
268
|
## Bundling
|
|
258
269
|
|
|
259
|
-
`
|
|
270
|
+
`bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. The pack step is followed by `scripts/clean-mcpb.ts`, which prunes dev dependencies (`mcpb clean`) and strips two classes of `node_modules/**` content that root-anchored `.mcpbignore` patterns cannot reach: dependency-shipped agent docs (`skills/`, `.claude/`, `.agents/`, `SKILL.md`) and platform-specific native bindings, which would otherwise lock the bundle to the platform it was packed on. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
|
|
260
271
|
|
|
261
272
|
**Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match.
|
|
262
273
|
|
|
@@ -274,14 +285,14 @@ Each per-version file opens with YAML frontmatter:
|
|
|
274
285
|
---
|
|
275
286
|
summary: "One-line headline, ≤350 chars" # required — powers the rollup index
|
|
276
287
|
breaking: false # optional — true flags breaking changes
|
|
277
|
-
security: false # optional — true
|
|
288
|
+
security: false # optional — true ONLY for a source-code security fix, never a dependency CVE bump
|
|
278
289
|
---
|
|
279
290
|
|
|
280
291
|
# 0.1.0 — YYYY-MM-DD
|
|
281
292
|
...
|
|
282
293
|
```
|
|
283
294
|
|
|
284
|
-
`breaking: true` renders a `· ⚠️ Breaking` badge — use it when consumers must update code on upgrade (signature changes, removed APIs, config renames). `security: true` renders a `· 🛡️ Security` badge and pairs with a `## Security` body section. When both are set, badges render `· ⚠️ Breaking · 🛡️ Security`.
|
|
295
|
+
`breaking: true` renders a `· ⚠️ Breaking` badge — use it when consumers must update code on upgrade (signature changes, removed APIs, config renames). `security: true` renders a `· 🛡️ Security` badge and pairs with a `## Security` body section — set it only for a security fix in this server's *own source code*, never for a routine dependency or transitive CVE bump (record those under `## Dependencies`). When both are set, badges render `· ⚠️ Breaking · 🛡️ Security`.
|
|
285
296
|
|
|
286
297
|
`agent-notes` is an optional free-form field for maintenance agents processing the release downstream. Content here won't appear in the rendered CHANGELOG — it's consumed by agents running the `maintenance` skill. Use it for adoption instructions that don't fit the human-facing sections: new files to create, fields to populate, one-time migration steps. Omit entirely when there's nothing to say.
|
|
287
298
|
|
package/CLAUDE.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Developer Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** @cyanheads/wsdot-mcp-server
|
|
4
|
-
**Version:** 0.1.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.
|
|
4
|
+
**Version:** 0.1.15
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.11.0`
|
|
6
6
|
**Engines:** Bun ≥1.3.0, Node ≥24.0.0
|
|
7
7
|
**MCP SDK:** `@modelcontextprotocol/sdk` ^1.29.0
|
|
8
8
|
**Zod:** ^4.4.3
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
- **Logic throws, framework catches.** Tool/resource handlers are pure — throw on failure, no `try/catch`. Plain `Error` is fine; the framework catches, classifies, and formats. Use error factories (`notFound()`, `validationError()`, etc.) when the error code matters.
|
|
17
17
|
- **Use `ctx.log`** for request-scoped logging. No `console` calls.
|
|
18
18
|
- **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
|
|
19
|
-
- **Check `ctx.elicit
|
|
19
|
+
- **Check `ctx.elicit`** for presence before calling.
|
|
20
20
|
- **Secrets in env vars only** — never hardcoded.
|
|
21
21
|
- **Close the loop on issues.** When implementing work tracked by a GitHub issue, comment on the issue with what landed and close it. Do both — a comment without a close leaves stale issues open; a close without a comment leaves no record of what shipped. The comment is for future readers — state the concrete changes, not the conversation that produced them.
|
|
22
22
|
|
|
@@ -81,6 +81,8 @@ export function getServerConfig() {
|
|
|
81
81
|
|
|
82
82
|
`parseEnvConfig` maps Zod schema paths → env var names so errors name the variable (`WSDOT_ACCESS_CODE`) not the path (`accessCode`). Throws `ConfigurationError`, which the framework prints as a clean startup banner.
|
|
83
83
|
|
|
84
|
+
For env booleans use `z.stringbool()`, never `z.coerce.boolean()` — `Boolean("false")` is `true`, so a coerced flag can't be disabled through the environment. `z.stringbool()` parses `true/false/1/0/yes/no/on/off` and rejects anything else, so `=false` actually disables.
|
|
85
|
+
|
|
84
86
|
---
|
|
85
87
|
|
|
86
88
|
## Context
|
|
@@ -90,13 +92,14 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
90
92
|
| Property | Description |
|
|
91
93
|
|:---------|:------------|
|
|
92
94
|
| `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. |
|
|
93
|
-
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
94
|
-
| `ctx.elicit` | Ask user for structured input. **Check for presence first:** `if (ctx.elicit) { ... }` |
|
|
95
|
-
| `ctx.
|
|
95
|
+
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
96
|
+
| `ctx.elicit` | Ask user for structured input — form call `(message, schema)` or `.url(message, url)` for an external link. **Check for presence first:** `if (ctx.elicit) { ... }` |
|
|
97
|
+
| `ctx.enrich` | Success-path agent context (empty-result notices, query echo, pagination totals) — `ctx.enrich(...)` or `.notice()` / `.total()` / `.echo()` / `.truncated()`. Reaches `structuredContent` and `content[]`; lands only when the definition declares an `enrichment` block (no-op otherwise). |
|
|
98
|
+
| `ctx.content` | Non-text content blocks — `.image(data, mimeType)`, `.audio(data, mimeType)`, or `ctx.content(block)` for a raw block. Prepended to `content[]` after `format()`; never enters `structuredContent`. |
|
|
96
99
|
| `ctx.signal` | `AbortSignal` for cancellation. |
|
|
97
100
|
| `ctx.progress` | Task progress (present when `task: true`) — `.setTotal(n)`, `.increment()`, `.update(message)`. |
|
|
98
101
|
| `ctx.requestId` | Unique request ID. |
|
|
99
|
-
| `ctx.tenantId` | Tenant ID from JWT
|
|
102
|
+
| `ctx.tenantId` | Tenant ID from JWT; `'default'` for stdio or HTTP with auth off. |
|
|
100
103
|
|
|
101
104
|
---
|
|
102
105
|
|
|
@@ -104,7 +107,7 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
104
107
|
|
|
105
108
|
Handlers throw — the framework catches, classifies, and formats.
|
|
106
109
|
|
|
107
|
-
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required
|
|
110
|
+
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required (≥ 5 words, lint-validated) — the single source of truth for the agent's next move. Pass `ctx.recoveryFor('reason')` as the throw's data to put it on the wire (`data.recovery.hint`, mirrored into `content[]` text); override with an explicit `{ recovery: { hint: '...' } }` when dynamic runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring.
|
|
108
111
|
|
|
109
112
|
```ts
|
|
110
113
|
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
@@ -116,7 +119,7 @@ errors: [
|
|
|
116
119
|
],
|
|
117
120
|
async handler(input, ctx) {
|
|
118
121
|
const item = await db.find(input.id);
|
|
119
|
-
if (!item) throw ctx.fail('no_match', `No item ${input.id}
|
|
122
|
+
if (!item) throw ctx.fail('no_match', `No item ${input.id}`, ctx.recoveryFor('no_match'));
|
|
120
123
|
return item;
|
|
121
124
|
}
|
|
122
125
|
```
|
|
@@ -189,7 +192,7 @@ src/
|
|
|
189
192
|
|
|
190
193
|
## Skills
|
|
191
194
|
|
|
192
|
-
Skills are modular instructions in `skills/` at the project root. Read them directly when a task matches — e.g., `skills/add-tool/SKILL.md` when adding a tool.
|
|
195
|
+
Skills are modular instructions in `skills/` at the project root. Read them directly when a task matches — e.g., `skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry.
|
|
193
196
|
|
|
194
197
|
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
195
198
|
|
|
@@ -209,54 +212,62 @@ Available skills:
|
|
|
209
212
|
| `tool-defs-analysis` | Read-only audit of MCP definition language across the surface — voice, leaks, defaults, recovery hints, output descriptions |
|
|
210
213
|
| `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
|
|
211
214
|
| `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
|
|
212
|
-
| `devcheck` | Lint, format, typecheck, audit |
|
|
213
215
|
| `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
214
216
|
| `git-wrapup` | Land working-tree changes as a versioned commit + annotated tag — version bump, changelog, verify, tag. Local only. |
|
|
215
217
|
| `release-and-publish` | Push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
216
218
|
| `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
|
|
219
|
+
| `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
|
|
217
220
|
| `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
|
|
218
221
|
| `report-issue-local` | File a bug or feature request against this server's own repo via `gh` CLI |
|
|
222
|
+
| `techniques` | Catalog of response/data-shaping techniques — overflow handling, payload shaping, retrieval patterns |
|
|
219
223
|
| `api-auth` | Auth modes, scopes, JWT/OAuth |
|
|
220
|
-
| `api-linter` | Definition linter rule catalog — invoked by `bun run lint:mcp` and `devcheck` |
|
|
221
224
|
| `api-canvas` | DataCanvas: register tabular data, run SQL, export, plus the `spillover()` helper for big result sets — Tier 3 opt-in |
|
|
222
225
|
| `api-config` | AppConfig, parseConfig, env vars |
|
|
223
226
|
| `api-context` | Context interface, logger, state, progress |
|
|
224
227
|
| `api-errors` | McpError, JsonRpcErrorCode, error patterns |
|
|
228
|
+
| `api-linter` | Definition linter rule catalog — invoked by `bun run lint:mcp` and `devcheck` |
|
|
229
|
+
| `api-mirror` | MirrorService: persistent self-refreshing local mirror (embedded SQLite + FTS5) of a bulk upstream dataset — Tier 3 opt-in |
|
|
225
230
|
| `api-services` | LLM, Speech, Graph services |
|
|
226
231
|
| `api-testing` | createMockContext, test patterns |
|
|
227
232
|
| `api-utils` | Formatting, parsing, security, pagination, scheduling, telemetry helpers |
|
|
228
233
|
| `api-telemetry` | OTel catalog: spans, metrics, completion logs, env config, cardinality rules |
|
|
229
234
|
| `api-workers` | Cloudflare Workers runtime |
|
|
230
235
|
|
|
236
|
+
**Chaining skills into pipelines.** When the user wants a multi-phase effort — build this server out, QA-and-fix the surface, update-and-ship — *and you can spawn sub-agents*, `skills/orchestrations/SKILL.md` sequences the task skills above into a gated pipeline with verification at each step. Read it to drive the run. Optional: skip it if you can't orchestrate sub-agents, and ignore it entirely if you were *spawned* as one — you've already been scoped to a single phase.
|
|
237
|
+
|
|
231
238
|
When you complete a skill's checklist, check the boxes and add a completion timestamp at the end (e.g., `Completed: 2026-03-11`).
|
|
232
239
|
|
|
233
240
|
---
|
|
234
241
|
|
|
235
242
|
## Commands
|
|
236
243
|
|
|
237
|
-
**Runtime:** Scripts use
|
|
244
|
+
**Runtime:** Scripts use Bun's native TypeScript execution — `bun run <cmd>` is the standard invocation. `npm run <cmd>` also works (npm delegates to bun).
|
|
238
245
|
|
|
239
246
|
| Command | Purpose |
|
|
240
247
|
|:--------|:--------|
|
|
241
|
-
| `
|
|
242
|
-
| `
|
|
243
|
-
| `
|
|
244
|
-
| `
|
|
248
|
+
| `bun run build` | Compile TypeScript |
|
|
249
|
+
| `bun run rebuild` | Clean + build |
|
|
250
|
+
| `bun run clean` | Remove build artifacts |
|
|
251
|
+
| `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
|
|
245
252
|
| `bun run audit:refresh` | Delete `bun.lock`, reinstall, and re-run `bun audit`. Use when `devcheck` flags a transitive advisory — Bun's `update` is sticky on transitive resolutions, so the advisory may be a stale-lockfile false positive. If it survives the refresh, it's real. |
|
|
246
|
-
| `
|
|
247
|
-
| `
|
|
248
|
-
| `
|
|
249
|
-
| `
|
|
250
|
-
| `
|
|
251
|
-
| `
|
|
252
|
-
| `
|
|
253
|
-
| `
|
|
253
|
+
| `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
|
|
254
|
+
| `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity (run by devcheck) |
|
|
255
|
+
| `bun run list-skills` | Print the skill registry |
|
|
256
|
+
| `bun run tree` | Generate directory structure doc |
|
|
257
|
+
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
258
|
+
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
|
|
259
|
+
| `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
|
|
260
|
+
| `bun run start:stdio` | Production mode (stdio) |
|
|
261
|
+
| `bun run start:http` | Production mode (HTTP) |
|
|
262
|
+
| `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
|
|
263
|
+
| `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
|
|
264
|
+
| `bun run bundle` | Build, pack, and clean a `.mcpb` for one-click Claude Desktop install |
|
|
254
265
|
|
|
255
266
|
---
|
|
256
267
|
|
|
257
268
|
## Bundling
|
|
258
269
|
|
|
259
|
-
`
|
|
270
|
+
`bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. The pack step is followed by `scripts/clean-mcpb.ts`, which prunes dev dependencies (`mcpb clean`) and strips two classes of `node_modules/**` content that root-anchored `.mcpbignore` patterns cannot reach: dependency-shipped agent docs (`skills/`, `.claude/`, `.agents/`, `SKILL.md`) and platform-specific native bindings, which would otherwise lock the bundle to the platform it was packed on. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
|
|
260
271
|
|
|
261
272
|
**Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match.
|
|
262
273
|
|
|
@@ -274,14 +285,14 @@ Each per-version file opens with YAML frontmatter:
|
|
|
274
285
|
---
|
|
275
286
|
summary: "One-line headline, ≤350 chars" # required — powers the rollup index
|
|
276
287
|
breaking: false # optional — true flags breaking changes
|
|
277
|
-
security: false # optional — true
|
|
288
|
+
security: false # optional — true ONLY for a source-code security fix, never a dependency CVE bump
|
|
278
289
|
---
|
|
279
290
|
|
|
280
291
|
# 0.1.0 — YYYY-MM-DD
|
|
281
292
|
...
|
|
282
293
|
```
|
|
283
294
|
|
|
284
|
-
`breaking: true` renders a `· ⚠️ Breaking` badge — use it when consumers must update code on upgrade (signature changes, removed APIs, config renames). `security: true` renders a `· 🛡️ Security` badge and pairs with a `## Security` body section. When both are set, badges render `· ⚠️ Breaking · 🛡️ Security`.
|
|
295
|
+
`breaking: true` renders a `· ⚠️ Breaking` badge — use it when consumers must update code on upgrade (signature changes, removed APIs, config renames). `security: true` renders a `· 🛡️ Security` badge and pairs with a `## Security` body section — set it only for a security fix in this server's *own source code*, never for a routine dependency or transitive CVE bump (record those under `## Dependencies`). When both are set, badges render `· ⚠️ Breaking · 🛡️ Security`.
|
|
285
296
|
|
|
286
297
|
`agent-notes` is an optional free-form field for maintenance agents processing the release downstream. Content here won't appear in the rendered CHANGELOG — it's consumed by agents running the `maintenance` skill. Use it for adoption instructions that don't fit the human-facing sections: new files to create, fields to populate, one-time migration steps. Omit entirely when there's nothing to say.
|
|
287
298
|
|
package/Dockerfile
CHANGED
|
@@ -58,7 +58,7 @@ RUN bun install --production --frozen-lockfile --ignore-scripts
|
|
|
58
58
|
# with: docker build --build-arg OTEL_ENABLED=true
|
|
59
59
|
ARG OTEL_ENABLED=true
|
|
60
60
|
RUN if [ "$OTEL_ENABLED" = "true" ]; then \
|
|
61
|
-
bun add @hono/otel \
|
|
61
|
+
bun add --omit=dev --ignore-scripts @hono/otel \
|
|
62
62
|
@opentelemetry/instrumentation-http \
|
|
63
63
|
@opentelemetry/exporter-metrics-otlp-http \
|
|
64
64
|
@opentelemetry/exporter-trace-otlp-http \
|
package/LICENSE
CHANGED
|
@@ -186,7 +186,7 @@ Apache License
|
|
|
186
186
|
same "printed page" as the copyright notice for easier
|
|
187
187
|
identification within third-party archives.
|
|
188
188
|
|
|
189
|
-
Copyright
|
|
189
|
+
Copyright 2026 Casey Hand @cyanheads
|
|
190
190
|
|
|
191
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
192
|
you may not use this file except in compliance with the License.
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
<div align="center">
|
|
9
9
|
|
|
10
|
-
[](./CHANGELOG.md) [](./LICENSE) [](https://github.com/users/cyanheads/packages/container/package/wsdot-mcp-server) [](https://modelcontextprotocol.io/) [](https://www.npmjs.com/package/@cyanheads/wsdot-mcp-server) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
11
11
|
|
|
12
12
|
</div>
|
|
13
13
|
|
|
@@ -58,7 +58,7 @@ Active WA highway alerts — incidents, construction, closures, restrictions.
|
|
|
58
58
|
|
|
59
59
|
- Filter by state route — natural forms all work: `"I-90"`, `"90"`, `"090"`, or `"SR 520"` / `"520"`
|
|
60
60
|
- Filter by WSDOT region: Northwest, Olympic, Southwest, South Central, North Central, Eastern
|
|
61
|
-
- Filter by milepost range to scope to a corridor
|
|
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
62
|
- Omit all filters to return all current statewide alerts
|
|
63
63
|
|
|
64
64
|
---
|
|
@@ -68,8 +68,9 @@ Active WA highway alerts — incidents, construction, closures, restrictions.
|
|
|
68
68
|
Current vs. average travel times for named WA highway corridors.
|
|
69
69
|
|
|
70
70
|
- Covers I-5, I-90, SR 520, SR 99, I-405, SR 167, and others
|
|
71
|
-
- Filter by
|
|
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"`)
|
|
72
72
|
- When current time exceeds average, the corridor is congested; the delta is the delay
|
|
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
|
|
73
74
|
|
|
74
75
|
---
|
|
75
76
|
|
|
@@ -97,6 +98,7 @@ Current vehicle wait times at WA/Canada land border crossings.
|
|
|
97
98
|
WSDOT highway camera metadata and image URLs.
|
|
98
99
|
|
|
99
100
|
- Filter by state route (`"I-90"`, `"90"`, `"SR 520"`, or `"520"` all work), WSDOT region, or milepost range
|
|
101
|
+
- 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
|
|
100
102
|
- Returns metadata and image URLs — camera images are copyright WSDOT, not fetched as bytes
|
|
101
103
|
- Results are paged (default 50, max 500) — pass `offset`/`limit` to page through the full statewide set; the notice reports the next offset
|
|
102
104
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "WSDOT access code no longer leaks into error payloads (#27); a bad access code now surfaces as its own non-retryable `invalid_access_code` reason instead of a generic `api_unavailable` or bare HTTP 400 (#28); mcp-ts-core ^0.11.0 maintenance."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.1.14 — 2026-07-30
|
|
8
|
+
|
|
9
|
+
## Changed
|
|
10
|
+
|
|
11
|
+
- **`invalid_access_code` reason** — all 12 tools declare a second error reason alongside `api_unavailable`: `ConfigurationError`, non-retryable, pointing at `https://wsdot.wa.gov/traffic/api/` to register a code. A 401/403, a 4xx whose body names the access code, or an HTML response below HTTP 500 (`Content-Type: text/html` or a body opening as an HTML document) now classifies as `invalid_access_code`; a 5xx HTML page stays `api_unavailable` and retryable, since that's an outage page, not a credential fault ([#28](https://github.com/cyanheads/wsdot-mcp-server/issues/28)).
|
|
12
|
+
- **Body read before status check** — both services read the response body before throwing on a non-2xx status, so the branches that explain a bad access code are reachable at HTTP 400 instead of being short-circuited. WSDOT Traffic's `text/html` + `Bad Request` and WSF's `{"Message": "...Access Code..."}` now both reach the caller, quoted in the error message and carried in `data.body` ([#28](https://github.com/cyanheads/wsdot-mcp-server/issues/28)).
|
|
13
|
+
- **`wsdot_get_ferry_schedule` terminal-pair mapping** — now keys off `data.status` instead of a message regex, and excludes `invalid_access_code`, so a credential rejection is no longer misreported as `invalid_terminal_pair` ([#28](https://github.com/cyanheads/wsdot-mcp-server/issues/28)).
|
|
14
|
+
- Shared upstream-HTTP handling for both services extracted to `src/services/wsdot-http.ts` — redaction, network/timeout classification, and the `invalid_access_code` / `api_unavailable` split now live in one place instead of being duplicated across `TrafficApiService` and `FerryApiService`.
|
|
15
|
+
|
|
16
|
+
## Security
|
|
17
|
+
|
|
18
|
+
- **Access code no longer reaches error payloads.** `TrafficApiService.fetchJson` and `FerryApiService.fetchJson` now compute a redacted `endpoint` (query string stripped) for every thrown error's `data.url`, covering the non-2xx, HTML, and WSF `{"Message"}` validation throws. Network, timeout, and abort failures are also caught and re-thrown as classified `McpError`s carrying only the redacted endpoint — Bun and Node otherwise hang the full requested URL off a raw fetch rejection as `error.path`. Upstream error bodies are scrubbed of any `AccessCode=…` / `apiaccesscode=…` assignment (scrub-then-truncate, so a cut can never leave a partial credential) before being carried in `data.body` ([#27](https://github.com/cyanheads/wsdot-mcp-server/issues/27)).
|
|
19
|
+
- `tests/security/security.test.ts`'s `SECRET_PATTERNS` previously conflated the credential's name with its value, flagging any prose naming `WSDOT_ACCESS_CODE`; it now matches the fake code and `access_code=<value>` assignments instead, and gained coverage for the error path (the file's header claimed error messages were covered, but only success-path tool output was).
|
|
20
|
+
|
|
21
|
+
## Dependencies
|
|
22
|
+
|
|
23
|
+
- `@cyanheads/mcp-ts-core` ^0.10.14 → ^0.11.0
|
|
24
|
+
- `typescript` ^6.0.3 → ^7.0.2
|
|
25
|
+
- `@biomejs/biome` ^2.5.0 → ^2.5.5
|
|
26
|
+
- `@types/node` ^26.1.1 → ^26.1.2
|
|
27
|
+
- `ignore` ^7.0.5 → ^7.0.6
|
|
28
|
+
- `tsc-alias` ^1.8.17 → ^1.9.1
|
|
29
|
+
- `vitest` ^4.1.9 → ^4.1.10
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Route filters compare route-type prefix and lettered suffix, so `SR 26` no longer matches US 26 and `US 97` excludes US 97A (#30); travel-times route filtering matches corridor road names, not just names (#26); the CurrentTime=0 sentinel is dropped instead of a zero-minute trip (#29); milepost filtering tests an alert extent for overlap (#35)."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.1.15 — 2026-07-30
|
|
8
|
+
|
|
9
|
+
## Changed
|
|
10
|
+
|
|
11
|
+
- **`routeMatches()` replaces `normalizeRoute()`** (`src/services/traffic/route-match.ts`) — parses a route designation into an optional route-type prefix (`i`, `sr`, `us`, `ore`) and a number that retains any lettered suffix. Two designations match when the numbers are equal and, only when both sides carry a prefix, the prefixes are equal too; a suffix letter is part of the number, so `US 97` and `US 97A` stay distinct. `wsdot_search_cameras` and `wsdot_search_alerts` both switch to it. Live-feed effect: `"SR 26"` drops from 21 results (SR 26 + US 26) to 17; `"US 97"` drops from 16 (US 97 + US 97A) to 15; a bare `"26"` still returns both routes ([#30](https://github.com/cyanheads/wsdot-mcp-server/issues/30)).
|
|
12
|
+
- **`wsdot_get_travel_times` route filter** now matches two ways: a route designation against `startPoint.roadName` / `endPoint.roadName` via `routeMatches()`, and a case-insensitive substring against the corridor name (unchanged). Most corridor names are endpoint pairs ("Seattle-Everett") with no route in them, so the route-only match was silently dropping most corridors. Live feed, 163 corridors: `"I-5"` 24 → 118, `"I-405"` 3 → 54, `"SR 520"` 0 → 12 ([#26](https://github.com/cyanheads/wsdot-mcp-server/issues/26)).
|
|
13
|
+
- **`wsdot_search_alerts` milepost range** now tests overlap against an alert's full extent (`startRoadwayLocation.milePost` and `endRoadwayLocation.milePost`) instead of the start point alone, so a closure spanning MP 10–30 is returned for a query at MP 20. The extent is taken as `Math.min`/`Math.max` of the two mileposts, so a decreasing-direction record whose start is numerically greater than its end still matches; either bound may be given alone, and an alert reporting no milepost is always kept ([#35](https://github.com/cyanheads/wsdot-mcp-server/issues/35)).
|
|
14
|
+
- **Unpopulated `RoadwayLocation` zeros dropped.** WSDOT fills the unset end location of a point alert with `MilePost`, `Latitude`, and `Longitude` all `0` — 77 of 178 alerts on the current feed. `normalizeRoadwayLocation()` (`src/services/traffic/traffic-service.ts`) drops that zero triple while keeping the road name and direction the record does carry, so the milepost overlap fix above doesn't read a point alert as stretching from MP 0.
|
|
15
|
+
|
|
16
|
+
## Fixed
|
|
17
|
+
|
|
18
|
+
- **`CurrentTime`/`AverageTime` of `0`** on a `wsdot_get_travel_times` corridor with a nonzero `Distance` is now dropped during normalization (`isMeasuredDuration()`) instead of surfaced as a measured trip — WSDOT emits `0` for a reversible express lane not open in the queried direction. The guard is keyed on the value, not the corridor name, since the opposite-direction lanes report real times under the same naming (e.g. `Seattle-Everett EL` at 55 min while `Everett-Seattle EL` reports `0`). `delayInMinutes` is derived only when both times are present, so it drops out too; `format()` now renders `**Current:** Not available — WSDOT reports no measurement for this corridor` instead of `0 min`. A `0` on a corridor with no reported distance is kept, since nothing contradicts it ([#29](https://github.com/cyanheads/wsdot-mcp-server/issues/29)).
|
|
@@ -24,6 +24,12 @@ export declare const getBorderWaits: import("@cyanheads/mcp-ts-core").ToolDefini
|
|
|
24
24
|
readonly when: "WSDOT Traffic API is unreachable or returns a non-2xx response after retries.";
|
|
25
25
|
readonly retryable: true;
|
|
26
26
|
readonly recovery: "Retry in 30 seconds. If the issue persists, check wsdot.wa.gov for service status.";
|
|
27
|
+
}, {
|
|
28
|
+
readonly reason: "invalid_access_code";
|
|
29
|
+
readonly code: JsonRpcErrorCode.ConfigurationError;
|
|
30
|
+
readonly when: "WSDOT rejected the request because WSDOT_ACCESS_CODE is missing, invalid, or not registered.";
|
|
31
|
+
readonly retryable: false;
|
|
32
|
+
readonly recovery: "Register an access code at https://wsdot.wa.gov/traffic/api/, set WSDOT_ACCESS_CODE on the server, and restart it.";
|
|
27
33
|
}], {
|
|
28
34
|
readonly totalCount: z.ZodNumber;
|
|
29
35
|
readonly notice: z.ZodOptional<z.ZodString>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-border-waits.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-border-waits.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
|
|
1
|
+
{"version":3,"file":"get-border-waits.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-border-waits.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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8HzB,CAAC"}
|
|
@@ -68,6 +68,13 @@ export const getBorderWaits = tool('wsdot_get_border_waits', {
|
|
|
68
68
|
retryable: true,
|
|
69
69
|
recovery: 'Retry in 30 seconds. If the issue persists, check wsdot.wa.gov for service status.',
|
|
70
70
|
},
|
|
71
|
+
{
|
|
72
|
+
reason: 'invalid_access_code',
|
|
73
|
+
code: JsonRpcErrorCode.ConfigurationError,
|
|
74
|
+
when: 'WSDOT rejected the request because WSDOT_ACCESS_CODE is missing, invalid, or not registered.',
|
|
75
|
+
retryable: false,
|
|
76
|
+
recovery: 'Register an access code at https://wsdot.wa.gov/traffic/api/, set WSDOT_ACCESS_CODE on the server, and restart it.',
|
|
77
|
+
},
|
|
71
78
|
],
|
|
72
79
|
async handler(_input, ctx) {
|
|
73
80
|
const crossings = await getTrafficApiService().getBorderCrossings(ctx);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-border-waits.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-border-waits.tool.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"get-border-waits.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-border-waits.tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,6 +17,12 @@ export declare const getFerryAlerts: import("@cyanheads/mcp-ts-core").ToolDefini
|
|
|
17
17
|
readonly when: "WSF Ferry API is unreachable or returns a non-2xx response after retries.";
|
|
18
18
|
readonly retryable: true;
|
|
19
19
|
readonly recovery: "Retry in 30 seconds. If the issue persists, check wsdot.wa.gov/ferries for service status.";
|
|
20
|
+
}, {
|
|
21
|
+
readonly reason: "invalid_access_code";
|
|
22
|
+
readonly code: JsonRpcErrorCode.ConfigurationError;
|
|
23
|
+
readonly when: "WSF rejected the request because WSDOT_ACCESS_CODE is missing, invalid, or not registered.";
|
|
24
|
+
readonly retryable: false;
|
|
25
|
+
readonly recovery: "Register an access code at https://wsdot.wa.gov/traffic/api/, set WSDOT_ACCESS_CODE on the server, and restart it.";
|
|
20
26
|
}], {
|
|
21
27
|
readonly totalCount: z.ZodNumber;
|
|
22
28
|
readonly notice: z.ZodOptional<z.ZodString>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-ferry-alerts.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-ferry-alerts.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
|
|
1
|
+
{"version":3,"file":"get-ferry-alerts.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-ferry-alerts.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;;;;;;;;;;;;;;;;;;;;;;EA0FzB,CAAC"}
|
|
@@ -46,6 +46,13 @@ export const getFerryAlerts = tool('wsdot_get_ferry_alerts', {
|
|
|
46
46
|
retryable: true,
|
|
47
47
|
recovery: 'Retry in 30 seconds. If the issue persists, check wsdot.wa.gov/ferries for service status.',
|
|
48
48
|
},
|
|
49
|
+
{
|
|
50
|
+
reason: 'invalid_access_code',
|
|
51
|
+
code: JsonRpcErrorCode.ConfigurationError,
|
|
52
|
+
when: 'WSF rejected the request because WSDOT_ACCESS_CODE is missing, invalid, or not registered.',
|
|
53
|
+
retryable: false,
|
|
54
|
+
recovery: 'Register an access code at https://wsdot.wa.gov/traffic/api/, set WSDOT_ACCESS_CODE on the server, and restart it.',
|
|
55
|
+
},
|
|
49
56
|
],
|
|
50
57
|
async handler(_input, ctx) {
|
|
51
58
|
const alerts = await getFerryApiService().getAlerts(ctx);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-ferry-alerts.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-ferry-alerts.tool.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"get-ferry-alerts.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-ferry-alerts.tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,6 +18,12 @@ export declare const getFerryRoutes: import("@cyanheads/mcp-ts-core").ToolDefini
|
|
|
18
18
|
readonly when: "WSF Ferry API is unreachable or returns a non-2xx response after retries.";
|
|
19
19
|
readonly retryable: true;
|
|
20
20
|
readonly recovery: "Retry in 30 seconds. If the issue persists, check wsdot.wa.gov/ferries for service status.";
|
|
21
|
+
}, {
|
|
22
|
+
readonly reason: "invalid_access_code";
|
|
23
|
+
readonly code: JsonRpcErrorCode.ConfigurationError;
|
|
24
|
+
readonly when: "WSF rejected the request because WSDOT_ACCESS_CODE is missing, invalid, or not registered.";
|
|
25
|
+
readonly retryable: false;
|
|
26
|
+
readonly recovery: "Register an access code at https://wsdot.wa.gov/traffic/api/, set WSDOT_ACCESS_CODE on the server, and restart it.";
|
|
21
27
|
}, {
|
|
22
28
|
readonly reason: "invalid_date";
|
|
23
29
|
readonly code: JsonRpcErrorCode.ValidationError;
|
|
@@ -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
|
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsHzB,CAAC"}
|