Core Concepts
Views
Edit a View, see it reload in preview, and call backing tools.
A View is a React component compiled into a predeclared HTML resource.
Edit view.tsx
import { useApp } from "bitmcp/react";
export default function Forecast() {
const { result, callTool } = useApp();
if (!result) return null;
return (
<div className="p-4 text-foreground">
<h1>{result.city}</h1>
<p className="text-muted">{result.summary}</p>
<button
type="button"
onClick={() => callTool("get-forecast.refresh", { city: result.city })}
>
Refresh
</button>
</div>
);
}See the hash reload
pnpm devChange the View and save. The preview reloads the iframe when the View hash changes. Seed input with Preview fixtures.
Troubleshooting
- Blank iframe — Confirm the tool returned structured content matching your
outputschema. - Fetch blocked in View — Add the API domain to
connecton the sibling tool. See Tools. - Styles missing — Use Tailwind
classNameutilities. The compiler inlines CSS per View.
Reference
ui:// resources
The compiler bundles view.tsx with esbuild into one HTML document with MIME type text/html;profile=mcp-app.
- URI shape:
ui://tools/<name>/<hash> - Content-addressed hash of compiled HTML (scripts and CSS inlined)
- Cacheable with
ttlMsandcacheScope: public
useApp()
const { result, callTool, app, isConnected, error } = useApp();File-based tools generate .bitmcp/views.d.ts so result is inferred from the sibling tool.
| Field | Description |
|---|---|
result | structuredContent from the host, or from callTool |
callTool(name, args?) | Calls a backing tool through the host |
app | Official Apps client instance |
isConnected | Connection state |
error | Connection error if any |
Views never call your server directly. callTool goes through the host as a stateless tools/call.
Styling
Every View is compiled with Tailwind v4. Theme tokens: bg-background, text-foreground, text-muted, border-border, font-sans. Add src/views.css only for extra @theme tokens or @source paths.
Escape hatches
import { useMcpApp, useHostStyles } from "bitmcp/react";Use only when you need direct access to the official client. The happy path is useApp().
Build boundaries
- Do not import
tool.tsfromview.tsx - Do not import Node builtins or secrets in Views
fetchin a View requires a siblingconnectallowlist