optimizely-opal.opal-tools-sdk 0.1.27.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.27.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/PKG-INFO +115 -1
- {optimizely_opal_opal_tools_sdk-0.1.27.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/README.md +114 -0
- {optimizely_opal_opal_tools_sdk-0.1.27.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/opal_tools_sdk/proteus.py +25 -2
- {optimizely_opal_opal_tools_sdk-0.1.27.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.27.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/pyproject.toml +1 -1
- {optimizely_opal_opal_tools_sdk-0.1.27.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/setup.py +1 -1
- {optimizely_opal_opal_tools_sdk-0.1.27.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/opal_tools_sdk/__init__.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.27.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/opal_tools_sdk/_registry.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.27.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/opal_tools_sdk/auth.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.27.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/opal_tools_sdk/decorators.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.27.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/opal_tools_sdk/logging.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.27.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/opal_tools_sdk/models.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.27.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/opal_tools_sdk/service.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.27.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/opal_tools_sdk/ui.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.27.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.27.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.27.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.27.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.27.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/setup.cfg +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.27.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/tests/test_integration.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.27.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/tests/test_nested_schema.py +0 -0
- {optimizely_opal_opal_tools_sdk-0.1.27.dev0 → optimizely_opal_opal_tools_sdk-0.1.28.dev0}/tests/test_proteus.py +0 -0
{optimizely_opal_opal_tools_sdk-0.1.27.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.27.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):
|
|
@@ -592,6 +595,22 @@ class ProteusBadge(BaseModel):
|
|
|
592
595
|
z: SprinklePropZ | None = None
|
|
593
596
|
|
|
594
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
|
+
|
|
595
614
|
class ProteusButton(BaseModel):
|
|
596
615
|
model_config = ConfigDict(
|
|
597
616
|
extra='forbid',
|
|
@@ -922,7 +941,7 @@ class ProteusDataTable(BaseModel):
|
|
|
922
941
|
extra='forbid',
|
|
923
942
|
)
|
|
924
943
|
field_type: Literal['DataTable'] = Field(default='DataTable', alias='$type')
|
|
925
|
-
columns: list[Column] |
|
|
944
|
+
columns: list[Column] | ProteusExpression | None = None
|
|
926
945
|
data: list[dict[str, Any]] | ProteusExpression | ProteusZip | None = None
|
|
927
946
|
|
|
928
947
|
|
|
@@ -2170,6 +2189,7 @@ class ProteusElement(
|
|
|
2170
2189
|
ProteusAction
|
|
2171
2190
|
| ProteusAvatar
|
|
2172
2191
|
| ProteusBadge
|
|
2192
|
+
| ProteusBridge
|
|
2173
2193
|
| ProteusButton
|
|
2174
2194
|
| ProteusCard
|
|
2175
2195
|
| ProteusCardHeader
|
|
@@ -2205,6 +2225,7 @@ class ProteusElement(
|
|
|
2205
2225
|
ProteusAction
|
|
2206
2226
|
| ProteusAvatar
|
|
2207
2227
|
| ProteusBadge
|
|
2228
|
+
| ProteusBridge
|
|
2208
2229
|
| ProteusButton
|
|
2209
2230
|
| ProteusCard
|
|
2210
2231
|
| ProteusCardHeader
|
|
@@ -3881,6 +3902,7 @@ ProteusZip.model_rebuild()
|
|
|
3881
3902
|
ProteusAction.model_rebuild()
|
|
3882
3903
|
ProteusAvatar.model_rebuild()
|
|
3883
3904
|
ProteusBadge.model_rebuild()
|
|
3905
|
+
ProteusBridge.model_rebuild()
|
|
3884
3906
|
ProteusButton.model_rebuild()
|
|
3885
3907
|
ProteusCard.model_rebuild()
|
|
3886
3908
|
ProteusCardHeader.model_rebuild()
|
|
@@ -3925,6 +3947,7 @@ class UI:
|
|
|
3925
3947
|
Action = ProteusAction
|
|
3926
3948
|
Avatar = ProteusAvatar
|
|
3927
3949
|
Badge = ProteusBadge
|
|
3950
|
+
Bridge = ProteusBridge
|
|
3928
3951
|
Button = ProteusButton
|
|
3929
3952
|
Card = ProteusCard
|
|
3930
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.27.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
|