MCP server
Inline cards
In hosts that render MCP Apps, Sprouts tools answer as interactive cards instead of prose — 27 views across eight of the nine tools, with the same numbers in text everywhere else.
In hosts that support it, Sprouts results arrive as interactive cards instead of prose: a results table you can scan, a spend strip you can toggle, a run you can cancel.
A tool result normally reaches you as prose the client renders. The MCP Apps extension lets a tool point at a ui:// HTML resource instead, and a host that supports it draws that resource in a sandboxed iframe.
- 27 views across eight of the nine tools. A view is a payload shape rather than a tool, so
sprouts_usagealone has three. sprouts_top_uphas none. Its answer is a link or the host's own payment dialog, and its controls live on the cards that already exist.
Which hosts render them#
| Renders cards | Text only |
|---|---|
| Claude web, desktop and mobile | Claude Code |
| Cursor | Codex |
| VS Code | Gemini CLI |
| ChatGPT |
Text is not a degraded answer. Every number, caveat and citation a card draws is in the prose too, and it has to stay that way permanently. That is why the text path is written first and the card is a rendering of it, never an addition to it.
What each card shows#
Search
The results table, with columns that follow the category: companies get the dashboard's own projection, people a different one, and the seven document categories are built from the columns the response carried.
When category: "auto" let the classifier choose, the card names the category it picked. Otherwise there is no way to tell what was actually searched.
Similar Companies
The same table, plus the anchors it was given as chips, the and/or combine mode, and each anchor's weight. A look-alike list without the companies it is like is a search result with no query attached.
Record Lookup
A reconciliation table first: what you asked for, what resolved, what was over the batch ceiling. It keeps your order and your duplicates, because callers match results back by position.
Single records open as detail views; a miss opens the explanation of why it missed.
Relationships
Ranked bridge people with match_confidence and the path for each row. For shared_institutions, the aggregation instead: institutions with per-side counts, no people and no cursor.
The two are different cards because they are different answers, and the card is chosen from the route that was called rather than sniffed from the payload.
Research Agent
The lifecycle:
- a progress card with how long the last call waited, a Check again button and a Cancel run button. Both are free. Checking again is also what collects a finished run, and the first read of a finished run is when its held price is charged (or released, if it failed)
- the terminal results, with rows, coverage, exclusions and cited insights
- a run list, which is the only way to recover a lost
run_idwithout paying for a second run - a cancelled card that reads as cancelled rather than as failure, and says that nothing was charged
Coverage
The per-corpus grid with record counts and how much of each corpus semantic search can reach, and the published not_in_index holes. MISSING and empty are drawn as different claims, because they are different claims.
Usage
- The balance, with an hour/day/month/total toggle and the spend strip.
- The run history, as a real pager.
- Any run's stored rows, replayed from the snapshot 10 at a time, like every other list. Moving through them is free.
When the balance is at or below the server's low-balance line, the card says so and offers Ask to top up.
Errors
A failed call draws its own card with the error code, what to do next and the request_id, and offers Try again only where a retry can help. An out-of-credits failure also offers Ask to top up.
A budget refusal is drawn as a decision rather than a failure, with nothing to retry, because the call never ran.
Feedback
A report form with its tag picker read live from the server, opened from the result it is about, and a receipt afterwards.
The receipt has no action on it at all. The tool is deliberately not idempotent, so two submits file two reports, and the card makes a double submit impossible rather than warning about it.
One row format, one page size#
- Every list is the same table, 10 rows at a time. That is the default page size the tools themselves send, so what a card shows and what a tool asked for are the same thing rather than two settings that can drift.
- Moving back and forward over rows the card already holds costs nothing, so the card never grows taller the more you use it. Past the first page the count line reads as a range,
showing 11–20 of 4,210+: a claim a growing prefix could not make. The count line also states what the rows on the card cost, from the responses themselves (· 0.5 credits). - Long values are clipped rather than allowed to run away. One company description of two thousand characters would otherwise make one table row taller than the whole card. A clipped cell says it was clipped: a shortened value must never render as a missing one.
- Cards are drawn in Sprouts' own palette, in a bordered panel opened by the Sprouts mark, so a result reads as something Sprouts delivered rather than as the host's own text.
- Light or dark follows the host's declared theme, not the browser's preference. A card is drawn inside someone else's surface, and a light card in a dark app is the same defect as unreadable text.
Paging#
Every list has one forward control, and it is always the cheapest way to more rows that is available:
| Where the next rows are | The control | Cost |
|---|---|---|
| Already in the card | Show next 10 (or however many are left) | Free, and makes no call |
| On a free route: run history, a finished research run | Load next page | Free |
| On a billable route: Search, Similar Companies, Relationships | Next page, then Yes, fetch it | The results that page adds, after you confirm |
| On a billable route, in a host that cannot run the call for the card | Ask for the next page | Nothing until the model acts on it |
Show previous is always there once you have moved, and always free.
Buying a page takes two clicks
Next page spends nothing. It puts the question on the card ("Fetch the next page? This spends credits from your balance.") with a Cancel beside it. Only Yes, fetch it spends.
- On a test key, the question says instead that no credits are charged.
- The question carries no credit figure: what a page costs depends on how many results it brings back, which is known only once it has. The count line states the cost after.
- The purchase is an ordinary tool call, so it goes through everything the model's own calls do: the session budget, the confirmation threshold and the response cache. The same page cannot be bought twice, and paging back over rows you have bought spends nothing.
How a card resends the request
A card can only page by resending exactly the request that produced its rows, because a cursor is bound to the rest of that request. So billable tools echo the validated request back in structuredContent.request, minus cursor and limit.
Where that echo is missing, or the host cannot run the call, the card falls back to asking. The message it puts in the conversation names the cursor and says to resend the request unchanged, with a line beside the button saying that the next page is a further charge. In a host where a card cannot send a message either, the card says the same thing in words, for you to ask yourself.
Record Lookup has nothing to buy. A lookup is keyed by the identifiers you sent, so there is no cursor, only a window over what came back.
Why fetch controls never name a number
The API returns up to the page size, and records too large to inline leave the table as links, so a page of 10 can arrive as 8 rows. The count line accounts for them instead (showing 8 of 177,429 · 2 held back for size), with the two listed below the table, free to open.
A control names a count only when the rows are already in the card.
What a card can spend#
One thing: the next page of a Search, Similar Companies or Relationships result, after two clicks, as above.
Nothing else on any card costs anything: paging rows already in hand, free-route pages, opening a history row, replaying stored rows, removing a coverage filter, polling or cancelling a run, filing feedback. Polling a run buys no new work: when it finds the run finished, it settles the price that was held when the run started.
Everything else that would spend still goes back through the conversation, where the model can weigh it with more context than a button has:
- A card never starts a new search or a new research run.
- A better route suggestion is shown rather than acted on.
- A card never opens a payment page. Ask to top up puts a message in the conversation ("I'm out of Sprouts credits. Open a top-up page for me.") and the assistant takes it from there with
sprouts_top_up. The only place a payment page is offered is a dialog your host draws, or a link the assistant hands you.