feat(ui): render the MCP-app widget only on the world/region page; every other page is just data #87

Closed
opened 2026-07-06 17:25:42 +00:00 by coilysiren · 2 comments
Owner

Goal (Kai)

eco-app currently renders an MCP-app UI widget (the MCP Apps spec inline visual render) for basically every page / tool. Kai only wants that rich visual render for the one page that is actually visual: the world / region page (the map). Every other page is "just data" - a plain data render (tables, stat tiles, sparklines), no MCP-app widget.

Keep it simple.

What to do

  1. Find where the MCP-app render is wired. Each eco tool/page currently returns an MCP-app UI resource (the "renders as an inline widget via the MCP Apps spec; falls back to a markdown/plain summary otherwise" behavior). Locate the shared render path in the eco-app source.
  2. Gate the widget render to the world/region page only. Confirm which tool(s) back that page - almost certainly get_eco_world and get_eco_map (the hotspot/deed map), possibly the get_eco_species population map. Those keep the MCP-app visual render.
  3. Make every other page "just data." For all other tools (economy, market, trades, stores, currency, government, civics, milestones, progression, crafting_atlas, social, server_status), drop the MCP-app widget and return the plain data render they already fall back to. Do not delete the underlying data - just stop emitting the widget resource for them.
  4. Keep the data payloads unchanged (the JSON each tool returns is the app's data layer and is consumed elsewhere).

Supporting artifact

Kai has a committed-ready fixture bundle - a real, trimmed snapshot of all 14 eco domains captured from the live Cycle-14 server (one JSON per domain + manifest + README). It is exactly the "just data" these pages render, so it doubles as the UI-dev fixture set for this change. Drop it under the app's fixtures dir; wire the just-data pages to render from it in a dev/mock path if that helps iterate.

Acceptance

  • The world/region page still renders its MCP-app visual widget.
  • Every other page renders plain data, no MCP-app widget resource emitted.
  • Tool data payloads unchanged; existing consumers unaffected.
  • Build + tests green; docs/FEATURES updated to reflect that the MCP-app render is now scoped to the world/region view only.

Note on scope

If "world and region" are two distinct pages/tools in the app, keep the widget for both. If unsure which tools map to that page, confirm from the routing/render code before gating - do not guess and silently drop the wrong one.

## Goal (Kai) eco-app currently renders an **MCP-app UI widget** (the MCP Apps spec inline visual render) for **basically every page / tool**. Kai only wants that rich visual render for the **one page that is actually visual: the world / region page** (the map). **Every other page is "just data"** - a plain data render (tables, stat tiles, sparklines), no MCP-app widget. Keep it simple. ## What to do 1. **Find where the MCP-app render is wired.** Each eco tool/page currently returns an MCP-app UI resource (the "renders as an inline widget via the MCP Apps spec; falls back to a markdown/plain summary otherwise" behavior). Locate the shared render path in the eco-app source. 2. **Gate the widget render to the world/region page only.** Confirm which tool(s) back that page - almost certainly `get_eco_world` and `get_eco_map` (the hotspot/deed map), possibly the `get_eco_species` population map. Those keep the MCP-app visual render. 3. **Make every other page "just data."** For all other tools (economy, market, trades, stores, currency, government, civics, milestones, progression, crafting_atlas, social, server_status), drop the MCP-app widget and return the plain data render they already fall back to. Do not delete the underlying data - just stop emitting the widget resource for them. 4. Keep the data payloads unchanged (the JSON each tool returns is the app's data layer and is consumed elsewhere). ## Supporting artifact Kai has a committed-ready **fixture bundle** - a real, trimmed snapshot of all 14 eco domains captured from the live Cycle-14 server (one JSON per domain + manifest + README). It is exactly the "just data" these pages render, so it doubles as the UI-dev fixture set for this change. Drop it under the app's fixtures dir; wire the just-data pages to render from it in a dev/mock path if that helps iterate. ## Acceptance - The world/region page still renders its MCP-app visual widget. - Every other page renders plain data, no MCP-app widget resource emitted. - Tool data payloads unchanged; existing consumers unaffected. - Build + tests green; `docs`/FEATURES updated to reflect that the MCP-app render is now scoped to the world/region view only. ## Note on scope If "world and region" are two distinct pages/tools in the app, keep the widget for both. If unsure which tools map to that page, confirm from the routing/render code before gating - do not guess and silently drop the wrong one.
Member

🔒 Reserved by ward agent --driver claude — container engineer-claude-eco-app-87 on host KAI-DESKTOP-TOWER is carrying this issue (reserved 2026-07-06T17:26:47Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); --force overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

run seed context — what this run is carrying (ward#609)
  • Resolved: coilyco-gaming/eco-app#87 · branch issue-87 · driver claude · workflow direct-main
  • Run: engineer-claude-eco-app-87 · ward v0.413.0 · dispatched 2026-07-06T17:26:47Z
  • Comment thread: 0 included in the pre-flight read, 0 stripped (ward's own automated comments).

Issue body as seeded:

## Goal (Kai)

eco-app currently renders an **MCP-app UI widget** (the MCP Apps spec inline visual render) for **basically every page / tool**. Kai only wants that rich visual render for the **one page that is actually visual: the world / region page** (the map). **Every other page is "just data"** - a plain data render (tables, stat tiles, sparklines), no MCP-app widget.

Keep it simple.

## What to do

1. **Find where the MCP-app render is wired.** Each eco tool/page currently returns an MCP-app UI resource (the "renders as an inline widget via the MCP Apps spec; falls back to a markdown/plain summary otherwise" behavior). Locate the shared render path in the eco-app source.
2. **Gate the widget render to the world/region page only.** Confirm which tool(s) back that page - almost certainly `get_eco_world` and `get_eco_map` (the hotspot/deed map), possibly the `get_eco_species` population map. Those keep the MCP-app visual render.
3. **Make every other page "just data."** For all other tools (economy, market, trades, stores, currency, government, civics, milestones, progression, crafting_atlas, social, server_status), drop the MCP-app widget and return the plain data render they already fall back to. Do not delete the underlying data - just stop emitting the widget resource for them.
4. Keep the data payloads unchanged (the JSON each tool returns is the app's data layer and is consumed elsewhere).

## Supporting artifact

Kai has a committed-ready **fixture bundle** - a real, trimmed snapshot of all 14 eco domains captured from the live Cycle-14 server (one JSON per domain + manifest + README). It is exactly the "just data" these pages render, so it doubles as the UI-dev fixture set for this change. Drop it under the app's fixtures dir; wire the just-data pages to render from it in a dev/mock path if that helps iterate.

## Acceptance

- The world/region page still renders its MCP-app visual widget.
- Every other page renders plain data, no MCP-app widget resource 

… (truncated to 2000 chars; full body is on this issue)

Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.413.0).

— Claude (she/her), via ward agent

<!-- ward-agent-reservation --> 🔒 Reserved by `ward agent --driver claude` — container `engineer-claude-eco-app-87` on host `KAI-DESKTOP-TOWER` is carrying this issue (reserved 2026-07-06T17:26:47Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); `--force` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh** — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <details><summary>run seed context — what this run is carrying (ward#609)</summary> - **Resolved:** `coilyco-gaming/eco-app#87` · branch `issue-87` · driver `claude` · workflow `direct-main` - **Run:** `engineer-claude-eco-app-87` · ward `v0.413.0` · dispatched `2026-07-06T17:26:47Z` - **Comment thread:** 0 included in the pre-flight read, 0 stripped (ward's own automated comments). **Issue body as seeded:** ``` ## Goal (Kai) eco-app currently renders an **MCP-app UI widget** (the MCP Apps spec inline visual render) for **basically every page / tool**. Kai only wants that rich visual render for the **one page that is actually visual: the world / region page** (the map). **Every other page is "just data"** - a plain data render (tables, stat tiles, sparklines), no MCP-app widget. Keep it simple. ## What to do 1. **Find where the MCP-app render is wired.** Each eco tool/page currently returns an MCP-app UI resource (the "renders as an inline widget via the MCP Apps spec; falls back to a markdown/plain summary otherwise" behavior). Locate the shared render path in the eco-app source. 2. **Gate the widget render to the world/region page only.** Confirm which tool(s) back that page - almost certainly `get_eco_world` and `get_eco_map` (the hotspot/deed map), possibly the `get_eco_species` population map. Those keep the MCP-app visual render. 3. **Make every other page "just data."** For all other tools (economy, market, trades, stores, currency, government, civics, milestones, progression, crafting_atlas, social, server_status), drop the MCP-app widget and return the plain data render they already fall back to. Do not delete the underlying data - just stop emitting the widget resource for them. 4. Keep the data payloads unchanged (the JSON each tool returns is the app's data layer and is consumed elsewhere). ## Supporting artifact Kai has a committed-ready **fixture bundle** - a real, trimmed snapshot of all 14 eco domains captured from the live Cycle-14 server (one JSON per domain + manifest + README). It is exactly the "just data" these pages render, so it doubles as the UI-dev fixture set for this change. Drop it under the app's fixtures dir; wire the just-data pages to render from it in a dev/mock path if that helps iterate. ## Acceptance - The world/region page still renders its MCP-app visual widget. - Every other page renders plain data, no MCP-app widget resource ``` … (truncated to 2000 chars; full body is on this issue) Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.413.0). </details> <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Author
Owner

WARD-OUTCOME: done - MCP-app widget now renders only for the world/region tools (get_eco_world, get_eco_map, get_eco_ecoregion); every other tool is data-only. Landed on main as f8ee51b.

The actual gate was the easy part and felt clean: one WIDGET_TOOLS frozenset as the source of truth, then a strip in list_tools and a thin call_tool wrapper over the dispatcher. Data payloads untouched, so /preview/*.json and the SPA are unaffected. That was maybe 20 lines.

What fought back was the blast radius around it. The widget claim was baked into 22 tests (each asserting the fragment was present), the tool descriptions ("Renders as a visual widget..."), and six docs files. So most of the work was flipping assertions to prove absence while keeping the data-block coverage, and un-claiming the widget everywhere it was advertised. Mechanical, but wide.

One real judgment call: get_eco_species. The issue flagged it as a possibly-keep, but routing settled it - frontend/src/pages/Map.tsx fetches ecoregion + world + map, not species, so species doesn't back the world/region page and became data-only like the rest. "region" resolved to get_eco_ecoregion the same way (it is fetched by /map, so it kept the widget). If you actually wanted the species population card to stay visual, it's a one-line add back to the frozenset.

Confidence is high: ruff + mypy clean, 489 tests green, the stdio smoke round-trip passes, and I runtime-checked that exactly those three tools still advertise a widget.

Two rough edges worth a follow-up: (1) the fixture bundle mentioned in the issue was never attached, so I couldn't wire the just-data pages to a dev/mock fixture path - it was optional ("if that helps"), but if you still want it, drop the bundle and file a ticket. (2) The economy/climate/currency resource shells are now orphaned (retained but referenced by no tool) - I annotated them in docs rather than removing, since a test pins their registration; a small cleanup could drop them.

WARD-OUTCOME: done - MCP-app widget now renders only for the world/region tools (get_eco_world, get_eco_map, get_eco_ecoregion); every other tool is data-only. Landed on main as f8ee51b. The actual gate was the easy part and felt clean: one `WIDGET_TOOLS` frozenset as the source of truth, then a strip in `list_tools` and a thin `call_tool` wrapper over the dispatcher. Data payloads untouched, so `/preview/*.json` and the SPA are unaffected. That was maybe 20 lines. What fought back was the blast radius around it. The widget claim was baked into 22 tests (each asserting the fragment was *present*), the tool descriptions ("Renders as a visual widget..."), and six docs files. So most of the work was flipping assertions to prove *absence* while keeping the data-block coverage, and un-claiming the widget everywhere it was advertised. Mechanical, but wide. One real judgment call: `get_eco_species`. The issue flagged it as a *possibly*-keep, but routing settled it - `frontend/src/pages/Map.tsx` fetches ecoregion + world + map, not species, so species doesn't back the world/region page and became data-only like the rest. "region" resolved to `get_eco_ecoregion` the same way (it *is* fetched by /map, so it kept the widget). If you actually wanted the species population card to stay visual, it's a one-line add back to the frozenset. Confidence is high: ruff + mypy clean, 489 tests green, the stdio smoke round-trip passes, and I runtime-checked that exactly those three tools still advertise a widget. Two rough edges worth a follow-up: (1) the fixture bundle mentioned in the issue was never attached, so I couldn't wire the just-data pages to a dev/mock fixture path - it was optional ("if that helps"), but if you still want it, drop the bundle and file a ticket. (2) The economy/climate/currency resource *shells* are now orphaned (retained but referenced by no tool) - I annotated them in docs rather than removing, since a test pins their registration; a small cleanup could drop them.
Sign in to join this conversation.
No description provided.