optimizely-opal.opal-tools-sdk 0.1.26.dev0__tar.gz → 0.1.28.dev0__tar.gz
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.
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/PKG-INFO +115 -1
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/README.md +114 -0
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/opal_tools_sdk/proteus.py +29 -2
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/optimizely_opal.opal_tools_sdk.egg-info/PKG-INFO +115 -1
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/pyproject.toml +1 -1
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/setup.py +1 -1
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/opal_tools_sdk/__init__.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/opal_tools_sdk/_registry.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/opal_tools_sdk/auth.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/opal_tools_sdk/decorators.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/opal_tools_sdk/logging.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/opal_tools_sdk/models.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/opal_tools_sdk/service.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/opal_tools_sdk/ui.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/optimizely_opal.opal_tools_sdk.egg-info/SOURCES.txt +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/optimizely_opal.opal_tools_sdk.egg-info/dependency_links.txt +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/optimizely_opal.opal_tools_sdk.egg-info/requires.txt +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/optimizely_opal.opal_tools_sdk.egg-info/top_level.txt +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/setup.cfg +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/tests/test_integration.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/tests/test_nested_schema.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/tests/test_proteus.py +0 -0
{optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: optimizely-opal.opal-tools-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.28.dev0
|
|
4
4
|
Summary: SDK for creating Opal-compatible tools services
|
|
5
5
|
Home-page: https://github.com/optimizely/opal-tools-sdk
|
|
6
6
|
Author: Optimizely
|
|
@@ -235,6 +235,120 @@ The response wrapper for islands:
|
|
|
235
235
|
- Use `IslandResponse.create([islands])` to create responses
|
|
236
236
|
- Supports multiple islands per response
|
|
237
237
|
|
|
238
|
+
## Resources & Proteus UI
|
|
239
|
+
|
|
240
|
+
The SDK supports defining MCP resources that serve dynamic UI specifications using the Proteus framework. This enables tools to render rich, interactive interfaces without hardcoded frontend integrations.
|
|
241
|
+
|
|
242
|
+
For the full Proteus component reference and visual designer, see the [Proteus documentation](https://optimizely-axiom.github.io/optiaxiom/guides/proteus/).
|
|
243
|
+
|
|
244
|
+
### Defining a Resource with `@resource`
|
|
245
|
+
|
|
246
|
+
Use the `@resource` decorator to register a function as an MCP resource:
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
from opal_tools_sdk import UI
|
|
250
|
+
from opal_tools_sdk.decorators import resource
|
|
251
|
+
|
|
252
|
+
@resource(
|
|
253
|
+
uri="ui://my-app/create-form",
|
|
254
|
+
name="create-form",
|
|
255
|
+
description="Form for creating new items",
|
|
256
|
+
)
|
|
257
|
+
async def get_create_form():
|
|
258
|
+
return UI.Document(
|
|
259
|
+
title="Create Item",
|
|
260
|
+
body=[
|
|
261
|
+
UI.Heading(children="New Item"),
|
|
262
|
+
UI.Field(
|
|
263
|
+
label="Item Name",
|
|
264
|
+
children=UI.Input(name="item_name", placeholder="Enter item name"),
|
|
265
|
+
),
|
|
266
|
+
UI.Field(
|
|
267
|
+
label="Description",
|
|
268
|
+
children=UI.Textarea(name="description", placeholder="Enter description"),
|
|
269
|
+
),
|
|
270
|
+
],
|
|
271
|
+
actions=[
|
|
272
|
+
UI.Action(children="Save", appearance="primary"),
|
|
273
|
+
UI.CancelAction(children="Cancel"),
|
|
274
|
+
],
|
|
275
|
+
)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
**Parameters:**
|
|
279
|
+
- `uri` (required): Unique URI for the resource (e.g., `"ui://my-app/create-form"`)
|
|
280
|
+
- `name` (required): Name of the resource
|
|
281
|
+
- `description` (optional): Description of the resource
|
|
282
|
+
- `mime_type` (optional): MIME type of the content. Auto-set to `"application/vnd.opal.proteus+json"` when returning a `UI.Document`
|
|
283
|
+
- `title` (optional): Human-readable title
|
|
284
|
+
|
|
285
|
+
The handler function can return either a `str` (manual JSON serialization) or a `UI.Document` (automatic serialization with MIME type set automatically).
|
|
286
|
+
|
|
287
|
+
### Linking a Tool to a UI Resource
|
|
288
|
+
|
|
289
|
+
Use the `ui_resource` parameter on `@tool` to associate a tool with a Proteus UI resource. The frontend fetches and renders the resource when the tool is invoked:
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
from opal_tools_sdk import tool
|
|
293
|
+
from pydantic import BaseModel, Field
|
|
294
|
+
|
|
295
|
+
class CreateItemParams(BaseModel):
|
|
296
|
+
item_name: str = Field(description="Name of the item")
|
|
297
|
+
description: str = Field(description="Item description")
|
|
298
|
+
|
|
299
|
+
@tool(
|
|
300
|
+
"create_item",
|
|
301
|
+
"Create a new item",
|
|
302
|
+
ui_resource="ui://my-app/create-form",
|
|
303
|
+
)
|
|
304
|
+
async def create_item(parameters: CreateItemParams):
|
|
305
|
+
return {"id": "item-123", "name": parameters.item_name, "status": "created"}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Building UI with `UI.Document`
|
|
309
|
+
|
|
310
|
+
Import the `UI` namespace from `opal_tools_sdk` or `opal_tools_sdk.ui`. It provides type-safe builders for all Proteus components:
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
from opal_tools_sdk import UI
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Available components:**
|
|
317
|
+
|
|
318
|
+
| Category | Components |
|
|
319
|
+
|----------|-----------|
|
|
320
|
+
| Layout | `UI.Document`, `UI.Group`, `UI.Card`, `UI.CardHeader`, `UI.CardLink`, `UI.Separator` |
|
|
321
|
+
| Typography | `UI.Heading`, `UI.Text`, `UI.Link` |
|
|
322
|
+
| Data Display | `UI.Avatar`, `UI.Badge`, `UI.DataTable`, `UI.Chart`, `UI.IconCalendar`, `UI.Image`, `UI.ImageCarousel`, `UI.Time` |
|
|
323
|
+
| Form Controls | `UI.Field`, `UI.Input`, `UI.Textarea`, `UI.Select`, `UI.SelectTrigger`, `UI.SelectContent`, `UI.Switch`, `UI.Range`, `UI.Question` |
|
|
324
|
+
| Actions | `UI.Action`, `UI.CancelAction` |
|
|
325
|
+
| Dynamic | `UI.Value`, `UI.Map`, `UI.MapIndex`, `UI.Show`, `UI.Concat`, `UI.Zip` |
|
|
326
|
+
|
|
327
|
+
**Data binding** with `UI.Value` resolves paths from the tool response:
|
|
328
|
+
|
|
329
|
+
```python
|
|
330
|
+
@resource(uri="ui://my-app/results", name="results")
|
|
331
|
+
async def get_results():
|
|
332
|
+
return UI.Document(
|
|
333
|
+
title=UI.Value(path="/title"),
|
|
334
|
+
body=UI.Map(
|
|
335
|
+
path="/items",
|
|
336
|
+
children=UI.Text(children=UI.Value(path="name")),
|
|
337
|
+
),
|
|
338
|
+
)
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Conditional rendering** with `UI.Show`:
|
|
342
|
+
|
|
343
|
+
```python
|
|
344
|
+
UI.Show(
|
|
345
|
+
when={"!!": UI.Value(path="/error")},
|
|
346
|
+
children=UI.Text(children="An error occurred", color="fg.error"),
|
|
347
|
+
)
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
The MIME type constant is available as `UI.MIME_TYPE` (`"application/vnd.opal.proteus+json"`).
|
|
351
|
+
|
|
238
352
|
## Type Definitions
|
|
239
353
|
|
|
240
354
|
The SDK provides several TypedDict and dataclass definitions for better type safety:
|
{optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/README.md
RENAMED
|
@@ -207,6 +207,120 @@ The response wrapper for islands:
|
|
|
207
207
|
- Use `IslandResponse.create([islands])` to create responses
|
|
208
208
|
- Supports multiple islands per response
|
|
209
209
|
|
|
210
|
+
## Resources & Proteus UI
|
|
211
|
+
|
|
212
|
+
The SDK supports defining MCP resources that serve dynamic UI specifications using the Proteus framework. This enables tools to render rich, interactive interfaces without hardcoded frontend integrations.
|
|
213
|
+
|
|
214
|
+
For the full Proteus component reference and visual designer, see the [Proteus documentation](https://optimizely-axiom.github.io/optiaxiom/guides/proteus/).
|
|
215
|
+
|
|
216
|
+
### Defining a Resource with `@resource`
|
|
217
|
+
|
|
218
|
+
Use the `@resource` decorator to register a function as an MCP resource:
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
from opal_tools_sdk import UI
|
|
222
|
+
from opal_tools_sdk.decorators import resource
|
|
223
|
+
|
|
224
|
+
@resource(
|
|
225
|
+
uri="ui://my-app/create-form",
|
|
226
|
+
name="create-form",
|
|
227
|
+
description="Form for creating new items",
|
|
228
|
+
)
|
|
229
|
+
async def get_create_form():
|
|
230
|
+
return UI.Document(
|
|
231
|
+
title="Create Item",
|
|
232
|
+
body=[
|
|
233
|
+
UI.Heading(children="New Item"),
|
|
234
|
+
UI.Field(
|
|
235
|
+
label="Item Name",
|
|
236
|
+
children=UI.Input(name="item_name", placeholder="Enter item name"),
|
|
237
|
+
),
|
|
238
|
+
UI.Field(
|
|
239
|
+
label="Description",
|
|
240
|
+
children=UI.Textarea(name="description", placeholder="Enter description"),
|
|
241
|
+
),
|
|
242
|
+
],
|
|
243
|
+
actions=[
|
|
244
|
+
UI.Action(children="Save", appearance="primary"),
|
|
245
|
+
UI.CancelAction(children="Cancel"),
|
|
246
|
+
],
|
|
247
|
+
)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Parameters:**
|
|
251
|
+
- `uri` (required): Unique URI for the resource (e.g., `"ui://my-app/create-form"`)
|
|
252
|
+
- `name` (required): Name of the resource
|
|
253
|
+
- `description` (optional): Description of the resource
|
|
254
|
+
- `mime_type` (optional): MIME type of the content. Auto-set to `"application/vnd.opal.proteus+json"` when returning a `UI.Document`
|
|
255
|
+
- `title` (optional): Human-readable title
|
|
256
|
+
|
|
257
|
+
The handler function can return either a `str` (manual JSON serialization) or a `UI.Document` (automatic serialization with MIME type set automatically).
|
|
258
|
+
|
|
259
|
+
### Linking a Tool to a UI Resource
|
|
260
|
+
|
|
261
|
+
Use the `ui_resource` parameter on `@tool` to associate a tool with a Proteus UI resource. The frontend fetches and renders the resource when the tool is invoked:
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
from opal_tools_sdk import tool
|
|
265
|
+
from pydantic import BaseModel, Field
|
|
266
|
+
|
|
267
|
+
class CreateItemParams(BaseModel):
|
|
268
|
+
item_name: str = Field(description="Name of the item")
|
|
269
|
+
description: str = Field(description="Item description")
|
|
270
|
+
|
|
271
|
+
@tool(
|
|
272
|
+
"create_item",
|
|
273
|
+
"Create a new item",
|
|
274
|
+
ui_resource="ui://my-app/create-form",
|
|
275
|
+
)
|
|
276
|
+
async def create_item(parameters: CreateItemParams):
|
|
277
|
+
return {"id": "item-123", "name": parameters.item_name, "status": "created"}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Building UI with `UI.Document`
|
|
281
|
+
|
|
282
|
+
Import the `UI` namespace from `opal_tools_sdk` or `opal_tools_sdk.ui`. It provides type-safe builders for all Proteus components:
|
|
283
|
+
|
|
284
|
+
```python
|
|
285
|
+
from opal_tools_sdk import UI
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**Available components:**
|
|
289
|
+
|
|
290
|
+
| Category | Components |
|
|
291
|
+
|----------|-----------|
|
|
292
|
+
| Layout | `UI.Document`, `UI.Group`, `UI.Card`, `UI.CardHeader`, `UI.CardLink`, `UI.Separator` |
|
|
293
|
+
| Typography | `UI.Heading`, `UI.Text`, `UI.Link` |
|
|
294
|
+
| Data Display | `UI.Avatar`, `UI.Badge`, `UI.DataTable`, `UI.Chart`, `UI.IconCalendar`, `UI.Image`, `UI.ImageCarousel`, `UI.Time` |
|
|
295
|
+
| Form Controls | `UI.Field`, `UI.Input`, `UI.Textarea`, `UI.Select`, `UI.SelectTrigger`, `UI.SelectContent`, `UI.Switch`, `UI.Range`, `UI.Question` |
|
|
296
|
+
| Actions | `UI.Action`, `UI.CancelAction` |
|
|
297
|
+
| Dynamic | `UI.Value`, `UI.Map`, `UI.MapIndex`, `UI.Show`, `UI.Concat`, `UI.Zip` |
|
|
298
|
+
|
|
299
|
+
**Data binding** with `UI.Value` resolves paths from the tool response:
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
@resource(uri="ui://my-app/results", name="results")
|
|
303
|
+
async def get_results():
|
|
304
|
+
return UI.Document(
|
|
305
|
+
title=UI.Value(path="/title"),
|
|
306
|
+
body=UI.Map(
|
|
307
|
+
path="/items",
|
|
308
|
+
children=UI.Text(children=UI.Value(path="name")),
|
|
309
|
+
),
|
|
310
|
+
)
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Conditional rendering** with `UI.Show`:
|
|
314
|
+
|
|
315
|
+
```python
|
|
316
|
+
UI.Show(
|
|
317
|
+
when={"!!": UI.Value(path="/error")},
|
|
318
|
+
children=UI.Text(children="An error occurred", color="fg.error"),
|
|
319
|
+
)
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
The MIME type constant is available as `UI.MIME_TYPE` (`"application/vnd.opal.proteus+json"`).
|
|
323
|
+
|
|
210
324
|
## Type Definitions
|
|
211
325
|
|
|
212
326
|
The SDK provides several TypedDict and dataclass definitions for better type safety:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# generated by datamodel-codegen:
|
|
2
2
|
# filename: proteus-document-spec.json
|
|
3
|
-
# timestamp: 2026-04-
|
|
3
|
+
# timestamp: 2026-04-27T14:20:49+00:00
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
@@ -22,6 +22,9 @@ class ProteusEventHandler1(BaseModel):
|
|
|
22
22
|
extra='forbid',
|
|
23
23
|
)
|
|
24
24
|
interaction: str = Field(..., description='Name of registered interaction to call')
|
|
25
|
+
params: dict[str, Any] | None = Field(
|
|
26
|
+
default=None, description='Parameters to pass to the interaction handler'
|
|
27
|
+
)
|
|
25
28
|
|
|
26
29
|
|
|
27
30
|
class Series(BaseModel):
|
|
@@ -276,6 +279,10 @@ class ProteusDocument(BaseModel):
|
|
|
276
279
|
description='If true, hides chat prompt and forces user interaction with document. User can press ESC or close to abandon.',
|
|
277
280
|
)
|
|
278
281
|
body: ProteusNode | None = Field(..., description='The main content of the document.')
|
|
282
|
+
compact: bool | None = Field(
|
|
283
|
+
default=None,
|
|
284
|
+
description='If true, constrains the body to a max height and makes it scrollable when content overflows.',
|
|
285
|
+
)
|
|
279
286
|
subtitle: ProteusNode | None = Field(
|
|
280
287
|
default=None,
|
|
281
288
|
description="A brief description or tagline that provides additional context about the Proteus document's purpose.",
|
|
@@ -588,6 +595,22 @@ class ProteusBadge(BaseModel):
|
|
|
588
595
|
z: SprinklePropZ | None = None
|
|
589
596
|
|
|
590
597
|
|
|
598
|
+
class ProteusBridge(BaseModel):
|
|
599
|
+
model_config = ConfigDict(
|
|
600
|
+
extra='forbid',
|
|
601
|
+
)
|
|
602
|
+
field_type: Literal['Bridge'] = Field(default='Bridge', alias='$type')
|
|
603
|
+
fallback: ProteusNode | None = Field(
|
|
604
|
+
default=None,
|
|
605
|
+
description="Content rendered on platforms without iframe support (Teams, Slack, mobile). If omitted, a default 'View in Opal web' message is shown.",
|
|
606
|
+
)
|
|
607
|
+
height: float | None = Field(default=None, description='Height of the iframe in pixels')
|
|
608
|
+
resource: str = Field(
|
|
609
|
+
...,
|
|
610
|
+
description="Resource URI identifying the MCP app to render (e.g., 'ui://sample-widget')",
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
|
|
591
614
|
class ProteusButton(BaseModel):
|
|
592
615
|
model_config = ConfigDict(
|
|
593
616
|
extra='forbid',
|
|
@@ -918,7 +941,7 @@ class ProteusDataTable(BaseModel):
|
|
|
918
941
|
extra='forbid',
|
|
919
942
|
)
|
|
920
943
|
field_type: Literal['DataTable'] = Field(default='DataTable', alias='$type')
|
|
921
|
-
columns: list[Column] |
|
|
944
|
+
columns: list[Column] | ProteusExpression | None = None
|
|
922
945
|
data: list[dict[str, Any]] | ProteusExpression | ProteusZip | None = None
|
|
923
946
|
|
|
924
947
|
|
|
@@ -2166,6 +2189,7 @@ class ProteusElement(
|
|
|
2166
2189
|
ProteusAction
|
|
2167
2190
|
| ProteusAvatar
|
|
2168
2191
|
| ProteusBadge
|
|
2192
|
+
| ProteusBridge
|
|
2169
2193
|
| ProteusButton
|
|
2170
2194
|
| ProteusCard
|
|
2171
2195
|
| ProteusCardHeader
|
|
@@ -2201,6 +2225,7 @@ class ProteusElement(
|
|
|
2201
2225
|
ProteusAction
|
|
2202
2226
|
| ProteusAvatar
|
|
2203
2227
|
| ProteusBadge
|
|
2228
|
+
| ProteusBridge
|
|
2204
2229
|
| ProteusButton
|
|
2205
2230
|
| ProteusCard
|
|
2206
2231
|
| ProteusCardHeader
|
|
@@ -3877,6 +3902,7 @@ ProteusZip.model_rebuild()
|
|
|
3877
3902
|
ProteusAction.model_rebuild()
|
|
3878
3903
|
ProteusAvatar.model_rebuild()
|
|
3879
3904
|
ProteusBadge.model_rebuild()
|
|
3905
|
+
ProteusBridge.model_rebuild()
|
|
3880
3906
|
ProteusButton.model_rebuild()
|
|
3881
3907
|
ProteusCard.model_rebuild()
|
|
3882
3908
|
ProteusCardHeader.model_rebuild()
|
|
@@ -3921,6 +3947,7 @@ class UI:
|
|
|
3921
3947
|
Action = ProteusAction
|
|
3922
3948
|
Avatar = ProteusAvatar
|
|
3923
3949
|
Badge = ProteusBadge
|
|
3950
|
+
Bridge = ProteusBridge
|
|
3924
3951
|
Button = ProteusButton
|
|
3925
3952
|
Card = ProteusCard
|
|
3926
3953
|
CardHeader = ProteusCardHeader
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: optimizely-opal.opal-tools-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.28.dev0
|
|
4
4
|
Summary: SDK for creating Opal-compatible tools services
|
|
5
5
|
Home-page: https://github.com/optimizely/opal-tools-sdk
|
|
6
6
|
Author: Optimizely
|
|
@@ -235,6 +235,120 @@ The response wrapper for islands:
|
|
|
235
235
|
- Use `IslandResponse.create([islands])` to create responses
|
|
236
236
|
- Supports multiple islands per response
|
|
237
237
|
|
|
238
|
+
## Resources & Proteus UI
|
|
239
|
+
|
|
240
|
+
The SDK supports defining MCP resources that serve dynamic UI specifications using the Proteus framework. This enables tools to render rich, interactive interfaces without hardcoded frontend integrations.
|
|
241
|
+
|
|
242
|
+
For the full Proteus component reference and visual designer, see the [Proteus documentation](https://optimizely-axiom.github.io/optiaxiom/guides/proteus/).
|
|
243
|
+
|
|
244
|
+
### Defining a Resource with `@resource`
|
|
245
|
+
|
|
246
|
+
Use the `@resource` decorator to register a function as an MCP resource:
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
from opal_tools_sdk import UI
|
|
250
|
+
from opal_tools_sdk.decorators import resource
|
|
251
|
+
|
|
252
|
+
@resource(
|
|
253
|
+
uri="ui://my-app/create-form",
|
|
254
|
+
name="create-form",
|
|
255
|
+
description="Form for creating new items",
|
|
256
|
+
)
|
|
257
|
+
async def get_create_form():
|
|
258
|
+
return UI.Document(
|
|
259
|
+
title="Create Item",
|
|
260
|
+
body=[
|
|
261
|
+
UI.Heading(children="New Item"),
|
|
262
|
+
UI.Field(
|
|
263
|
+
label="Item Name",
|
|
264
|
+
children=UI.Input(name="item_name", placeholder="Enter item name"),
|
|
265
|
+
),
|
|
266
|
+
UI.Field(
|
|
267
|
+
label="Description",
|
|
268
|
+
children=UI.Textarea(name="description", placeholder="Enter description"),
|
|
269
|
+
),
|
|
270
|
+
],
|
|
271
|
+
actions=[
|
|
272
|
+
UI.Action(children="Save", appearance="primary"),
|
|
273
|
+
UI.CancelAction(children="Cancel"),
|
|
274
|
+
],
|
|
275
|
+
)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
**Parameters:**
|
|
279
|
+
- `uri` (required): Unique URI for the resource (e.g., `"ui://my-app/create-form"`)
|
|
280
|
+
- `name` (required): Name of the resource
|
|
281
|
+
- `description` (optional): Description of the resource
|
|
282
|
+
- `mime_type` (optional): MIME type of the content. Auto-set to `"application/vnd.opal.proteus+json"` when returning a `UI.Document`
|
|
283
|
+
- `title` (optional): Human-readable title
|
|
284
|
+
|
|
285
|
+
The handler function can return either a `str` (manual JSON serialization) or a `UI.Document` (automatic serialization with MIME type set automatically).
|
|
286
|
+
|
|
287
|
+
### Linking a Tool to a UI Resource
|
|
288
|
+
|
|
289
|
+
Use the `ui_resource` parameter on `@tool` to associate a tool with a Proteus UI resource. The frontend fetches and renders the resource when the tool is invoked:
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
from opal_tools_sdk import tool
|
|
293
|
+
from pydantic import BaseModel, Field
|
|
294
|
+
|
|
295
|
+
class CreateItemParams(BaseModel):
|
|
296
|
+
item_name: str = Field(description="Name of the item")
|
|
297
|
+
description: str = Field(description="Item description")
|
|
298
|
+
|
|
299
|
+
@tool(
|
|
300
|
+
"create_item",
|
|
301
|
+
"Create a new item",
|
|
302
|
+
ui_resource="ui://my-app/create-form",
|
|
303
|
+
)
|
|
304
|
+
async def create_item(parameters: CreateItemParams):
|
|
305
|
+
return {"id": "item-123", "name": parameters.item_name, "status": "created"}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Building UI with `UI.Document`
|
|
309
|
+
|
|
310
|
+
Import the `UI` namespace from `opal_tools_sdk` or `opal_tools_sdk.ui`. It provides type-safe builders for all Proteus components:
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
from opal_tools_sdk import UI
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Available components:**
|
|
317
|
+
|
|
318
|
+
| Category | Components |
|
|
319
|
+
|----------|-----------|
|
|
320
|
+
| Layout | `UI.Document`, `UI.Group`, `UI.Card`, `UI.CardHeader`, `UI.CardLink`, `UI.Separator` |
|
|
321
|
+
| Typography | `UI.Heading`, `UI.Text`, `UI.Link` |
|
|
322
|
+
| Data Display | `UI.Avatar`, `UI.Badge`, `UI.DataTable`, `UI.Chart`, `UI.IconCalendar`, `UI.Image`, `UI.ImageCarousel`, `UI.Time` |
|
|
323
|
+
| Form Controls | `UI.Field`, `UI.Input`, `UI.Textarea`, `UI.Select`, `UI.SelectTrigger`, `UI.SelectContent`, `UI.Switch`, `UI.Range`, `UI.Question` |
|
|
324
|
+
| Actions | `UI.Action`, `UI.CancelAction` |
|
|
325
|
+
| Dynamic | `UI.Value`, `UI.Map`, `UI.MapIndex`, `UI.Show`, `UI.Concat`, `UI.Zip` |
|
|
326
|
+
|
|
327
|
+
**Data binding** with `UI.Value` resolves paths from the tool response:
|
|
328
|
+
|
|
329
|
+
```python
|
|
330
|
+
@resource(uri="ui://my-app/results", name="results")
|
|
331
|
+
async def get_results():
|
|
332
|
+
return UI.Document(
|
|
333
|
+
title=UI.Value(path="/title"),
|
|
334
|
+
body=UI.Map(
|
|
335
|
+
path="/items",
|
|
336
|
+
children=UI.Text(children=UI.Value(path="name")),
|
|
337
|
+
),
|
|
338
|
+
)
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Conditional rendering** with `UI.Show`:
|
|
342
|
+
|
|
343
|
+
```python
|
|
344
|
+
UI.Show(
|
|
345
|
+
when={"!!": UI.Value(path="/error")},
|
|
346
|
+
children=UI.Text(children="An error occurred", color="fg.error"),
|
|
347
|
+
)
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
The MIME type constant is available as `UI.MIME_TYPE` (`"application/vnd.opal.proteus+json"`).
|
|
351
|
+
|
|
238
352
|
## Type Definitions
|
|
239
353
|
|
|
240
354
|
The SDK provides several TypedDict and dataclass definitions for better type safety:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "optimizely-opal.opal-tools-sdk"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.28-dev"
|
|
8
8
|
description = "SDK for creating Opal-compatible tools services"
|
|
9
9
|
authors = [{ name = "Optimizely", email = "opal-team@optimizely.com" }]
|
|
10
10
|
readme = "README.md"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{optimizely_opal_opal_tools_sdk-0.1.26.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/setup.cfg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|