diff --git a/build.ts b/build.ts index d157b91..c32f431 100644 --- a/build.ts +++ b/build.ts @@ -25,45 +25,45 @@ async function buildJs( } await Promise.all([ - buildJs("src/index.ts", { outdir: "dist/src" }), - buildJs("src/react/useApp.tsx", { outdir: "dist/src/react" }), - buildJs("examples/example-ui-react.tsx", { outdir: "dist/examples" }), - buildJs("examples/example-ui.ts", { outdir: "dist/examples" }), - buildJs("examples/example-server.ts", { - target: "node", - outdir: "dist/examples", - }), + buildJs("src/app.ts"), + buildJs("src/react/index.tsx", { outdir: "dist/react" }), + // buildJs("examples/example-ui-react.tsx", { outdir: "dist/examples" }), + // buildJs("examples/example-ui.ts", { outdir: "dist/examples" }), + // buildJs("examples/example-server.ts", { + // target: "node", + // outdir: "dist/examples", + // }), ]); -async function inlineHtml(htmlFilePath: string, outputFilePath: string) { - let html = await readFile(htmlFilePath, "utf-8"); - // Find all src sources, then fetch them, then replace them - const files = html.matchAll( - / to prevent premature script tag closing - // This is the standard way to inline scripts safely - scriptContent = scriptContent.replace(/<\/script>/gi, "<\\/script>"); - return ``; - }, - ); - await writeFile(outputFilePath, html); -} +// async function inlineHtml(htmlFilePath: string, outputFilePath: string) { +// let html = await readFile(htmlFilePath, "utf-8"); +// // Find all src sources, then fetch them, then replace them +// const files = html.matchAll( +// / to prevent premature script tag closing +// // This is the standard way to inline scripts safely +// scriptContent = scriptContent.replace(/<\/script>/gi, "<\\/script>"); +// return ``; +// }, +// ); +// await writeFile(outputFilePath, html); +// } -await inlineHtml("examples/example-ui.html", "dist/examples/example-ui.html"); -await inlineHtml( - "examples/example-ui-react.html", - "dist/examples/example-ui-react.html", -); +// await inlineHtml("examples/example-ui.html", "dist/examples/example-ui.html"); +// await inlineHtml( +// "examples/example-ui-react.html", +// "dist/examples/example-ui-react.html", +// ); diff --git a/index.ts b/index.ts deleted file mode 100644 index ef907bf..0000000 --- a/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { App } from "./app"; -export { PostMessageTransport } from "./message-transport"; -export * from "./types"; diff --git a/package.json b/package.json index 14accbd..2cf7754 100644 --- a/package.json +++ b/package.json @@ -4,11 +4,16 @@ "version": "1.0.0", "description": "Hey", "type": "module", - "main": "./src/index.ts", + "main": "./dist/app.js", "exports": { - ".": "./src/index.ts", - "./types": "./src/types.ts", - "./react/useApp": "./src/react/useApp.tsx" + ".": { + "types": "./dist/app.d.ts", + "default": "./dist/app.js" + }, + "./react": { + "types": "./dist/react/index.d.ts", + "default": "./dist/react/index.js" + } }, "scripts": { "start:example-server0": "tsx examples/example-server.ts", diff --git a/react/useApp.tsx b/react/useApp.tsx deleted file mode 100644 index 43a8c3a..0000000 --- a/react/useApp.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { useEffect, useState } from "react"; -import { Implementation } from "@modelcontextprotocol/sdk/types.js"; -import { Client } from "@modelcontextprotocol/sdk/client"; -import { App, McpUiAppCapabilities, PostMessageTransport } from "../app"; -export * from "../app"; - -export interface UseAppOptions { - appInfo: Implementation; - capabilities: McpUiAppCapabilities; - /** - * Called after client is created but before connection. - * Use this to register request/notification handlers via - * client.setRequestHandler() and client.setNotificationHandler(). - */ - onAppCreated?: (app: App) => void; -} - -export interface AppState { - app: App | null; - isConnected: boolean; - error: Error | null; -} - -export function useApp({ - appInfo, - capabilities, - onAppCreated, -}: UseAppOptions): AppState { - const [app, setApp] = useState(null); - const [isConnected, setIsConnected] = useState(false); - const [error, setError] = useState(null); - - useEffect(() => { - let mounted = true; - - async function connect() { - try { - const transport = new PostMessageTransport(window.parent); - const app = new App(appInfo, capabilities); - - // Register handlers BEFORE connecting - onAppCreated?.(app); - - await app.connect(transport); - - if (mounted) { - setApp(app); - setIsConnected(true); - setError(null); - } - } catch (error) { - if (mounted) { - setApp(null); - setIsConnected(false); - setError( - error instanceof Error ? error : new Error("Failed to connect"), - ); - } - } - } - - connect(); - - return () => { - mounted = false; - }; - }, []); // Intentionally not including options to avoid reconnection - - return { app, isConnected, error }; -} diff --git a/react/useAutoResize.ts b/react/useAutoResize.ts deleted file mode 100644 index ce39c75..0000000 --- a/react/useAutoResize.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { useEffect, RefObject } from "react"; -import { App } from "../app"; - -/** - * Custom hook that automatically reports size changes to the parent window. - * - * @param client - MCP UI client for sending size notifications - */ -export function useAutoResize( - app: App | null, - elementRef?: RefObject, -) { - useEffect(() => { - if (!app) { - return; - } - - return app.setupSizeChangeNotifications(); - }, [app, elementRef]); -} diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index ef907bf..0000000 --- a/src/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { App } from "./app"; -export { PostMessageTransport } from "./message-transport"; -export * from "./types"; diff --git a/src/peer.ts_ b/src/peer.ts_ deleted file mode 100644 index 495b540..0000000 --- a/src/peer.ts_ +++ /dev/null @@ -1,351 +0,0 @@ -// import { -// z, -// ZodRawShape, -// ZodLiteral, -// ZodObject, -// ZodString, -// ZodTypeAny, -// ZodType, -// ZodTypeDef, -// ZodOptional, -// } from "zod"; -// import { Client } from "@modelcontextprotocol/sdk/client/index.js"; -// import { -// // ToolCallback, -// RegisteredTool, -// } from "@modelcontextprotocol/sdk/server/mcp.js"; -// import { Server } from "@modelcontextprotocol/sdk/server/index.js"; -// import { -// Protocol, -// type RequestOptions, -// type RequestHandlerExtra, -// } from "@modelcontextprotocol/sdk/shared/protocol.js"; - -// import { -// CallToolRequestSchema, -// CallToolResult, -// CallToolResultSchema, -// CancelledNotificationSchema, -// ClientCapabilities, -// ClientCapabilitiesSchema, -// Request, -// Implementation, -// JSONRPCError, -// JSONRPCMessage, -// JSONRPCMessageSchema, -// JSONRPCNotification, -// JSONRPCResponse, -// ListPromptsRequestSchema, -// ListPromptsResultSchema, -// ListResourcesRequestSchema, -// ListResourcesResultSchema, -// ListResourceTemplatesRequestSchema, -// ListResourceTemplatesResultSchema, -// LoggingMessageNotification, -// LoggingMessageNotificationSchema, -// MessageExtraInfo, -// Notification, -// PromptListChangedNotificationSchema, -// RequestId, -// ResourceListChangedNotificationSchema, -// Result, -// ServerCapabilities, -// ServerCapabilitiesSchema, -// ServerNotification, -// ServerRequest, -// ToolAnnotations, -// ToolListChangedNotificationSchema, -// } from "@modelcontextprotocol/sdk/types.js"; -// import { -// Transport, -// TransportSendOptions, -// } from "@modelcontextprotocol/sdk/shared/transport.js"; -// import { -// McpUiToolInputNotification, -// McpUiToolInputNotificationSchema, -// McpUiToolResultNotification, -// McpUiToolResultNotificationSchema, -// McpUiHostCapabilities, -// McpUiHostInfo, -// McpUiInitializedNotification, -// McpUiInitializeRequest, -// McpUiInitializeResultSchema, -// McpUiIntentRequest, -// McpUiIntentRequestSchema, -// McpUiIntentResultSchema, -// McpUiRenderDataRequest, -// McpUiRenderDataRequestSchema, -// McpUiMessageRequest, -// McpUiMessageRequestSchema, -// McpUiMessageResult, -// McpUiMessageResultSchema, -// McpUiOpenLinkRequest, -// McpUiOpenLinkRequestSchema, -// McpUiOpenLinkResultSchema, -// McpUiRenderDataResultSchema, -// McpUiSizeChangeNotification, -// McpUiSizeChangeNotificationSchema, -// } from "./types"; - -// export { PostMessageTransport } from "./message-transport.js"; - -// if (false) { -// let server: Server; -// let client: Client; -// } - -// const McpUiInitializeRequestSchema = z.object({ -// method: z.literal("ui/initialize"), -// params: z.object({ -// name: z.string(), -// version: z.string(), -// capabilities: ServerCapabilitiesSchema.extend( -// ClientCapabilitiesSchema.shape, -// ), -// }), -// }); - -// /** -// * Converts a provided Zod schema to a Zod object if it is a ZodRawShape, -// * otherwise returns the schema as is. -// */ -// function getZodSchemaObject( -// schema: ZodRawShape | ZodType | undefined, -// ): ZodType | undefined { -// if (!schema) { -// return undefined; -// } - -// if (isZodRawShape(schema)) { -// return z.object(schema); -// } - -// return schema; -// } - -// export class McpUiGuest extends Protocol { -// private _registeredHostTools: Record = {}; - -// assertCapabilityForMethod(method: Request["method"]): void {} -// assertRequestHandlerCapability(method: Request["method"]): void {} -// assertNotificationCapability(method: Notification["method"]): void {} - -// async callServerTool( -// name: string, -// argsSchema: ZodTypeAny, -// args: unknown, -// options?: RequestOptions, -// ) { -// //: Promise { -// } -// async addUiToChat( -// resourceUri: string, -// state: Record, -// ): Promise {} -// // In future, could expose callHostTool - -// registerHostTool< -// InputArgs extends ZodRawShape | ZodType, -// OutputArgs extends ZodRawShape | ZodType, -// >( -// name: string, -// config: { -// title?: string; -// description?: string; -// inputSchema?: InputArgs; -// outputSchema?: OutputArgs; -// annotations?: ToolAnnotations; -// _meta?: Record; -// }, -// cb: ToolCallback, -// ): RegisteredTool { -// if (this._registeredHostTools[name]) { -// throw new Error(`Tool ${name} is already registered on host`); -// } - -// const { -// title, -// description, -// inputSchema, -// outputSchema, -// annotations, -// _meta, -// } = config; - -// return this._createRegisteredTool( -// name, -// title, -// description, -// inputSchema, -// outputSchema, -// annotations, -// _meta, -// cb as ToolCallback, -// ); -// } - -// private _createRegisteredTool( -// name: string, -// title: string | undefined, -// description: string | undefined, -// inputSchema: ZodRawShape | ZodType | undefined, -// outputSchema: ZodRawShape | ZodType | undefined, -// annotations: ToolAnnotations | undefined, -// _meta: Record | undefined, -// callback: ToolCallback, -// ): RegisteredTool { -// const registeredTool: RegisteredTool = { -// title, -// description, -// inputSchema: getZodSchemaObject(inputSchema) as any, -// outputSchema: getZodSchemaObject(outputSchema) as any, -// annotations, -// _meta, -// callback, -// enabled: true, -// disable: () => registeredTool.update({ enabled: false }), -// enable: () => registeredTool.update({ enabled: true }), -// remove: () => registeredTool.update({ name: null }), -// update: (updates) => { -// if (typeof updates.name !== "undefined" && updates.name !== name) { -// delete this._registeredHostTools[name]; -// if (updates.name) -// this._registeredHostTools[updates.name] = registeredTool; -// } -// if (typeof updates.title !== "undefined") -// registeredTool.title = updates.title; -// if (typeof updates.description !== "undefined") -// registeredTool.description = updates.description; -// if (typeof updates.paramsSchema !== "undefined") -// registeredTool.inputSchema = z.object(updates.paramsSchema); -// if (typeof updates.callback !== "undefined") -// registeredTool.callback = updates.callback; -// if (typeof updates.annotations !== "undefined") -// registeredTool.annotations = updates.annotations; -// if (typeof updates._meta !== "undefined") -// registeredTool._meta = updates._meta; -// if (typeof updates.enabled !== "undefined") -// registeredTool.enabled = updates.enabled; -// // this.sendToolListChanged(); -// }, -// }; -// this._registeredHostTools[name] = registeredTool; - -// return registeredTool; -// } - -// sendMessage(params: McpUiMessageRequest["params"], options?: RequestOptions) { -// return this.request( -// { -// method: "ui/message", -// params, -// }, -// McpUiMessageResultSchema, -// options, -// ); -// } - -// sendLog(params: LoggingMessageNotification["params"]) { -// return this.notification({ -// method: "notifications/message", -// params, -// }); -// } - -// sendOpenLink( -// params: McpUiOpenLinkRequest["params"], -// options?: RequestOptions, -// ) { -// return this.request( -// { -// method: "ui/open-link", -// params, -// }, -// McpUiOpenLinkResultSchema, -// options, -// ); -// } - -// sendSizeChange(params: McpUiSizeChangeNotification["params"]) { -// return this.notification({ -// method: "ui/size-change", -// params, -// }); -// } - -// private _hostCapabilities?: McpUiHostCapabilities; -// private _hostInfo?: McpUiHostInfo; - -// override async connect( -// transport: Transport, -// options?: RequestOptions, -// ): Promise { -// await super.connect(transport); - -// try { -// const result = await this.request( -// { -// method: "ui/initialize", -// params: { -// // TODO: protocolVersion: LATEST_PROTOCOL_VERSION, -// }, -// }, -// McpUiInitializeResultSchema, -// options, -// ); - -// if (result === undefined) { -// throw new Error(`Server sent invalid initialize result: ${result}`); -// } - -// this._hostCapabilities = result.hostCapabilities; -// this._hostInfo = result.hostInfo; - -// await this.notification({ -// method: "ui/notifications/initialized", -// }); -// } catch (error) { -// // Disconnect if initialization fails. -// void this.close(); -// throw error; -// } -// } -// } - -// export type ToolCallback< -// Args extends undefined | ZodRawShape | ZodType = undefined, -// > = Args extends ZodRawShape -// ? ( -// args: z.objectOutputType, -// extra: RequestHandlerExtra, -// ) => CallToolResult | Promise -// : Args extends ZodType -// ? ( -// args: T, -// extra: RequestHandlerExtra, -// ) => CallToolResult | Promise -// : ( -// extra: RequestHandlerExtra, -// ) => CallToolResult | Promise; - -// // Helper to check if an object is a Zod schema (ZodRawShape) -// function isZodRawShape(obj: unknown): obj is ZodRawShape { -// if (typeof obj !== "object" || obj === null) return false; - -// const isEmptyObject = Object.keys(obj).length === 0; - -// // Check if object is empty or at least one property is a ZodType instance -// // Note: use heuristic check to avoid instanceof failure across different Zod versions -// return isEmptyObject || Object.values(obj as object).some(isZodTypeLike); -// } - -// function isZodTypeLike(value: unknown): value is ZodType { -// return ( -// value !== null && -// typeof value === "object" && -// "parse" in value && -// typeof value.parse === "function" && -// "safeParse" in value && -// typeof value.safeParse === "function" -// ); -// } diff --git a/src/react/index.tsx b/src/react/index.tsx new file mode 100644 index 0000000..05abb11 --- /dev/null +++ b/src/react/index.tsx @@ -0,0 +1,2 @@ +export * from "./useApp"; +export * from "./useAutoResize"; \ No newline at end of file