koala73 / koala73/worldmonitor
chore(mcp): get_chokepoint_status serves chokepoint-flows raw — the FlowSource taxonomy is neither declared nor enforced on the MCP surface
- Dominant language
- TypeScript
- Stars
- 86.6k
- Forks
- 13.1k
- Avg merge
- 8h 4m
- Merged PRs (30d)
- 825
Description
## Summary
Priority: **P3**. Found while shipping #6101. `FlowEstimate.source` is now a closed `FlowSource` enum, discoverable from the OpenAPI (`enum:`) and the generated TS (literal union), and the REST handler narrows the untyped seeder value so the served response cannot fall outside it. **The MCP surface gets none of that.**
`get_chokepoint_status` is a *cache tool*: it has no `_execute`, so `executeTool` reads its `_cacheKeys` straight from Upstash and never touches the sebuf handler.
```js
// api/mcp/dispatch.ts:43
const reads = tool._cacheKeys.map(k => readJsonFromUpstash(k));
```
Consequences for the `chokepoint-flows` dataset (which is `energy:chokepoint-flows:v1`, the raw seeder blob):
1. **Not discoverable.** The output schema declares no `source` property at all:
```js
// api/mcp/registry/cache-tools.ts:2032-2035
'chokepoint-flows': {
type: ['object', 'null'],
additionalProperties: { type: 'object' },
},
```
An agent calling `tools/list` or `describe_tool('get_chokepoint_status')` sees nothing about the taxonomy — on the one surface built specifically for agents.
2. **Not narrowed.** `toFlowSource` never runs on this path, so an undeclared seeder value reaches an MCP client verbatim.
This is pre-existing architecture, not a regression from #6101 — `cache-tools.ts` was already decoupled from the sebuf handlers (same raw-Redis pattern as `chokepoint_transits`, `ref`). #6101 simply made the asymmetry visible.
## The trap in the obvious fix
"Just add `enum: ['FLOW_SOURCE_UNSPECIFIED','portwatch-dwt','portwatch-counts']` to the schema" is **wrong on its own**, and it is the exact defect class #6078/#6084 documented in `docs/solutions/design-patterns/contract-gate-field-names-miss-value-axis.md`: it would declare a closed set on a path that does not enforce it. The MCP tool serves the raw blob, so the schema would be a promise the code does not keep — a fresh declare-vs-serve divergence, on a second surface, created by fixing the first one.
Whatever is chosen, the declaration and the served bytes must move together.
## Options
1. **Narrow + declare.** Add the `enum` to the schema AND normalize `source` in the tool's `_postFilter` (that seam already exists on this tool). Declaration and behavior stay in step, and MCP matches REST.
2. **Declare it open, honestly.** Leave the value un-narrowed but document in the `chokepoint-flows` dataset description that it is the raw seeder payload and may carry values outside the `FlowSource` taxonomy the REST endpoint serves. Cheapest; keeps the two surfaces deliberately different rather than accidentally different.
3. **Leave as-is.** No schema shape for the dataset at all, as today.
Note for whoever takes this: the sebuf empty-`enum_value` limitation that blocks `hazard_alert_level` on the OpenAPI path (#6106) does **not** apply here — `cache-tools.ts` is hand-authored JSON Schema, so `hazardAlertLevel: { enum: ['RED','ORANGE',''] }` is expressible on this surface today if option 1 is taken.
## Acceptance
- [ ] Pick an option; if 1, the narrowing and the `enum:` land in the same change.
- [ ] If 1: a test drives the MCP tool with an out-of-taxonomy `source` and asserts the served value is narrowed — not just that the schema validates.
- [ ] `tests/mcp-output-schema-coverage.test.mjs` still passes, and its fixture (`tests/fixtures/jmespath-samples/thin-get-chokepoint-status.response.json`) exercises the new shape rather than passing trivially through `additionalProperties: {type:'object'}`.
## Related
- #6101 — promoted `FlowEstimate.source` on the REST/OpenAPI/TS path
- #6106 — the `hazard_alert_level` half, blocked on sebuf codegen
- `docs/solutions/design-patterns/contract-gate-field-names-miss-value-axis.md`
Contributor guide
Research direction
Start with api/mcp/dispatch.ts and the chokepoint-flows entry in api/mcp/registry/cache-tools.ts, then inspect the existing _postFilter seam and related tests. Run tests/mcp-output-schema-coverage.test.mjs and review its thin-get-chokepoint-status fixture. Done means one selected option is implemented consistently, with an out-of-taxonomy source case covered if narrowing is chosen.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, tooling
- Issue type
- Refactor
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100