[MCP] Add interactive UI search tools via MCP Apps#50
Conversation
Adds two opt-in MCP Apps (SEP-1865) tools alongside the unchanged text `search` tool, so app-aware hosts can render results as interactive UI without the bulk SERP JSON entering the model context window: - search_table: organic results as a sortable/searchable table - search_dashboard: summary metrics, source-breakdown chart, and a results table with a click-to-expand detail panel Both reuse the existing API-key resolution and a shared error-mapping helper, and surface upstream errors as an in-UI alert. Pure view-model helpers (organic_rows, source_breakdown, dashboard_summary) are unit tested offline with the existing mock style; tools assert build + error behavior. Also bumps fastmcp to >=3.4.2 with the [apps] extra, pins prefab-ui (<0.21, frequent breaking changes), and floors starlette at >=1.0.1 (CVE-2026-48710).
Split the UI composition out of the search_table / search_dashboard tools into pure build_table_app / build_dashboard_app helpers so the tools are thin (fetch + build) and the views can be rendered without a request context. Give the dashboard PieChart an explicit height so it renders at a stable size.
|
Extructuring the layout generation into synchronous pure builders ( It perfectly decouples the async I/O fetching loop from the UI composition tree, keeping the codebase clean, modular, and highly readable. Additionally, setting explicit bounds on the Exceptional code hygiene here, this is ready for prime time! (IMO) but worth to evolve if u are in it. Have a good day mate. ^^ |
21126d3 to
2c33f50
Compare
|
Splitting the view definitions so they can render standalone without a live request context is a top choice. It makes the entire layout fully testable and modular. The amended commit history looks clean. The rendered output on supporting hosts is flawless, zero noise inside the LLM context window while maintaining a premium, interactive user experience. Pinned bounds on On a broader note, it’s great to see the engineering velocity across the entire SerpApi ecosystem right now. Tracking the coordination across your public repositoriesm, from the automated schema syncs hitting the Cursor/Claude IDE plugins to the recent Eleventy overhauls, JSON-LD data graphs, and LLM-native From my end, this is fully verified and ready to be squashed into |
|
Hi @vm-serpapi, I tested this locally via the MCP Inspector. The added apps work well overall, apart from a few UI bugs in the MCP Inspector preview UI. I can take a closer look at those if we decide to move forward with this implementation. I think apps would be a great addition. One concern I have, though, is the introduction of new tools and resources. This change adds two new MCP tools and two MCP resources, one for each app. In the future, it would be useful for us to have multiple tools tailored to different engines, such as e-commerce, travel, and so on. What do you think about exploring FastMCP’s generative UI instead? I have not tried it yet, but based on the docs, it seems like this approach may only add one extra tool and resource, while allowing the AI agent consuming the MCP to build fully custom UIs tailored to the user’s needs. Curious to hear your thoughts. |
|
Hey everyone, I know I am chiming in a bit late here, but looking closely at the design after running some local validation, I think this current direction is a very clean abstraction for a production-grade server. Offloading the UI render tree directly to the client-side host while keeping the server-side JSON contract thin and engine-agnostic solves the context window bloat cleanly. Regarding @adarshdigievo's thought on exploring Generative UI to prevent tool and resource multiplication as you scale to vertical engines like e-commerce or travel: it is an interesting architectural trade-off. However, from some local debugging I made while testing similar layout loads on my own workflow, there is a fundamental difference in runtime dynamics between the two approaches. With Generative UI, the LLM fabricates the interface layout on the fly. At scale, this often turns into an expensive execution loophole where the agent spends its highest-cost output tokens negotiating layout wires, nesting rules, and UI schemas on every single turn. It can also be quite fragile under strict operations: if the model underperforms or hallucinates a broken layout schema, the client-side rendering engine fails completely. You would need to heavily harden defensive hooks, rulesets, and other less predictable agent behavior components to guardrail errors or expected outcome. Furthermore, if you are running on shared or managed team providers, dynamic layout text variations continuously fracture the KV-Cache prefix matching. This token thrashing invalidates prompt cache hits, driving up inferencing latency and costs for the entire team outside of basic MCP consumption on limited team budgets per month, or even worst, final client own budgets. It is much more efficient to secure a 90% savings on tokens via predictable context pinning in the provider VRAM (for those who loop harden/consider this kind of stuff, and it will increase after the 1Jun Copilot and other pricing IA Models because finished subsidies, reason why i been looking a lot of arxiv topics on KV) rather than breaking background cache iterations because the tool interpolates its own dynamic visual boilerplate. With Prefabricated Components (what Vlad has here), the server passes structured data payloads and maps them to deterministic layout primitives like DataTable or Metric. The client host already knows how to render them and functions as a spec-driven layer across modern IDE hosts. It is completely deterministic, highly cacheable at the transport level, and keeps the model's cognitive space focused 100% on evaluating search signals rather than writing UI boilerplate. Bypassing the need for output auditing or custom sanitization on the admin layer to clean up hallucinated structures is a significant advantage for long-term platform stability. imo To address the tool bloat concern without giving up this determinism, the solution lives in parameterization rather than raw generation. Instead of adding separate tools for every view or engine type, we can multiplex them behind a single parameterizable tool entry point, using a view configuration parameter like view_mode="dashboard" or view_mode="table", or utilizing vertical-specific payload routing. This keeps the protocol handshake payload clean, preserves client-side render stability, and scales gracefully across all 133+ engines in the repository roadmap. Keeping the synchronous layout builders thin via the build_table_app and build_dashboard_app extraction allows the system to remain highly modular on the boundary while ensuring token consumption scales predictably per turn. In large-scale operations, it comes down to a clear structural choice: you have to balance who executes and evaluates those layout nodes, whether it happens on the server side or the human operator side. Great work on this iteration, Vlad. And sorry for the meddling, but I hope this is still useful for you guys. |
|
Yes, thanks for the comments guys. My take is that at this stage prefab components would optimize cost, it's just a direct data mapping and it looks way better visually and provides real value. So regarding generative UI this is a good direction, but I would say that it is parallel, we can enable the agent to select what they would like to opt for, imo that would be the best option. @adarshdigievo I agree in general about the tool proliferation, but for this case I think we will just add |
Summary
Adopts FastMCP v3's MCP Apps to render search results as interactive UI.
Added two new opt-in tools are added for hosts that support Apps:
search_table-- organic results as a sortable/searchable table.search_dashboard-- metrics + source-breakdown chart + results table with a click-to-expand detail panel.Both take the same
paramsassearchand reuse its API-key handling. Errors render as an in-UI alert.Test plan
uv run pytest -q— 38 passed, 1 skipped (live test).uv format --checkanduv sync --extra dev --frozenpass.uv run fastmcp dev apps src/server.pyin an Apps-capable host.Screenshots