Skip to main content
Glama
Geofflew

Conversation as an App

by Geofflew
README.md
> **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

![A host invokes a spending tool; its structured result and registered HTML view form an app. Clicking a category calls a second tool through the host and returns the contributing records.](docs/overview.png)

## 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

B3.2/5.0

Scored across 2 tools

Disambiguation5/5

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.

Naming Consistency5/5

Both tools follow a consistent snake_case verb_noun pattern (summarize_spending, inspect_category), making the naming predictable and easy to parse.

Tool Count3/5

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.

Completeness4/5

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.

Maintenance

ActivityMaintained
ResponsivenessNo issues