@leanmcp/ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,604 @@
1
+ import React, { ReactNode, ButtonHTMLAttributes, HTMLAttributes, InputHTMLAttributes } from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+ import { McpUiAppCapabilities, App, McpUiHostContext } from '@modelcontextprotocol/ext-apps';
4
+ export { App, McpUiAppCapabilities, McpUiHostContext, McpUiToolInputNotification, McpUiToolInputPartialNotification, McpUiToolResultNotification } from '@modelcontextprotocol/ext-apps';
5
+ import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
6
+ import { ChartData, ChartType as ChartType$1, ChartOptions } from 'chart.js';
7
+
8
+ /**
9
+ * @UIApp Decorator
10
+ *
11
+ * Links an MCP tool to a React UI component.
12
+ * When applied to a tool method, it:
13
+ * 1. Adds _meta.ui/resourceUri to the tool definition
14
+ * 2. Auto-registers a resource that renders the component to HTML
15
+ *
16
+ * This decorator is designed to work with @leanmcp/core decorators.
17
+ */
18
+
19
+ /**
20
+ * Options for @UIApp decorator
21
+ */
22
+ interface UIAppOptions {
23
+ /** React component to render for this tool's UI */
24
+ component: React.ComponentType<any>;
25
+ /** Custom resource URI (auto-generated if not provided) */
26
+ uri?: string;
27
+ /** HTML document title */
28
+ title?: string;
29
+ /** Additional CSS styles */
30
+ styles?: string;
31
+ }
32
+ /**
33
+ * Decorator that links a tool to a UI component.
34
+ *
35
+ * When the tool is called, the host will fetch the UI resource
36
+ * which returns the component rendered as HTML.
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * import { Tool } from '@leanmcp/core';
41
+ * import { UIApp } from '@leanmcp/ui';
42
+ * import { WeatherCard } from './WeatherCard';
43
+ *
44
+ * class WeatherService {
45
+ * @Tool({ description: 'Get weather for a city' })
46
+ * @UIApp({ component: WeatherCard })
47
+ * async getWeather(args: { city: string }) {
48
+ * return { city: args.city, temp: 22 };
49
+ * }
50
+ * }
51
+ * ```
52
+ */
53
+ declare function UIApp(options: UIAppOptions): MethodDecorator;
54
+ /**
55
+ * Helper to get UIApp metadata from a method
56
+ */
57
+ declare function getUIAppMetadata(target: Function): UIAppOptions | undefined;
58
+ /**
59
+ * Helper to get the resource URI from a method
60
+ */
61
+ declare function getUIAppUri(target: Function): string | undefined;
62
+
63
+ /**
64
+ * App information (same as ext-apps Implementation)
65
+ */
66
+ interface AppInfo {
67
+ name: string;
68
+ version: string;
69
+ }
70
+ /**
71
+ * Options for App behavior
72
+ */
73
+ interface AppOptions {
74
+ /**
75
+ * Automatically report size changes to the host using ResizeObserver.
76
+ * @default true
77
+ */
78
+ autoResize?: boolean;
79
+ }
80
+ /**
81
+ * MCP App context value - available via useMcpApp()
82
+ */
83
+ interface McpAppContextValue {
84
+ /** The ext-apps App instance */
85
+ app: App | null;
86
+ /** Whether connected to host */
87
+ isConnected: boolean;
88
+ /** Connection error, if any */
89
+ error: Error | null;
90
+ /** Host context (theme, viewport, etc.) */
91
+ hostContext: McpUiHostContext;
92
+ /** Tool input arguments received from host */
93
+ toolInput: Record<string, unknown> | null;
94
+ /** Partial tool input (streaming) */
95
+ toolInputPartial: Record<string, unknown> | null;
96
+ /** Tool result received from host */
97
+ toolResult: CallToolResult | null;
98
+ /** Call a server tool */
99
+ callTool: (name: string, args?: Record<string, unknown>) => Promise<CallToolResult>;
100
+ /** Send a message to the host chat */
101
+ sendMessage: (text: string) => Promise<void>;
102
+ /** Send a log message */
103
+ sendLog: (level: 'debug' | 'info' | 'warning' | 'error', data: unknown) => Promise<void>;
104
+ /** Open a link in the host */
105
+ openLink: (url: string) => Promise<void>;
106
+ }
107
+ interface AppProviderProps {
108
+ /** App name and version */
109
+ appInfo: AppInfo;
110
+ /** App capabilities (tools, experimental features) */
111
+ capabilities?: McpUiAppCapabilities;
112
+ /** App options (autoResize, etc.) */
113
+ options?: AppOptions;
114
+ /** React children */
115
+ children: ReactNode;
116
+ }
117
+ /**
118
+ * AppProvider - Root context for MCP Apps
119
+ *
120
+ * Provides connection management and hooks for MCP App components.
121
+ * Uses PostMessage transport for iframe communication.
122
+ *
123
+ * @example
124
+ * ```tsx
125
+ * function MyApp() {
126
+ * return (
127
+ * <AppProvider
128
+ * appInfo={{ name: "MyApp", version: "1.0.0" }}
129
+ * capabilities={{ tools: { listChanged: true } }}
130
+ * options={{ autoResize: true }}
131
+ * >
132
+ * <MyContent />
133
+ * </AppProvider>
134
+ * );
135
+ * }
136
+ * ```
137
+ */
138
+ declare function AppProvider({ appInfo, capabilities, options, children }: AppProviderProps): react_jsx_runtime.JSX.Element;
139
+ /**
140
+ * Hook to access the MCP App context
141
+ * Returns SSR-safe defaults when no provider is available (during server rendering)
142
+ */
143
+ declare function useMcpApp(): McpAppContextValue;
144
+
145
+ interface UseToolResult<T = unknown> {
146
+ /** Call the tool with arguments */
147
+ call: (args?: Record<string, unknown>) => Promise<T>;
148
+ /** Whether the tool is currently being called */
149
+ loading: boolean;
150
+ /** The result of the last tool call */
151
+ result: T | null;
152
+ /** Error from the last tool call */
153
+ error: Error | null;
154
+ /** Reset the result and error state */
155
+ reset: () => void;
156
+ }
157
+ /**
158
+ * Hook for calling an MCP tool with loading and error state
159
+ *
160
+ * @example
161
+ * ```tsx
162
+ * function WeatherWidget() {
163
+ * const { call, loading, result, error } = useTool<WeatherData>('get-weather');
164
+ *
165
+ * return (
166
+ * <div>
167
+ * <button onClick={() => call({ city: 'London' })} disabled={loading}>
168
+ * {loading ? 'Loading...' : 'Get Weather'}
169
+ * </button>
170
+ * {result && <div>Temperature: {result.temperature}°C</div>}
171
+ * {error && <div>Error: {error.message}</div>}
172
+ * </div>
173
+ * );
174
+ * }
175
+ * ```
176
+ */
177
+ declare function useTool<T = unknown>(toolName: string): UseToolResult<T>;
178
+
179
+ interface UseToolResultReturn<T = unknown> {
180
+ /** The tool result, typed as T */
181
+ result: T | null;
182
+ /** Raw CallToolResult from MCP */
183
+ rawResult: CallToolResult | null;
184
+ /** Whether a result has been received */
185
+ hasResult: boolean;
186
+ /** Extract text content from result */
187
+ textContent: string | null;
188
+ }
189
+ /**
190
+ * Hook to access tool result from host
191
+ *
192
+ * @example
193
+ * ```tsx
194
+ * function WeatherCard() {
195
+ * const { result } = useToolResult<{ city: string; temp: number }>();
196
+ *
197
+ * if (!result) return <div>Loading...</div>;
198
+ *
199
+ * return <div>{result.city}: {result.temp}°C</div>;
200
+ * }
201
+ * ```
202
+ */
203
+ declare function useToolResult<T = unknown>(): UseToolResultReturn<T>;
204
+
205
+ interface UseToolInputReturn<T = Record<string, unknown>> {
206
+ /** The tool input arguments, typed as T */
207
+ input: T | null;
208
+ /** Whether input has been received */
209
+ hasInput: boolean;
210
+ }
211
+ /**
212
+ * Hook to access tool input arguments from host
213
+ *
214
+ * @example
215
+ * ```tsx
216
+ * function WeatherCard() {
217
+ * const { input } = useToolInput<{ city: string }>();
218
+ *
219
+ * if (!input) return <div>Loading...</div>;
220
+ *
221
+ * return <div>Weather for: {input.city}</div>;
222
+ * }
223
+ * ```
224
+ */
225
+ declare function useToolInput<T = Record<string, unknown>>(): UseToolInputReturn<T>;
226
+
227
+ interface UseToolInputPartialReturn {
228
+ /** Partial arguments currently available */
229
+ partialArgs: Record<string, unknown> | null;
230
+ /** Whether partial args are being received */
231
+ isStreaming: boolean;
232
+ }
233
+ /**
234
+ * Hook to access streaming partial tool input
235
+ *
236
+ * @example
237
+ * ```tsx
238
+ * function MyComponent() {
239
+ * const { partialArgs, isStreaming } = useToolInputPartial();
240
+ *
241
+ * if (isStreaming) {
242
+ * return <div>Receiving: {JSON.stringify(partialArgs)}</div>;
243
+ * }
244
+ * return <div>Ready for input</div>;
245
+ * }
246
+ * ```
247
+ */
248
+ declare function useToolInputPartial(): UseToolInputPartialReturn;
249
+
250
+ interface UseHostContextReturn {
251
+ /** Current theme: 'light' or 'dark' */
252
+ theme: 'light' | 'dark';
253
+ /** Display mode: 'inline', 'fullscreen', 'pip' */
254
+ displayMode: 'inline' | 'fullscreen' | 'pip';
255
+ /** Viewport dimensions */
256
+ viewport: {
257
+ width: number;
258
+ height: number;
259
+ } | null;
260
+ /** User locale (e.g., 'en-US') */
261
+ locale: string | null;
262
+ /** User timezone (e.g., 'America/New_York') */
263
+ timeZone: string | null;
264
+ /** Platform type */
265
+ platform: 'web' | 'desktop' | 'mobile' | null;
266
+ /** Full raw context */
267
+ rawContext: McpUiHostContext;
268
+ }
269
+ /**
270
+ * Hook to access host context
271
+ *
272
+ * @example
273
+ * ```tsx
274
+ * function ThemedCard() {
275
+ * const { theme, locale } = useHostContext();
276
+ *
277
+ * return (
278
+ * <div className={theme === 'dark' ? 'dark-mode' : 'light-mode'}>
279
+ * Locale: {locale}
280
+ * </div>
281
+ * );
282
+ * }
283
+ * ```
284
+ */
285
+ declare function useHostContext(): UseHostContextReturn;
286
+
287
+ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
288
+ /** Button variant */
289
+ variant?: 'primary' | 'secondary' | 'ghost' | 'destructive' | 'outline';
290
+ /** Button size */
291
+ size?: 'sm' | 'md' | 'lg' | 'icon';
292
+ /** Loading state */
293
+ loading?: boolean;
294
+ /** Render as child component (Radix Slot) */
295
+ asChild?: boolean;
296
+ /** Left icon */
297
+ leftIcon?: ReactNode;
298
+ /** Right icon */
299
+ rightIcon?: ReactNode;
300
+ }
301
+ /**
302
+ * Button component with multiple variants and loading state
303
+ *
304
+ * @example
305
+ * ```tsx
306
+ * <Button variant="primary" onClick={handleClick}>
307
+ * Click Me
308
+ * </Button>
309
+ *
310
+ * <Button variant="ghost" loading>
311
+ * Loading...
312
+ * </Button>
313
+ * ```
314
+ */
315
+ declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
316
+
317
+ interface ActionButtonProps extends Omit<ButtonProps, 'onClick' | 'loading' | 'onError'> {
318
+ /** Tool name to call */
319
+ toolName: string;
320
+ /** Arguments to pass to the tool */
321
+ toolArgs?: Record<string, unknown>;
322
+ /** Callback when tool call succeeds */
323
+ onToolSuccess?: (result: unknown) => void;
324
+ /** Callback when tool call fails */
325
+ onToolError?: (error: Error) => void;
326
+ /** Show result inline after success */
327
+ showResult?: boolean;
328
+ /** Custom result renderer */
329
+ renderResult?: (result: unknown) => ReactNode;
330
+ }
331
+ /**
332
+ * ActionButton - Button that calls an MCP tool with loading state
333
+ *
334
+ * @example
335
+ * ```tsx
336
+ * <ActionButton
337
+ * toolName="get-weather"
338
+ * toolArgs={{ city: 'London' }}
339
+ * onToolSuccess={(data) => console.log(data)}
340
+ * >
341
+ * Get Weather
342
+ * </ActionButton>
343
+ * ```
344
+ */
345
+ declare function ActionButton({ toolName, toolArgs, onToolSuccess, onToolError, showResult, renderResult, children, ...buttonProps }: ActionButtonProps): react_jsx_runtime.JSX.Element;
346
+
347
+ interface ToolFormField {
348
+ /** Field name (matches tool input schema) */
349
+ name: string;
350
+ /** Display label */
351
+ label: string;
352
+ /** Input type */
353
+ type?: 'text' | 'number' | 'email' | 'password' | 'textarea';
354
+ /** Placeholder text */
355
+ placeholder?: string;
356
+ /** Default value */
357
+ defaultValue?: string | number;
358
+ /** Required field */
359
+ required?: boolean;
360
+ /** Helper text */
361
+ helperText?: string;
362
+ }
363
+ interface ToolFormProps {
364
+ /** Tool name to call on submit */
365
+ toolName: string;
366
+ /** Form fields */
367
+ fields: ToolFormField[];
368
+ /** Submit button text */
369
+ submitText?: string;
370
+ /** Callback when tool call succeeds */
371
+ onSuccess?: (result: unknown) => void;
372
+ /** Callback when tool call fails */
373
+ onError?: (error: Error) => void;
374
+ /** Show result after success */
375
+ showResult?: boolean;
376
+ /** Additional class name */
377
+ className?: string;
378
+ }
379
+ /**
380
+ * ToolForm - Auto-generates a form that submits to an MCP tool
381
+ *
382
+ * @example
383
+ * ```tsx
384
+ * <ToolForm
385
+ * toolName="create-user"
386
+ * fields={[
387
+ * { name: 'name', label: 'Name', required: true },
388
+ * { name: 'email', label: 'Email', type: 'email', required: true },
389
+ * ]}
390
+ * submitText="Create User"
391
+ * onSuccess={(user) => console.log('Created:', user)}
392
+ * />
393
+ * ```
394
+ */
395
+ declare function ToolForm({ toolName, fields, submitText, onSuccess, onError, showResult, className, }: ToolFormProps): react_jsx_runtime.JSX.Element;
396
+
397
+ interface CardProps extends HTMLAttributes<HTMLDivElement> {
398
+ /** Card variant */
399
+ variant?: 'default' | 'outline' | 'elevated';
400
+ /** Padding size */
401
+ padding?: 'none' | 'sm' | 'md' | 'lg';
402
+ /** Make card interactive (hover effects) */
403
+ interactive?: boolean;
404
+ }
405
+ /**
406
+ * Card container component
407
+ */
408
+ declare const Card: React.ForwardRefExoticComponent<CardProps & React.RefAttributes<HTMLDivElement>>;
409
+ interface CardHeaderProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
410
+ /** Header title */
411
+ title?: ReactNode;
412
+ /** Header description */
413
+ description?: ReactNode;
414
+ /** Right-side action */
415
+ action?: ReactNode;
416
+ }
417
+ declare const CardHeader: React.ForwardRefExoticComponent<CardHeaderProps & React.RefAttributes<HTMLDivElement>>;
418
+ interface CardContentProps extends HTMLAttributes<HTMLDivElement> {
419
+ }
420
+ declare const CardContent: React.ForwardRefExoticComponent<CardContentProps & React.RefAttributes<HTMLDivElement>>;
421
+ interface CardFooterProps extends HTMLAttributes<HTMLDivElement> {
422
+ }
423
+ declare const CardFooter: React.ForwardRefExoticComponent<CardFooterProps & React.RefAttributes<HTMLDivElement>>;
424
+
425
+ interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
426
+ /** Input label */
427
+ label?: string;
428
+ /** Helper text */
429
+ helperText?: string;
430
+ /** Error message */
431
+ error?: string;
432
+ /** Input size */
433
+ size?: 'sm' | 'md' | 'lg';
434
+ /** Left icon or element */
435
+ leftElement?: ReactNode;
436
+ /** Right icon or element */
437
+ rightElement?: ReactNode;
438
+ /** Full width */
439
+ fullWidth?: boolean;
440
+ }
441
+ /**
442
+ * Input component with label, validation, and icon support
443
+ *
444
+ * @example
445
+ * ```tsx
446
+ * <Input
447
+ * label="Email"
448
+ * type="email"
449
+ * placeholder="Enter your email"
450
+ * error={errors.email}
451
+ * />
452
+ * ```
453
+ */
454
+ declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
455
+
456
+ interface DataGridColumn<T> {
457
+ /** Column key (accessor) */
458
+ key: keyof T | string;
459
+ /** Column header */
460
+ header: string;
461
+ /** Custom cell renderer */
462
+ cell?: (value: unknown, row: T) => ReactNode;
463
+ /** Enable sorting */
464
+ sortable?: boolean;
465
+ /** Column width */
466
+ width?: number | string;
467
+ }
468
+ interface DataGridProps<T extends Record<string, unknown>> {
469
+ /** Data array */
470
+ data: T[];
471
+ /** Column definitions */
472
+ columns: DataGridColumn<T>[];
473
+ /** Enable global search */
474
+ searchable?: boolean;
475
+ /** Search placeholder */
476
+ searchPlaceholder?: string;
477
+ /** Row click handler */
478
+ onRowClick?: (row: T) => void;
479
+ /** Loading state */
480
+ loading?: boolean;
481
+ /** Empty state message */
482
+ emptyMessage?: string;
483
+ /** Additional class name */
484
+ className?: string;
485
+ }
486
+ /**
487
+ * DataGrid - Sortable, filterable data table for tool results
488
+ *
489
+ * @example
490
+ * ```tsx
491
+ * const { result } = useTool<{ items: Item[] }>('get-items');
492
+ *
493
+ * <DataGrid
494
+ * data={result?.items ?? []}
495
+ * columns={[
496
+ * { key: 'name', header: 'Name', sortable: true },
497
+ * { key: 'price', header: 'Price', cell: (v) => `$${v}` },
498
+ * ]}
499
+ * searchable
500
+ * />
501
+ * ```
502
+ */
503
+ declare function DataGrid<T extends Record<string, unknown>>({ data, columns, searchable, searchPlaceholder, onRowClick, loading, emptyMessage, className, }: DataGridProps<T>): react_jsx_runtime.JSX.Element;
504
+
505
+ type ChartType = 'line' | 'bar' | 'pie' | 'doughnut';
506
+ interface ChartProps {
507
+ /** Chart type */
508
+ type: ChartType;
509
+ /** Chart data */
510
+ data: ChartData<ChartType$1>;
511
+ /** Chart options */
512
+ options?: ChartOptions<ChartType$1>;
513
+ /** Chart height */
514
+ height?: number | string;
515
+ /** Chart width */
516
+ width?: number | string;
517
+ /** Additional class name */
518
+ className?: string;
519
+ }
520
+ /**
521
+ * Chart component for data visualization
522
+ *
523
+ * @example
524
+ * ```tsx
525
+ * const { result } = useTool<{ prices: number[], labels: string[] }>('get-stock-data');
526
+ *
527
+ * <Chart
528
+ * type="line"
529
+ * data={{
530
+ * labels: result?.labels ?? [],
531
+ * datasets: [{
532
+ * label: 'Price',
533
+ * data: result?.prices ?? [],
534
+ * borderColor: '#6366f1',
535
+ * tension: 0.4,
536
+ * }],
537
+ * }}
538
+ * height={300}
539
+ * />
540
+ * ```
541
+ */
542
+ declare function Chart({ type, data, options, height, width, className, }: ChartProps): react_jsx_runtime.JSX.Element;
543
+
544
+ interface AppShellProps extends HTMLAttributes<HTMLDivElement> {
545
+ /** Header content */
546
+ header?: ReactNode;
547
+ /** Sidebar content */
548
+ sidebar?: ReactNode;
549
+ /** Footer content */
550
+ footer?: ReactNode;
551
+ /** Sidebar position */
552
+ sidebarPosition?: 'left' | 'right';
553
+ /** Sidebar width */
554
+ sidebarWidth?: number | string;
555
+ /** Enable auto-resize to content */
556
+ autoResize?: boolean;
557
+ /** Padding */
558
+ padding?: 'none' | 'sm' | 'md' | 'lg';
559
+ }
560
+ /**
561
+ * AppShell - Root layout container for MCP Apps
562
+ *
563
+ * Provides header, sidebar, main content, and footer areas with auto-resize support.
564
+ *
565
+ * @example
566
+ * ```tsx
567
+ * <AppShell
568
+ * header={<h1>My App</h1>}
569
+ * sidebar={<Navigation />}
570
+ * autoResize
571
+ * >
572
+ * <MainContent />
573
+ * </AppShell>
574
+ * ```
575
+ */
576
+ declare function AppShell({ header, sidebar, footer, sidebarPosition, sidebarWidth, autoResize, padding, className, children, ...props }: AppShellProps): react_jsx_runtime.JSX.Element;
577
+
578
+ interface CodeBlockProps {
579
+ /** Code content */
580
+ code: string;
581
+ /** Programming language */
582
+ language?: string;
583
+ /** Show line numbers */
584
+ showLineNumbers?: boolean;
585
+ /** Enable copy button */
586
+ copyable?: boolean;
587
+ /** Additional class name */
588
+ className?: string;
589
+ }
590
+ /**
591
+ * CodeBlock - Syntax highlighted code display
592
+ *
593
+ * @example
594
+ * ```tsx
595
+ * <CodeBlock
596
+ * code={`const greeting = "Hello, World!";`}
597
+ * language="javascript"
598
+ * copyable
599
+ * />
600
+ * ```
601
+ */
602
+ declare function CodeBlock({ code, language, showLineNumbers, copyable, className, }: CodeBlockProps): react_jsx_runtime.JSX.Element;
603
+
604
+ export { ActionButton, type ActionButtonProps, type AppInfo, type AppOptions, AppProvider, type AppProviderProps, AppShell, type AppShellProps, Button, type ButtonProps, Card, CardContent, type CardContentProps, CardFooter, type CardFooterProps, CardHeader, type CardHeaderProps, type CardProps, Chart, type ChartProps, type ChartType, CodeBlock, type CodeBlockProps, DataGrid, type DataGridColumn, type DataGridProps, Input, type InputProps, type McpAppContextValue, ToolForm, type ToolFormField, type ToolFormProps, UIApp, type UIAppOptions, type UseHostContextReturn, type UseToolInputPartialReturn, type UseToolInputReturn, type UseToolResult, type UseToolResultReturn, getUIAppMetadata, getUIAppUri, useHostContext, useMcpApp, useTool, useToolInput, useToolInputPartial, useToolResult };