Robinhood App walkthrough
Robinhood demonstrates a larger App without making firmware own the product. Native code keeps OAuth/MCP transport and an exact operation allowlist; the App owns a checked-in Tool catalog, validation, mapping, selective SQLite persistence, schedule and fixed portfolio View.
Why the Agent sees three Tools, not 54 schemas
| Public Tool | Responsibility |
|---|---|
robinhood.search_tools | Search the checked-in 54-Tool catalog locally and return exact provider schema/safety guidance |
robinhood.call | Validate one exact provider operation and invoke it through native MCP policy |
robinhood.refresh_portfolio | Run the App-owned aggregate that refreshes bounded dashboard Data |
This is a cross-model deferred-loading pattern. It avoids putting every complete provider schema into every model request while keeping the catalog executable and reviewable inside the App. It is not a native provider-specific Tool Search wire protocol.
Two independent allowlists
assets/tool-catalog.jsoncontains the provider name, exact input schema and combined usage/safety description.app.json.providerOperationscontains the operations native MCP transport may invoke.
A provider Tool must exist in both. The App validates the selected schema before crossing the native boundary; the host independently enforces the installed allowlist. Catalog and allowlist changes must be reviewed together.
Selective persistence
| Provider result | SQLite effect | Reason |
|---|---|---|
| accounts | replace account rows | account selector and status |
| portfolio | upsert current portfolio and value | dashboard and chart |
| positions | replace per-account positions | positions View |
| orders | replace per-account activity | activity View |
| realized P&L | upsert day/week values | dashboard metrics |
| place/cancel result | upsert returned order state only | directly changes visible activity |
| other provider Tools | none | current fixed View does not consume them |
There is no generic raw-response log, quote cache or schema-less Tool cache. Live provider results still return to the Agent even when SQLite does not persist them.
Scheduled aggregate
A five-minute App schedule routes to refreshPortfolio. It loads accounts, batches required per-account calls, normalizes bounded dashboard tables and writes onerefresh_runs record in a transaction. One successful transaction publishes one revision. The View may remain closed during the whole operation.
View ownership
The fixed View projects accounts, portfolio, totals, positions, activity and chart points from local SQLite. It owns account selection, screen/span choice and bounded scrolling as presentation state. It never owns OAuth, MCP sessions or raw provider responses.
Real-account safety
- Tool descriptions distinguish read/review operations from real account or real-money effects.
- The App validates provider arguments locally before native transport.
- A real-money retry must reuse the same provider
ref_id. - An ambiguous transport result must not be retried with a new id.
- Native allowlisting is not a substitute for product-level confirmation and risk controls.
The repository App exposes real-action provider operations, so development and demonstrations must explicitly choose read-only scenarios unless the user has authorized account-changing work.
Read the source: app.json ↗, schema.sql ↗, actions.js ↗, view.js ↗, Tool contract ↗.