Skip to content

Latest commit

 

History

History
302 lines (221 loc) · 8.8 KB

File metadata and controls

302 lines (221 loc) · 8.8 KB

@mcp-apps-kit/ui-react

npm node license

React bindings for MCP applications.

@mcp-apps-kit/ui-react builds on @mcp-apps-kit/ui to provide React context and hooks for tool calls, tool results, and host context.

Table of Contents

Background

React widgets often need host-aware APIs for tool calls and UI state. This package provides a React-first wrapper around the vanilla UI SDK so you can use hooks instead of manual subscriptions.

Features

  • AppsProvider context wrapper
  • Hooks for tools, host context, and widget state
  • Typed tool calls with generics
  • Optional debug logger hook
  • Host capabilities - Query what the host supports (theming, display modes, file upload, etc.)
  • Size notifications - Automatic resize observer integration
  • Partial tool input - React to streaming tool inputs

Compatibility

  • Hosts: MCP Apps and ChatGPT (OpenAI Apps SDK)
  • Node.js: >= 18 for tooling/builds (browser runtime)
  • Peer dependencies: react and react-dom ^18 || ^19

Install

npm install @mcp-apps-kit/ui-react

Usage

Quick start

import { AppsProvider, useAppsClient, useToolResult, useHostContext } from "@mcp-apps-kit/ui-react";

function Widget() {
  const client = useAppsClient();
  const result = useToolResult();
  const host = useHostContext();

  return (
    <div data-theme={host.theme}>
      <button onClick={() => client.callTool("greet", { name: "Alice" })}>Greet</button>
      <pre>{JSON.stringify(result, null, 2)}</pre>
    </div>
  );
}

export function App() {
  return (
    <AppsProvider>
      <Widget />
    </AppsProvider>
  );
}

Typed tools and results

Use ClientToolsFromCore from @mcp-apps-kit/core for end-to-end type safety:

import { AppsProvider, useAppsClient, useToolResult } from "@mcp-apps-kit/ui-react";
import type { AppClientTools } from "../server"; // Exported from your server

function Widget() {
  // Typed client - callTool arguments and return types are inferred
  const client = useAppsClient<AppClientTools>();

  // Typed results - result?.greet?.message is typed as string | undefined
  const result = useToolResult<AppClientTools>();

  const handleGreet = async () => {
    // Option 1: Using callTool with tool name string
    await client.callTool("greet", { name: "Alice" });

    // Option 2: Using the typed tools proxy (recommended)
    await client.tools.callGreet({ name: "Alice" });
  };

  if (result?.greet) {
    return (
      <div>
        <p>{result.greet.message}</p>
        <p>at {result.greet.timestamp}</p>
      </div>
    );
  }

  return <button onClick={handleGreet}>Greet</button>;
}

export function App() {
  return (
    <AppsProvider>
      <Widget />
    </AppsProvider>
  );
}

Automatic size notifications

By default, the MCP adapter automatically reports UI size changes to the host using a ResizeObserver. This feature is only supported in MCP Apps and is silently ignored in ChatGPT.

To disable automatic resizing:

export function App() {
  return (
    <AppsProvider autoResize={false}>
      <Widget />
    </AppsProvider>
  );
}

Note: The autoResize option is only applied during initial mount. Changing it at runtime has no effect.

The AppClientTools type is generated in your server code:

// server/index.ts
import { createApp, defineTool, type ClientToolsFromCore } from "@mcp-apps-kit/core";

const app = createApp({
  tools: {
    greet: defineTool({
      input: z.object({ name: z.string() }),
      output: z.object({ message: z.string(), timestamp: z.string() }),
      handler: async (input) => ({ ... }),
    }),
  },
});

// Export for UI code
export type AppClientTools = ClientToolsFromCore<typeof app.tools>;

Examples

API

Provider

  • AppsProvider - Context wrapper for all hooks
    • client? - Pre-initialized client instance (optional)
    • forceAdapter? - Force a specific adapter ("mcp" | "openai" | "mock")
    • autoResize? - Enable/disable automatic size change notifications (default: true). Only supported in MCP Apps; ignored in ChatGPT. Note: changing this prop after initial mount has no effect.
    • fallback? - Component to show while client initializes
    • errorFallback? - Component to show on initialization error

Core Hooks

Hook Description
useAppsClient Client instance for tool calls
useToolResult Current tool result data
useToolInput Tool input parameters
useHostContext Host info (theme, viewport, locale, etc.)
useWidgetState Persisted state across reloads
useDisplayMode Fullscreen/panel mode control
useDebugLogger Debug logging configuration

Typed Tools Proxy

The client returned by useAppsClient includes a tools property with typed method wrappers:

import { useAppsClient } from "@mcp-apps-kit/ui-react";
import type { AppClientTools } from "../server";

function Widget() {
  const client = useAppsClient<AppClientTools>();

  const handleClick = async () => {
    // Tool name "greet" becomes method "callGreet"
    const result = await client.tools.callGreet({ name: "Alice" });
    console.log(result.message);
  };

  return <button onClick={handleClick}>Greet</button>;
}

Method names are generated by prepending "call" and capitalizing the first letter of the tool name (e.g., greetcallGreet, searchRestaurantscallSearchRestaurants).

Host Capabilities & Version

import { useHostCapabilities, useHostVersion } from "@mcp-apps-kit/ui-react";

function Widget() {
  const capabilities = useHostCapabilities();
  const version = useHostVersion();

  // Common capabilities (both platforms)
  const themes = capabilities?.theming?.themes; // ["light", "dark"]
  const modes = capabilities?.displayModes?.modes; // ["inline", "fullscreen", "pip"]

  // MCP Apps specific
  const hasPartialInput = !!capabilities?.partialToolInput;

  // ChatGPT specific
  const hasFileUpload = !!capabilities?.fileUpload;

  // Host version (MCP Apps only)
  // { name: "MCP Host", version: "1.0.0" }
  return <div>Host: {version?.name}</div>;
}

Size Notifications (MCP Apps)

import { useSizeChangedNotifications } from "@mcp-apps-kit/ui-react";

function Widget() {
  // Attach to container to auto-report size changes
  const containerRef = useSizeChangedNotifications();

  return <div ref={containerRef}>Content that may resize</div>;
}

Partial Tool Input (MCP Apps)

import { useOnToolInputPartial } from "@mcp-apps-kit/ui-react";

function Widget() {
  useOnToolInputPartial((input) => {
    // React to streaming partial input from the model
    console.log("Partial input:", input);
  });

  return <div>Streaming input widget</div>;
}

Theme & Style Hooks

Hook Description
useHostStyleVariables Apply host-provided CSS variables
useDocumentTheme Sync document theme with host
useSafeAreaInsets Safe area insets (ChatGPT)

Lifecycle Hooks

Hook Description
useOnToolCancelled Callback when tool is cancelled
useOnTeardown Cleanup callback before widget removal

File Operations (ChatGPT)

Hook Description
useFileUpload Upload files to host
useFileDownload Get file download URLs

Layout (ChatGPT)

Hook Description
useIntrinsicHeight Set widget intrinsic height
useView View management

Modals (ChatGPT)

Hook Description
useModal Modal dialog management

Contributing

See ../../CONTRIBUTING.md for development setup and guidelines. Issues and pull requests are welcome.

License

MIT