From aa8968808a3e69b908cb1a63ef4f6ae265179116 Mon Sep 17 00:00:00 2001 From: martinalong Date: Wed, 10 Dec 2025 15:39:22 -0800 Subject: [PATCH] Add utility for using styles --- src/app.ts | 1 + src/react/index.tsx | 2 + src/react/useHostStyles.ts | 84 ++++++++++++++++++++++++++++++++++++++ src/styles.ts | 41 +++++++++++++++++++ 4 files changed, 128 insertions(+) create mode 100644 src/react/useHostStyles.ts create mode 100644 src/styles.ts diff --git a/src/app.ts b/src/app.ts index c4970fc..2321374 100644 --- a/src/app.ts +++ b/src/app.ts @@ -47,6 +47,7 @@ import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; export { PostMessageTransport } from "./message-transport"; export * from "./types"; +export { applyHostStyles } from "./styles"; /** * Metadata key for associating a resource URI with a tool call. diff --git a/src/react/index.tsx b/src/react/index.tsx index d5ce376..82867df 100644 --- a/src/react/index.tsx +++ b/src/react/index.tsx @@ -9,6 +9,7 @@ * ## Main Exports * * - {@link useApp} - React hook to create and connect an MCP App + * - {@link useHostStyles} - React hook to apply host styles as CSS variables * - {@link useAutoResize} - React hook for manual auto-resize control (rarely needed) * * @module @modelcontextprotocol/ext-apps/react @@ -32,3 +33,4 @@ */ export * from "./useApp"; export * from "./useAutoResize"; +export * from "./useHostStyles"; diff --git a/src/react/useHostStyles.ts b/src/react/useHostStyles.ts new file mode 100644 index 0000000..9fa7ad5 --- /dev/null +++ b/src/react/useHostStyles.ts @@ -0,0 +1,84 @@ +import { useEffect, useRef } from "react"; +import { App } from "../app"; +import { applyHostStyles } from "../styles"; +import { McpUiHostContext } from "../types"; + +/** + * React hook that applies host styles as CSS custom properties. + * + * This hook listens to host context changes and automatically applies the + * `styles` CSS variables to `document.documentElement`. This allows your + * app to use the host's theming values via CSS variables like + * `var(--color-background-primary)`. + * + * The hook also applies styles from the initial host context when the app + * first connects. + * + * @param app - The connected App instance, or null during initialization + * @param initialContext - Initial host context from the connection (optional). + * If provided, styles will be applied immediately on mount. + * + * @example Basic usage with useApp + * ```tsx + * import { useApp } from '@modelcontextprotocol/ext-apps/react'; + * import { useHostStyles } from '@modelcontextprotocol/ext-apps/react'; + * + * function MyApp() { + * const { app, isConnected } = useApp({ + * appInfo: { name: "MyApp", version: "1.0.0" }, + * capabilities: {}, + * }); + * + * // Automatically apply host styles as CSS variables + * useHostStyles(app); + * + * return ( + *
+ * Hello! + *
+ * ); + * } + * ``` + * + * @example With initial context + * ```tsx + * const [hostContext, setHostContext] = useState(null); + * + * // ... get initial context from app.connect() result + * + * useHostStyles(app, hostContext); + * ``` + * + * @see {@link applyHostStyles} for the underlying function + * @see {@link McpUiStyles} for available CSS variables + */ +export function useHostStyles( + app: App | null, + initialContext?: McpUiHostContext | null, +): void { + const initialStylesApplied = useRef(false); + + // Apply initial styles once on mount + useEffect(() => { + if (initialStylesApplied.current) { + return; + } + if (initialContext?.styles) { + applyHostStyles(initialContext.styles); + initialStylesApplied.current = true; + } + }, [initialContext]); + + // Listen for host context changes and apply updated styles + useEffect(() => { + if (!app) { + return; + } + + app.onhostcontextchanged = (params) => { + if (params.styles) { + applyHostStyles(params.styles); + } + }; + }, [app]); +} diff --git a/src/styles.ts b/src/styles.ts new file mode 100644 index 0000000..52ab6a1 --- /dev/null +++ b/src/styles.ts @@ -0,0 +1,41 @@ +import { McpUiStyles } from "./types"; + +/** + * Apply host styles as CSS custom properties on an element. + * + * This function takes the `styles` object from `McpUiHostContext` and sets + * each CSS variable on the specified root element (defaults to `document.documentElement`). + * This allows apps to use the host's theming values via CSS variables like + * `var(--color-background-primary)`. + * + * @param styles - The styles object from `McpUiHostContext.styles` + * @param root - The element to apply styles to (defaults to `document.documentElement`) + * + * @example Apply styles from host context + * ```typescript + * import { applyHostStyles } from '@modelcontextprotocol/ext-apps'; + * + * app.onhostcontextchanged = (params) => { + * if (params.styles) { + * applyHostStyles(params.styles); + * } + * }; + * ``` + * + * @example Apply to a specific element + * ```typescript + * const container = document.getElementById('app-root'); + * applyHostStyles(hostContext.styles, container); + * ``` + * + * @see {@link McpUiStyles} for the available CSS variables + * @see {@link McpUiHostContext} for the full host context structure + */ +export function applyHostStyles( + styles: McpUiStyles, + root: HTMLElement = document.documentElement, +): void { + for (const [key, value] of Object.entries(styles)) { + root.style.setProperty(key, value); + } +}