* feat(app,app-bridge): guard requests sent before handshake completes Calling host-bound methods before the ui/initialize → ui/notifications/initialized handshake completes can race the handshake on strict hosts and leave the iframe permanently hidden. App (View side): - All 8 host-bound methods (callServerTool, readServerResource, listServerResources, sendMessage, updateModelContext, openLink, downloadFile, requestDisplayMode) now check that connect() has sent the initialized notification. - New AppOptions.strict (default false): console.error when false, throw when true. AppOptions is now exported. - useApp() forwards strict to the App constructor. - Flag resets on reconnect. AppBridge (Host side): - replaceRequestHandler is overridden to wrap every host-bound handler with a console.warn if the request arrives before ui/notifications/initialized. Never throws. ui/initialize and ping use setRequestHandler directly and are exempt; notifications are unaffected. - Catches Views that hand-roll postMessage without the SDK. - Flag resets on reconnect. Refs anthropics/claude-ai-mcp#61, anthropics/claude-ai-mcp#149. * fix(docs): drop AppOptions from intentionallyNotExported now that it is exported
This commit is contained in:
parent
739c0e049d
commit
94c94216d4
1 changed files with 200 additions and 9 deletions
|
|
@ -1,20 +1,25 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { Implementation } from "@modelcontextprotocol/sdk/types.js";
|
||||
import { Client } from "@modelcontextprotocol/sdk/client";
|
||||
import { App, McpUiAppCapabilities, PostMessageTransport } from "../app";
|
||||
import {
|
||||
App,
|
||||
AppOptions,
|
||||
McpUiAppCapabilities,
|
||||
PostMessageTransport,
|
||||
} from "../app";
|
||||
export * from "../app";
|
||||
|
||||
/**
|
||||
* Options for configuring the {@link useApp `useApp`} hook.
|
||||
*
|
||||
* Note: This interface does NOT expose {@link App `App`} options like `autoResize`.
|
||||
* The hook creates the `App` with default options (`autoResize: true`). If you
|
||||
* need custom `App` options, create the `App` manually instead of using this hook.
|
||||
* The `strict` option is forwarded to the underlying {@link App `App`}
|
||||
* instance. For other {@link AppOptions `AppOptions`}, create the `App`
|
||||
* manually instead of using this hook.
|
||||
*
|
||||
* @see {@link useApp `useApp`} for the hook that uses these options
|
||||
* @see {@link useAutoResize `useAutoResize`} for manual auto-resize control with custom `App` options
|
||||
*/
|
||||
export interface UseAppOptions {
|
||||
export interface UseAppOptions extends Pick<AppOptions, "strict"> {
|
||||
/** App identification (name and version) */
|
||||
appInfo: Implementation;
|
||||
/**
|
||||
|
|
@ -121,6 +126,7 @@ export function useApp({
|
|||
appInfo,
|
||||
capabilities,
|
||||
onAppCreated,
|
||||
strict,
|
||||
}: UseAppOptions): AppState {
|
||||
const [app, setApp] = useState<App | null>(null);
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
|
|
@ -135,7 +141,10 @@ export function useApp({
|
|||
window.parent,
|
||||
window.parent,
|
||||
);
|
||||
const app = new App(appInfo, capabilities);
|
||||
const app = new App(appInfo, capabilities, {
|
||||
autoResize: true,
|
||||
strict,
|
||||
});
|
||||
|
||||
// Register handlers BEFORE connecting
|
||||
onAppCreated?.(app);
|
||||
|
|
|
|||
Loading…
Reference in a new issue