search-homes. After that, the view registers its own tools, so follow-ups such as “now the Mission, under $2M” update the open map instead of rendering a second view.
The listings are fictional San Francisco homes. Map tiles come from Esri’s keyless Canvas basemaps, so the example needs no listing API or API key.
Try the live example
Ask for homes in San Francisco. Select Fullscreen in the view to see the listing cards, then click a card or pin to load its details. Open the example in a new tab.This embedded chat runs its model on Manufact’s servers, which cannot reach tools registered inside the view. Follow-up refinements such as “now the Mission, under $2M” need a host that forwards view tools to the model. To try them, run the example with your own model provider API key.
Run the example
The example is a complete project with about 3,500 lines of view code, so clone it instead of copying it. It requires Node.js 22.22.2 or later.search-homes and opens the view. Each later prompt calls a view tool, and the open map updates in place. Select Fullscreen in the view to switch display modes.
The project has four main files:
How it works
The example combines five MCP Apps patterns. Each one works on its own in your server.Open the view once with the whole dataset
search-homes is the only tool that renders the view. Besides the matching listing IDs, it returns the whole staged catalog in structuredContent, so the view can filter any neighborhood locally without another server call. The tool description tells the model to use the view tools for follow-ups instead of calling search-homes again.
src/index.ts
minPrice and minBaths filters and returns the median price.
Let the model drive the open view
useViewTool() registers a tool that exists only while the view is mounted. The host lists it to the model, and the handler runs inside the view with access to React state and refs. The example registers seven view tools. This one zooms the Leaflet map:
views/property-search/view.tsx
Keep the model in sync with the view
Users also filter, remove, and select homes by clicking.<ModelContext> reports the current view state to the model, so “remove the most expensive one” refers to what is on screen now:
views/property-search/view.tsx
Load details with an app-only tool
get-listing-details has visibility: "app", so MCP Apps hosts hide it from the model while the view can still call it. The view calls it with useCallTool() when the user selects a card or pin:
src/index.ts
views/property-search/view.tsx
Allow map tiles and fullscreen
The view loads tiles fromserver.arcgisonline.com, so the tool’s view.csp lists that origin in resourceDomains and connectDomains. Without it, the host’s Content Security Policy blocks the tiles and the map renders blank.
The view declares both display modes in viewConfig and requests fullscreen from a button with useDisplayMode():
views/property-search/view.tsx
Adapt it to your app
- Real listings: replace the staged
LISTINGSarray with a call to your listing API insidesearch-homes. Keep the returned catalog small. For large datasets, return only the first page and fetch more from the view with an app-only tool. - Another tile provider: change the tile URL in
map.tsx, update both CSP lists, and show the attribution your provider’s terms require. - Other domains: the same shape fits any “search, then refine” app, such as hotels, restaurants, or job listings. Keep one rendering tool, move refinements into view tools, and report visible state with
ModelContext.