Conversation as an App
> **This is an example repo, not a library.** It exists to show an engineering idea, rebuilt over synthetic data so the idea can be read and run. It is not maintained as a dependency or hardened for production. Study the concepts; code reuse requires permission.
# Turn a conversational answer into something people can inspect

## Situation
Someone asks, **“How much did we spend in September?”** A total and category breakdown answer the first question. Then they want to understand the travel amount: which records contributed to it?
Conversation is a useful way to ask for the summary. Once it appears, selecting a category is a more direct way to explore it than describing that category in another message.
## Complication
A chart alone does not provide that interaction. It needs data with stable category names, a way to request contributing records, and a host that connects the interface to the server. Recalculating the summary inside the browser would also create a second source of truth.
The important connection is **question → tool result → interactive view → follow-up tool call → underlying records**. The view should help the person explore the answer while keeping its calculations and evidence together.
## Our Answer
A genuine [MCP Apps](https://apps.extensions.modelcontextprotocol.io/api/documents/Quickstart.html) integration connects two tools and one HTML resource. The included local host makes a fixed initial call so the full flow runs without a language model, API key, or external account.
### Give the host both an answer and a view
`summarize_spending` calculates totals from 18 seeded synthetic expenses. Its response includes `structuredContent` for the app and ordinary text for hosts that show only text. Both come from the same calculation, in integer cents.
The tool's `_meta.ui.resourceUri` names a registered `ui://` resource containing the bundled HTML and JavaScript. An app-capable host reads that resource, loads the view, and delivers the result. Registering HTML does not make every MCP client an app host; the host needs MCP Apps support.
The official TypeScript MCP SDK implements tool discovery, resource reads, and tool calls. The official MCP Apps extension implements the app-to-host bridge. A small esbuild bundle puts the view and its dependencies in one HTML resource without runtime CDN requests.
[Server registration](src/server.ts) · [Calculations and fixture](src/data.ts) · [Build](scripts/build.ts)
### Make a click continue the same investigation
The view renders category buttons with proportional bars. Selecting Travel requests `inspect_category({category:"Travel"})` through the host. The host forwards that request to the MCP server; the server validates the category and returns its actual contributing records.
In the generated dataset, the summary shows **$439.35 for Travel**. Selecting it returns **six records totaling $439.35**. The view checks that the detail total agrees with the selected summary before displaying it. It uses the returned record IDs and amounts rather than inventing detail from the chart.
`inspect_category` is marked app-visible in tool metadata. The local host also restricts the view to that tool. Metadata communicates intended visibility to hosts; the server independently validates arguments. Both tools are read-only.
[Interactive view](src/app.ts) · [Local host bridge](src/host.ts)
### Keep the interaction's state understandable
Changing categories clears the previous rows and shows a loading message. If someone clicks again before the first request finishes, a sequence counter prevents the slower response from overwriting the newer selection. Errors stay visible in the status area instead of leaving old records under a new heading.
The local preview uses a sandboxed iframe and the official `AppBridge`. Its small HTTP adapter forwards browser requests to an actual MCP client connected over stdio to the server. It is a learning host for this bundled app, not a general-purpose host for untrusted extensions.
[Preview adapter](scripts/preview.ts) · [Protocol tests](test/protocol.test.ts)
## Benefits
People can move from a summary to its evidence without repeatedly restating what they want. The category, amount, and contributing records stay in one view, making a surprising total easier to inspect.
The same result remains readable as text. The interface adds a convenient way to explore the answer while the server retains responsibility for its data and calculations.
[Run and connect it](docs/running.md) · [Mechanisms and tradeoffs](docs/mechanisms.md) · [Blog draft](docs/post.md)
Copyright © 2026 Geoffrey Lew. All rights reserved.
TDQS
Scored across 2 tools
The two tools have clearly distinct roles: summarize_spending provides an aggregate view by category, while inspect_category drills down into one category's records. There is no overlap or ambiguity in when to use each.
Both tools follow a consistent snake_case verb_noun pattern (summarize_spending, inspect_category), making the naming predictable and easy to parse.
Two tools is thin for a server, though it may be sufficient for a narrowly scoped spending exploration surface. The set lacks any additional operations that would make it feel more robust.
The pair covers the core workflow of viewing a summary and then inspecting category details. Minor gaps exist, such as no way to filter by date range or search across records, but the essential exploration loop is present.