Documentation Index
Fetch the complete documentation index at: https://docs.metabind.ai/llms.txt
Use this file to discover all available pages before exploring further.
useMCPHost() returns the MCP host bridge when the BindJS view is rendered inside an MCP host — a sandboxed iframe in Claude, ChatGPT, VS Code, or Cursor, or natively via the Assistant SDK on iOS or Android. When the view runs anywhere else (a local preview, a standalone editor, a non-MCP context), the hook returns null. Every interaction described on this page goes through that bridge: calling other tools, posting messages to the chat, updating the LLM’s working context, requesting a different display mode, opening links the iframe can’t navigate to itself.
This page is about how to use the bridge from inside a component body. For the wider picture of how a single BindJS definition reaches each rendering target, see Native rendering.
The MCPHost interface
The hook returns either a singleMCPHost object or null. Always guard for null before you use it.
null, then call methods — is the same shape every interaction below uses.
Calling tools with toolCall
toolCall invokes another tool on the same MCP App and resolves to the tool’s structured data — not the raw MCP envelope.
- If the underlying MCP response carries
structuredContent, that value is returned directly. - Otherwise, the runtime parses the first text block as JSON and returns the parsed value. If parsing fails, you get the raw text.
- If the tool returns
isError: true,toolCallthrows. There is no{ data, error }envelope — usetry/catchor.catch().
Messaging the chat
Two methods talk to the surrounding chat. They have different consequences and are usually combined.sendMessage injects a message into the host’s chat as if the user had typed it. The LLM sees the message and produces a full response turn.
updateModelContext updates the LLM’s working context silently. No response is triggered. The argument is an arbitrary key-value object — the host injects it into the model context for the next turn.
Display modes
requestDisplayMode asks the host to resize or reposition the BindJS view’s container. Three modes are defined:
'inline'— embedded in the chat transcript at natural height. This is the default.'fullscreen'— the view takes over the host surface.'pip'— picture-in-picture overlay, persistent across host navigation.
'inline'. Don’t assume the mode change took effect — design the view so the inline rendering is acceptable on its own.
Navigation
openLink asks the host to open a URL. Sandboxed iframes can’t navigate the parent window directly, so this routes through the chat host’s URL handler:
openLink as the only portable way to leave the view — window.open, anchor target="_blank", and similar web-only mechanisms either fail silently or break sandbox isolation, depending on the host.
Iframe sizing
sizeChanged informs the chat host of a content-height change so it can resize the iframe.
Logging
log sends a structured log entry to the host. Use it instead of console.log for diagnostics that need to reach the host: a sandboxed iframe’s console isn’t always visible from the surrounding chat surface, but log is.
debug, info, warning, error — map to the host’s log surface. The optional data argument is a JSON-compatible object that travels with the message.
Low-level transport
sendRequest and sendNotification are the JSON-RPC primitives every other method on the bridge is built on.
Fetch on mount
A common tool-result UI is “fetch data when the view appears, then render based on the result”. The pattern usesuseState for the data and triggers the call inside .onAppear():
.then and .catch callbacks trigger a re-render, so the body runs again with the new values.
.onAppear() fires once when the view is first composed. If you need to refetch on a prop change, drive the call from a state effect or re-key the subtree with .id(...) so a new instance is composed. See State and environment.End-to-end example: a product card
A product card pulls together every piece of the bridge described above. Tapping View details fetches the product via a tool, renders it in the card, places the selected product in the LLM’s context, and prompts the LLM to talk about it — so the chat that follows is grounded in the same product the user is looking at.The user taps View details
The button’s handler reads the
MCPHost bridge and bails out if it’s null — the same view rendered in a local preview is still safe to render, the button is inert.The view fetches the product via a tool
toolCall("get_product", { id }) invokes a Data Tool defined elsewhere in the same MCP App. The runtime resolves it to the structured product object directly. If the tool throws, the catch block logs through the host and the busy state clears.The component renders the product
setProduct(result) triggers a re-render. The body runs again with product populated, and the Text(product.name) / Text(product.description) paths replace the Empty() placeholders.The view tells the LLM what's selected
updateModelContext({ selectedProduct: result }) updates the model’s working context silently. No turn fires. From the model’s perspective, it now knows which product the user is looking at.toolCall to fetch, updateModelContext to inform the model, sendMessage to prompt — covers most real uses of the host bridge. Vary the messaging step to match the experience you want: skip sendMessage if you only want to update the context for the next user-driven turn; skip updateModelContext if you only want to force a turn against the context already in flight.
Important rules
useMCPHost()returnsnulloutside an MCP context. Always guard. Local previews, the standalone editor, unit-test runners — none of these supply a bridge, and the same component should still render somewhere reasonable.toolCallthrows on tool errors. Usetry/catch(or.catch()on the promise). There is no{ data, error }envelope to destructure.sendMessagetriggers a turn;updateModelContextdoesn’t. If you only want to inform the model, useupdateModelContextalone. If you want a response, follow the silent update with asendMessage.- Display-mode requests are advisory. Hosts may ignore them. Design the view so the inline rendering is acceptable on its own.
- Sandboxed iframes can’t navigate. Use
openLink, notwindow.openor anchor tags. The host’s URL handler decides what happens next.
What to read next
State and environment
The hooks and patterns the example above relies on —
useState, useStore, useEnvironment.Hooks
A concept-level index of every runtime-injected hook with one-paragraph use cases.
Composition and slots
How components compose into trees and how to declare slots that accept child components.
Connecting to Claude Desktop
Wire up your MCP App to a host so the bridge becomes live.