@leanmcp/ui 0.3.2 → 0.3.4

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.
package/README.md CHANGED
@@ -1,285 +1,285 @@
1
- <p align="center">
2
- <img
3
- src="https://raw.githubusercontent.com/LeanMCP/leanmcp-sdk/refs/heads/main/assets/logo.svg"
4
- alt="LeanMCP Logo"
5
- width="400"
6
- />
7
- </p>
8
-
9
- <p align="center">
10
- <strong>@leanmcp/ui</strong><br/>
11
- MCP-Native UI SDK for React — Build rich, interactive MCP Apps with first-class tool integration.
12
- </p>
13
-
14
- <p align="center">
15
- <a href="https://www.npmjs.com/package/@leanmcp/ui">
16
- <img src="https://img.shields.io/npm/v/@leanmcp/ui" alt="npm version" />
17
- </a>
18
- <a href="https://www.npmjs.com/package/@leanmcp/ui">
19
- <img src="https://img.shields.io/npm/dm/@leanmcp/ui" alt="npm downloads" />
20
- </a>
21
- <a href="https://docs.leanmcp.com/sdk/ui">
22
- <img src="https://img.shields.io/badge/Docs-leanmcp-0A66C2?" />
23
- </a>
24
- <a href="https://discord.com/invite/DsRcA3GwPy">
25
- <img src="https://img.shields.io/badge/Discord-Join-5865F2?logo=discord&logoColor=white" />
26
- </a>
27
- <a href="https://x.com/LeanMcp">
28
- <img src="https://img.shields.io/badge/@LeanMCP-f5f5f5?logo=x&logoColor=000000" />
29
- </a>
30
- </p>
31
-
32
- ## Features
33
-
34
- - **MCP-Native Components** — ToolButton, ToolSelect, ToolForm, ToolDataGrid, and more
35
- - **First-Class Tool Integration** — Components that natively call MCP tools
36
- - **ChatGPT Apps Support** — Build apps that work inside ChatGPT with `@GPTApp`
37
- - **Streaming Support** — Handle partial/streaming tool responses
38
- - **Theming** — Automatic host theme adaptation (light/dark)
39
- - **Testing Utilities** — `MockAppProvider` for unit testing
40
-
41
- ## Installation
42
-
43
- ```bash
44
- npm install @leanmcp/ui
45
- ```
46
-
47
- ## Quick Start
48
-
49
- ```tsx
50
- import { AppProvider, ToolButton } from '@leanmcp/ui';
51
- import '@leanmcp/ui/styles.css';
52
-
53
- function MyApp() {
54
- return (
55
- <AppProvider appInfo={{ name: 'MyApp', version: '1.0.0' }}>
56
- <ToolButton tool="refresh-data" resultDisplay="toast">
57
- Refresh
58
- </ToolButton>
59
- </AppProvider>
60
- );
61
- }
62
- ```
63
-
64
- ## Components
65
-
66
- ### MCP-Native Components
67
-
68
- | Component | Description |
69
- |-----------|-------------|
70
- | `ToolButton` | Button with tool execution, confirmation, result display |
71
- | `ToolSelect` | Select with tool-based options and selection callbacks |
72
- | `ToolInput` | Input with debounced search and autocomplete |
73
- | `ToolForm` | Form with multiple field types (text, select, checkbox, slider) |
74
- | `ToolDataGrid` | Table with server-side pagination, sorting, row actions |
75
- | `ResourceView` | Display MCP server resources with auto-refresh |
76
- | `StreamingContent` | Render streaming/partial tool data |
77
-
78
- ### Utility Components
79
-
80
- | Component | Description |
81
- |-----------|-------------|
82
- | `RequireConnection` | Guard wrapper for MCP connection state |
83
- | `ToolErrorBoundary` | Error boundary with retry for tool errors |
84
- | `ToolProvider` | Scoped configuration context |
85
-
86
- ## Hooks
87
-
88
- | Hook | Description |
89
- |------|-------------|
90
- | `useTool` | Call tools with retry, abort, transformation |
91
- | `useToolStream` | Handle streaming tool input |
92
- | `useResource` | Read MCP resources with auto-refresh |
93
- | `useMessage` | Send messages to host chat |
94
- | `useHostContext` | Access host theme and viewport |
95
-
96
- ### GPT Apps SDK Hooks
97
-
98
- These hooks provide access to the ChatGPT Apps SDK globals (compatible with OpenAI's `window.openai` API):
99
-
100
- | Hook | Description |
101
- |------|-------------|
102
- | `useToolOutput` | Access `structuredContent` from the tool response |
103
- | `useToolInput` | Access input arguments passed to the tool |
104
- | `useWidgetState` | Read/write persistent widget state across sessions |
105
- | `useToolResponseMetadata` | Access `_meta` from the tool response |
106
- | `useOpenAiGlobal` | Low-level hook to subscribe to any `window.openai` property |
107
-
108
- ## Examples
109
-
110
- ### ToolButton with Confirmation
111
-
112
- ```tsx
113
- <ToolButton
114
- tool="delete-item"
115
- args={{ id: item.id }}
116
- confirm={{
117
- title: 'Delete Item?',
118
- description: 'This cannot be undone.'
119
- }}
120
- variant="destructive"
121
- >
122
- Delete
123
- </ToolButton>
124
- ```
125
-
126
- ### ToolSelect with Dynamic Options
127
-
128
- ```tsx
129
- <ToolSelect
130
- optionsTool="list-categories"
131
- transformOptions={(r) => r.categories.map(c => ({
132
- value: c.id,
133
- label: c.name
134
- }))}
135
- onSelectTool="set-category"
136
- argName="categoryId"
137
- />
138
- ```
139
-
140
- ### ToolDataGrid
141
-
142
- ```tsx
143
- <ToolDataGrid
144
- dataTool="list-users"
145
- columns={[
146
- { key: 'name', header: 'Name', sortable: true },
147
- { key: 'email', header: 'Email' },
148
- { key: 'status', header: 'Status' }
149
- ]}
150
- transformData={(r) => ({ rows: r.users, total: r.total })}
151
- rowActions={[
152
- { label: 'Edit', tool: 'edit-user' }
153
- ]}
154
- pagination
155
- />
156
- ```
157
-
158
- ### ToolForm
159
-
160
- ```tsx
161
- <ToolForm
162
- toolName="create-item"
163
- fields={[
164
- { name: 'title', label: 'Title', required: true },
165
- { name: 'priority', label: 'Priority', type: 'select',
166
- options: [
167
- { value: 'low', label: 'Low' },
168
- { value: 'high', label: 'High' }
169
- ]
170
- },
171
- { name: 'notify', label: 'Send notifications', type: 'switch' }
172
- ]}
173
- showSuccessToast
174
- />
175
- ```
176
-
177
- ## Server-Side Integration
178
-
179
- Use `@UIApp` decorator to register your React component as an MCP resource.
180
-
181
- > **Note:** Use a relative path string for the `component` property, not an imported component. This avoids importing React components on the server side.
182
-
183
- ```typescript
184
- // mcp/dashboard/index.ts
185
- import { UIApp } from '@leanmcp/core';
186
-
187
- export class DashboardService {
188
- @UIApp({
189
- component: './Dashboard', // Path relative to this file
190
- name: 'dashboard',
191
- title: 'Analytics Dashboard'
192
- })
193
- dashboard() {}
194
- }
195
- ```
196
-
197
- ## ChatGPT Integration
198
-
199
- Use `@GPTApp` for ChatGPT-specific apps:
200
-
201
- ```typescript
202
- import { GPTApp } from '@leanmcp/ui';
203
-
204
- export class SlackService {
205
- @GPTApp({
206
- component: './SlackApp', // Path relative to this file
207
- name: 'slack-composer'
208
- })
209
- slackComposer() {}
210
- }
211
- ```
212
-
213
- ### GPT Apps SDK Hooks Usage
214
-
215
- Access tool output (structuredContent) without making additional API calls:
216
-
217
- ```tsx
218
- import { useToolOutput, useWidgetState } from '@leanmcp/ui';
219
-
220
- function ChannelsView() {
221
- // Access the structured data from the tool response
222
- const toolOutput = useToolOutput<{ channels: Channel[] }>();
223
-
224
- // Persist state across the session
225
- const [state, setState] = useWidgetState({ selectedChannel: null });
226
-
227
- if (!toolOutput?.channels) return <div>Loading...</div>;
228
-
229
- return (
230
- <ul>
231
- {toolOutput.channels.map(ch => (
232
- <li key={ch.id} onClick={() => setState({ selectedChannel: ch.id })}>
233
- {ch.name}
234
- </li>
235
- ))}
236
- </ul>
237
- );
238
- }
239
- ```
240
-
241
- ## Theming
242
-
243
- The SDK uses CSS variables compatible with MCP host theming. Import the styles:
244
-
245
- ```tsx
246
- import '@leanmcp/ui/styles.css';
247
- ```
248
-
249
- The styles automatically adapt to the host's theme (light/dark).
250
-
251
- ## Testing
252
-
253
- Use `MockAppProvider` for unit testing:
254
-
255
- ```tsx
256
- import { MockAppProvider } from '@leanmcp/ui/testing';
257
-
258
- test('renders tool result', () => {
259
- render(
260
- <MockAppProvider
261
- toolResult={{ data: 'test' }}
262
- callTool={async () => ({ content: [{ type: 'text', text: '{}' }] })}
263
- >
264
- <MyComponent />
265
- </MockAppProvider>
266
- );
267
- });
268
- ```
269
-
270
- ## Documentation
271
-
272
- - [Full Documentation](https://docs.leanmcp.com/sdk/ui)
273
- - [Component Reference](https://docs.leanmcp.com/sdk/ui-components)
274
- - [Hooks Reference](https://docs.leanmcp.com/sdk/ui-hooks)
275
- - [ChatGPT Apps Guide](https://docs.leanmcp.com/sdk/ui-gpt-apps)
276
-
277
- ## Related Packages
278
-
279
- - [@leanmcp/core](https://www.npmjs.com/package/@leanmcp/core) — Core MCP server functionality
280
- - [@leanmcp/cli](https://www.npmjs.com/package/@leanmcp/cli) — CLI for project scaffolding
281
- - [@leanmcp/auth](https://www.npmjs.com/package/@leanmcp/auth) — Authentication decorators
282
-
283
- ## License
284
-
285
- MIT
1
+ <p align="center">
2
+ <img
3
+ src="https://raw.githubusercontent.com/LeanMCP/leanmcp-sdk/refs/heads/main/assets/logo.png"
4
+ alt="LeanMCP Logo"
5
+ width="400"
6
+ />
7
+ </p>
8
+
9
+ <p align="center">
10
+ <strong>@leanmcp/ui</strong><br/>
11
+ MCP-Native UI SDK for React — Build rich, interactive MCP Apps with first-class tool integration.
12
+ </p>
13
+
14
+ <p align="center">
15
+ <a href="https://www.npmjs.com/package/@leanmcp/ui">
16
+ <img src="https://img.shields.io/npm/v/@leanmcp/ui" alt="npm version" />
17
+ </a>
18
+ <a href="https://www.npmjs.com/package/@leanmcp/ui">
19
+ <img src="https://img.shields.io/npm/dm/@leanmcp/ui" alt="npm downloads" />
20
+ </a>
21
+ <a href="https://docs.leanmcp.com/sdk/ui">
22
+ <img src="https://img.shields.io/badge/Docs-leanmcp-0A66C2?" />
23
+ </a>
24
+ <a href="https://discord.com/invite/DsRcA3GwPy">
25
+ <img src="https://img.shields.io/badge/Discord-Join-5865F2?logo=discord&logoColor=white" />
26
+ </a>
27
+ <a href="https://x.com/LeanMcp">
28
+ <img src="https://img.shields.io/badge/@LeanMCP-f5f5f5?logo=x&logoColor=000000" />
29
+ </a>
30
+ </p>
31
+
32
+ ## Features
33
+
34
+ - **MCP-Native Components** — ToolButton, ToolSelect, ToolForm, ToolDataGrid, and more
35
+ - **First-Class Tool Integration** — Components that natively call MCP tools
36
+ - **ChatGPT Apps Support** — Build apps that work inside ChatGPT with `@GPTApp`
37
+ - **Streaming Support** — Handle partial/streaming tool responses
38
+ - **Theming** — Automatic host theme adaptation (light/dark)
39
+ - **Testing Utilities** — `MockAppProvider` for unit testing
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ npm install @leanmcp/ui
45
+ ```
46
+
47
+ ## Quick Start
48
+
49
+ ```tsx
50
+ import { AppProvider, ToolButton } from '@leanmcp/ui';
51
+ import '@leanmcp/ui/styles.css';
52
+
53
+ function MyApp() {
54
+ return (
55
+ <AppProvider appInfo={{ name: 'MyApp', version: '1.0.0' }}>
56
+ <ToolButton tool="refresh-data" resultDisplay="toast">
57
+ Refresh
58
+ </ToolButton>
59
+ </AppProvider>
60
+ );
61
+ }
62
+ ```
63
+
64
+ ## Components
65
+
66
+ ### MCP-Native Components
67
+
68
+ | Component | Description |
69
+ |-----------|-------------|
70
+ | `ToolButton` | Button with tool execution, confirmation, result display |
71
+ | `ToolSelect` | Select with tool-based options and selection callbacks |
72
+ | `ToolInput` | Input with debounced search and autocomplete |
73
+ | `ToolForm` | Form with multiple field types (text, select, checkbox, slider) |
74
+ | `ToolDataGrid` | Table with server-side pagination, sorting, row actions |
75
+ | `ResourceView` | Display MCP server resources with auto-refresh |
76
+ | `StreamingContent` | Render streaming/partial tool data |
77
+
78
+ ### Utility Components
79
+
80
+ | Component | Description |
81
+ |-----------|-------------|
82
+ | `RequireConnection` | Guard wrapper for MCP connection state |
83
+ | `ToolErrorBoundary` | Error boundary with retry for tool errors |
84
+ | `ToolProvider` | Scoped configuration context |
85
+
86
+ ## Hooks
87
+
88
+ | Hook | Description |
89
+ |------|-------------|
90
+ | `useTool` | Call tools with retry, abort, transformation |
91
+ | `useToolStream` | Handle streaming tool input |
92
+ | `useResource` | Read MCP resources with auto-refresh |
93
+ | `useMessage` | Send messages to host chat |
94
+ | `useHostContext` | Access host theme and viewport |
95
+
96
+ ### GPT Apps SDK Hooks
97
+
98
+ These hooks provide access to the ChatGPT Apps SDK globals (compatible with OpenAI's `window.openai` API):
99
+
100
+ | Hook | Description |
101
+ |------|-------------|
102
+ | `useToolOutput` | Access `structuredContent` from the tool response |
103
+ | `useToolInput` | Access input arguments passed to the tool |
104
+ | `useWidgetState` | Read/write persistent widget state across sessions |
105
+ | `useToolResponseMetadata` | Access `_meta` from the tool response |
106
+ | `useOpenAiGlobal` | Low-level hook to subscribe to any `window.openai` property |
107
+
108
+ ## Examples
109
+
110
+ ### ToolButton with Confirmation
111
+
112
+ ```tsx
113
+ <ToolButton
114
+ tool="delete-item"
115
+ args={{ id: item.id }}
116
+ confirm={{
117
+ title: 'Delete Item?',
118
+ description: 'This cannot be undone.'
119
+ }}
120
+ variant="destructive"
121
+ >
122
+ Delete
123
+ </ToolButton>
124
+ ```
125
+
126
+ ### ToolSelect with Dynamic Options
127
+
128
+ ```tsx
129
+ <ToolSelect
130
+ optionsTool="list-categories"
131
+ transformOptions={(r) => r.categories.map(c => ({
132
+ value: c.id,
133
+ label: c.name
134
+ }))}
135
+ onSelectTool="set-category"
136
+ argName="categoryId"
137
+ />
138
+ ```
139
+
140
+ ### ToolDataGrid
141
+
142
+ ```tsx
143
+ <ToolDataGrid
144
+ dataTool="list-users"
145
+ columns={[
146
+ { key: 'name', header: 'Name', sortable: true },
147
+ { key: 'email', header: 'Email' },
148
+ { key: 'status', header: 'Status' }
149
+ ]}
150
+ transformData={(r) => ({ rows: r.users, total: r.total })}
151
+ rowActions={[
152
+ { label: 'Edit', tool: 'edit-user' }
153
+ ]}
154
+ pagination
155
+ />
156
+ ```
157
+
158
+ ### ToolForm
159
+
160
+ ```tsx
161
+ <ToolForm
162
+ toolName="create-item"
163
+ fields={[
164
+ { name: 'title', label: 'Title', required: true },
165
+ { name: 'priority', label: 'Priority', type: 'select',
166
+ options: [
167
+ { value: 'low', label: 'Low' },
168
+ { value: 'high', label: 'High' }
169
+ ]
170
+ },
171
+ { name: 'notify', label: 'Send notifications', type: 'switch' }
172
+ ]}
173
+ showSuccessToast
174
+ />
175
+ ```
176
+
177
+ ## Server-Side Integration
178
+
179
+ Use `@UIApp` decorator to register your React component as an MCP resource.
180
+
181
+ > **Note:** Use a relative path string for the `component` property, not an imported component. This avoids importing React components on the server side.
182
+
183
+ ```typescript
184
+ // mcp/dashboard/index.ts
185
+ import { UIApp } from '@leanmcp/core';
186
+
187
+ export class DashboardService {
188
+ @UIApp({
189
+ component: './Dashboard', // Path relative to this file
190
+ name: 'dashboard',
191
+ title: 'Analytics Dashboard'
192
+ })
193
+ dashboard() {}
194
+ }
195
+ ```
196
+
197
+ ## ChatGPT Integration
198
+
199
+ Use `@GPTApp` for ChatGPT-specific apps:
200
+
201
+ ```typescript
202
+ import { GPTApp } from '@leanmcp/ui';
203
+
204
+ export class SlackService {
205
+ @GPTApp({
206
+ component: './SlackApp', // Path relative to this file
207
+ name: 'slack-composer'
208
+ })
209
+ slackComposer() {}
210
+ }
211
+ ```
212
+
213
+ ### GPT Apps SDK Hooks Usage
214
+
215
+ Access tool output (structuredContent) without making additional API calls:
216
+
217
+ ```tsx
218
+ import { useToolOutput, useWidgetState } from '@leanmcp/ui';
219
+
220
+ function ChannelsView() {
221
+ // Access the structured data from the tool response
222
+ const toolOutput = useToolOutput<{ channels: Channel[] }>();
223
+
224
+ // Persist state across the session
225
+ const [state, setState] = useWidgetState({ selectedChannel: null });
226
+
227
+ if (!toolOutput?.channels) return <div>Loading...</div>;
228
+
229
+ return (
230
+ <ul>
231
+ {toolOutput.channels.map(ch => (
232
+ <li key={ch.id} onClick={() => setState({ selectedChannel: ch.id })}>
233
+ {ch.name}
234
+ </li>
235
+ ))}
236
+ </ul>
237
+ );
238
+ }
239
+ ```
240
+
241
+ ## Theming
242
+
243
+ The SDK uses CSS variables compatible with MCP host theming. Import the styles:
244
+
245
+ ```tsx
246
+ import '@leanmcp/ui/styles.css';
247
+ ```
248
+
249
+ The styles automatically adapt to the host's theme (light/dark).
250
+
251
+ ## Testing
252
+
253
+ Use `MockAppProvider` for unit testing:
254
+
255
+ ```tsx
256
+ import { MockAppProvider } from '@leanmcp/ui/testing';
257
+
258
+ test('renders tool result', () => {
259
+ render(
260
+ <MockAppProvider
261
+ toolResult={{ data: 'test' }}
262
+ callTool={async () => ({ content: [{ type: 'text', text: '{}' }] })}
263
+ >
264
+ <MyComponent />
265
+ </MockAppProvider>
266
+ );
267
+ });
268
+ ```
269
+
270
+ ## Documentation
271
+
272
+ - [Full Documentation](https://docs.leanmcp.com/sdk/ui)
273
+ - [Component Reference](https://docs.leanmcp.com/sdk/ui-components)
274
+ - [Hooks Reference](https://docs.leanmcp.com/sdk/ui-hooks)
275
+ - [ChatGPT Apps Guide](https://docs.leanmcp.com/sdk/ui-gpt-apps)
276
+
277
+ ## Related Packages
278
+
279
+ - [@leanmcp/core](https://www.npmjs.com/package/@leanmcp/core) — Core MCP server functionality
280
+ - [@leanmcp/cli](https://www.npmjs.com/package/@leanmcp/cli) — CLI for project scaffolding
281
+ - [@leanmcp/auth](https://www.npmjs.com/package/@leanmcp/auth) — Authentication decorators
282
+
283
+ ## License
284
+
285
+ MIT