mcp-ui 0.1.1__py3-none-any.whl → 0.1.2__py3-none-any.whl
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.
- mcp_ui/__init__.py +9 -0
- mcp_ui/core.py +234 -0
- {mcp_ui-0.1.1.dist-info → mcp_ui-0.1.2.dist-info}/METADATA +1 -2
- mcp_ui-0.1.2.dist-info/RECORD +6 -0
- mcp_ui-0.1.1.dist-info/RECORD +0 -4
- {mcp_ui-0.1.1.dist-info → mcp_ui-0.1.2.dist-info}/WHEEL +0 -0
- {mcp_ui-0.1.1.dist-info → mcp_ui-0.1.2.dist-info}/licenses/LICENSE +0 -0
mcp_ui/__init__.py
ADDED
mcp_ui/core.py
ADDED
@@ -0,0 +1,234 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
from dataclasses import dataclass, field, asdict
|
5
|
+
from typing import Any, Dict, Literal, Optional, Union
|
6
|
+
import base64
|
7
|
+
|
8
|
+
# --------------------------
|
9
|
+
# Types & Constants
|
10
|
+
# --------------------------
|
11
|
+
|
12
|
+
URI = str # Must start with "ui://"
|
13
|
+
|
14
|
+
MimeType = Literal[
|
15
|
+
"text/html",
|
16
|
+
"text/uri-list",
|
17
|
+
"application/vnd.mcp-ui.remote-dom+javascript; framework=react",
|
18
|
+
"application/vnd.mcp-ui.remote-dom+javascript; framework=webcomponents",
|
19
|
+
]
|
20
|
+
|
21
|
+
UIMetadataKey = {
|
22
|
+
"PREFERRED_FRAME_SIZE": "preferred-frame-size",
|
23
|
+
"INITIAL_RENDER_DATA": "initial-render-data",
|
24
|
+
}
|
25
|
+
|
26
|
+
UI_METADATA_PREFIX = "mcpui.dev/ui-"
|
27
|
+
|
28
|
+
InternalMessageType = {
|
29
|
+
"UI_MESSAGE_RECEIVED": "ui-message-received",
|
30
|
+
"UI_MESSAGE_RESPONSE": "ui-message-response",
|
31
|
+
"UI_SIZE_CHANGE": "ui-size-change",
|
32
|
+
"UI_LIFECYCLE_IFRAME_READY": "ui-lifecycle-iframe-ready",
|
33
|
+
"UI_LIFECYCLE_IFRAME_RENDER_DATA": "ui-lifecycle-iframe-render-data",
|
34
|
+
}
|
35
|
+
|
36
|
+
ReservedUrlParams = {
|
37
|
+
"WAIT_FOR_RENDER_DATA": "waitForRenderData",
|
38
|
+
}
|
39
|
+
|
40
|
+
|
41
|
+
# --------------------------
|
42
|
+
# Resource Content Payloads
|
43
|
+
# --------------------------
|
44
|
+
|
45
|
+
@dataclass
|
46
|
+
class RawHtmlContent:
|
47
|
+
type: Literal["rawHtml"]
|
48
|
+
htmlString: str
|
49
|
+
|
50
|
+
|
51
|
+
@dataclass
|
52
|
+
class ExternalUrlContent:
|
53
|
+
type: Literal["externalUrl"]
|
54
|
+
iframeUrl: str
|
55
|
+
|
56
|
+
|
57
|
+
@dataclass
|
58
|
+
class RemoteDomContent:
|
59
|
+
type: Literal["remoteDom"]
|
60
|
+
script: str
|
61
|
+
framework: Literal["react", "webcomponents"]
|
62
|
+
|
63
|
+
|
64
|
+
ResourceContentPayload = Union[RawHtmlContent, ExternalUrlContent, RemoteDomContent]
|
65
|
+
|
66
|
+
|
67
|
+
# --------------------------
|
68
|
+
# Resource Representations
|
69
|
+
# --------------------------
|
70
|
+
|
71
|
+
@dataclass
|
72
|
+
class HTMLTextContent:
|
73
|
+
uri: URI
|
74
|
+
mimeType: MimeType
|
75
|
+
text: str
|
76
|
+
blob: Optional[str] = None
|
77
|
+
_meta: Optional[Dict[str, Any]] = None
|
78
|
+
|
79
|
+
|
80
|
+
@dataclass
|
81
|
+
class Base64BlobContent:
|
82
|
+
uri: URI
|
83
|
+
mimeType: MimeType
|
84
|
+
blob: str
|
85
|
+
text: Optional[str] = None
|
86
|
+
_meta: Optional[Dict[str, Any]] = None
|
87
|
+
|
88
|
+
|
89
|
+
UIResource = Dict[str, Union[str, HTMLTextContent, Base64BlobContent]]
|
90
|
+
|
91
|
+
|
92
|
+
@dataclass
|
93
|
+
class CreateUIResourceOptions:
|
94
|
+
uri: URI
|
95
|
+
content: ResourceContentPayload
|
96
|
+
encoding: Literal["text", "blob"]
|
97
|
+
uiMetadata: Optional[Dict[str, Any]] = None
|
98
|
+
metadata: Optional[Dict[str, Any]] = None
|
99
|
+
resourceProps: Optional[Dict[str, Any]] = None
|
100
|
+
|
101
|
+
|
102
|
+
# --------------------------
|
103
|
+
# Utils
|
104
|
+
# --------------------------
|
105
|
+
|
106
|
+
def utf8_to_base64(s: str) -> str:
|
107
|
+
return base64.b64encode(s.encode("utf-8")).decode("utf-8")
|
108
|
+
|
109
|
+
|
110
|
+
def get_additional_resource_props(options: CreateUIResourceOptions) -> Dict[str, Any]:
|
111
|
+
props = dict(options.resourceProps or {})
|
112
|
+
|
113
|
+
if options.uiMetadata or options.metadata:
|
114
|
+
ui_prefixed_metadata = {
|
115
|
+
f"{UI_METADATA_PREFIX}{k}": v for k, v in (options.uiMetadata or {}).items()
|
116
|
+
}
|
117
|
+
props["_meta"] = {
|
118
|
+
**ui_prefixed_metadata,
|
119
|
+
**(options.metadata or {}),
|
120
|
+
**props.get("_meta", {}),
|
121
|
+
}
|
122
|
+
|
123
|
+
return props
|
124
|
+
|
125
|
+
|
126
|
+
# --------------------------
|
127
|
+
# Resource Factory
|
128
|
+
# --------------------------
|
129
|
+
|
130
|
+
def create_ui_resource(options: CreateUIResourceOptions) -> Dict[str, Any]:
|
131
|
+
if not options.uri.startswith("ui://"):
|
132
|
+
raise ValueError("MCP-UI SDK: URI must start with 'ui://'.")
|
133
|
+
|
134
|
+
if isinstance(options.content, RawHtmlContent):
|
135
|
+
actual_content = options.content.htmlString
|
136
|
+
mime_type: MimeType = "text/html"
|
137
|
+
|
138
|
+
elif isinstance(options.content, ExternalUrlContent):
|
139
|
+
actual_content = options.content.iframeUrl
|
140
|
+
mime_type = "text/uri-list"
|
141
|
+
|
142
|
+
elif isinstance(options.content, RemoteDomContent):
|
143
|
+
actual_content = options.content.script
|
144
|
+
mime_type = (
|
145
|
+
f"application/vnd.mcp-ui.remote-dom+javascript; framework={options.content.framework}" # type: ignore
|
146
|
+
)
|
147
|
+
|
148
|
+
else:
|
149
|
+
raise ValueError(f"MCP-UI SDK: Invalid content.type: {options.content}")
|
150
|
+
|
151
|
+
if options.encoding == "text":
|
152
|
+
resource = HTMLTextContent(
|
153
|
+
uri=options.uri,
|
154
|
+
mimeType=mime_type,
|
155
|
+
text=actual_content,
|
156
|
+
**get_additional_resource_props(options),
|
157
|
+
)
|
158
|
+
elif options.encoding == "blob":
|
159
|
+
resource = Base64BlobContent(
|
160
|
+
uri=options.uri,
|
161
|
+
mimeType=mime_type,
|
162
|
+
blob=utf8_to_base64(actual_content),
|
163
|
+
**get_additional_resource_props(options),
|
164
|
+
)
|
165
|
+
else:
|
166
|
+
raise ValueError(f"MCP-UI SDK: Invalid encoding type: {options.encoding}")
|
167
|
+
|
168
|
+
# ✅ return a dict so MCP can consume it
|
169
|
+
return {
|
170
|
+
"type": "resource",
|
171
|
+
"resource": asdict(resource),
|
172
|
+
}
|
173
|
+
|
174
|
+
# --------------------------
|
175
|
+
# UI Action Results
|
176
|
+
# --------------------------
|
177
|
+
|
178
|
+
@dataclass
|
179
|
+
class UIActionResultToolCall:
|
180
|
+
type: Literal["tool"] = "tool"
|
181
|
+
payload: Dict[str, Any] = field(default_factory=dict)
|
182
|
+
|
183
|
+
|
184
|
+
@dataclass
|
185
|
+
class UIActionResultPrompt:
|
186
|
+
type: Literal["prompt"] = "prompt"
|
187
|
+
payload: Dict[str, str] = field(default_factory=dict)
|
188
|
+
|
189
|
+
|
190
|
+
@dataclass
|
191
|
+
class UIActionResultLink:
|
192
|
+
type: Literal["link"] = "link"
|
193
|
+
payload: Dict[str, str] = field(default_factory=dict)
|
194
|
+
|
195
|
+
|
196
|
+
@dataclass
|
197
|
+
class UIActionResultIntent:
|
198
|
+
type: Literal["intent"] = "intent"
|
199
|
+
payload: Dict[str, Any] = field(default_factory=dict)
|
200
|
+
|
201
|
+
|
202
|
+
@dataclass
|
203
|
+
class UIActionResultNotification:
|
204
|
+
type: Literal["notify"] = "notify"
|
205
|
+
payload: Dict[str, str] = field(default_factory=dict)
|
206
|
+
|
207
|
+
|
208
|
+
UIActionResult = Union[
|
209
|
+
UIActionResultToolCall,
|
210
|
+
UIActionResultPrompt,
|
211
|
+
UIActionResultLink,
|
212
|
+
UIActionResultIntent,
|
213
|
+
UIActionResultNotification,
|
214
|
+
]
|
215
|
+
|
216
|
+
|
217
|
+
def ui_action_result_tool_call(tool_name: str, params: Dict[str, Any]) -> UIActionResultToolCall:
|
218
|
+
return UIActionResultToolCall(payload={"toolName": tool_name, "params": params})
|
219
|
+
|
220
|
+
|
221
|
+
def ui_action_result_prompt(prompt: str) -> UIActionResultPrompt:
|
222
|
+
return UIActionResultPrompt(payload={"prompt": prompt})
|
223
|
+
|
224
|
+
|
225
|
+
def ui_action_result_link(url: str) -> UIActionResultLink:
|
226
|
+
return UIActionResultLink(payload={"url": url})
|
227
|
+
|
228
|
+
|
229
|
+
def ui_action_result_intent(intent: str, params: Dict[str, Any]) -> UIActionResultIntent:
|
230
|
+
return UIActionResultIntent(payload={"intent": intent, "params": params})
|
231
|
+
|
232
|
+
|
233
|
+
def ui_action_result_notification(message: str) -> UIActionResultNotification:
|
234
|
+
return UIActionResultNotification(payload={"message": message})
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: mcp-ui
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.2
|
4
4
|
Summary: A Python SDK for building MCP UI resources (iframe, panels, text blocks, etc.) in a type-safe way.
|
5
5
|
Project-URL: Homepage, https://github.com/jameszokah/mcp-ui
|
6
6
|
Project-URL: Repository, https://github.com/jameszokah/mcp-ui
|
@@ -14,7 +14,6 @@ Classifier: Development Status :: 3 - Alpha
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
15
15
|
Classifier: Operating System :: OS Independent
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
17
|
-
Classifier: Programming Language :: Python :: 3.8
|
18
17
|
Classifier: Programming Language :: Python :: 3.9
|
19
18
|
Classifier: Programming Language :: Python :: 3.10
|
20
19
|
Classifier: Programming Language :: Python :: 3.11
|
@@ -0,0 +1,6 @@
|
|
1
|
+
mcp_ui/__init__.py,sha256=j0sF9vVV2kw0NUoeLImJIcJQjjuVrgpcKJ5Al70tUgQ,225
|
2
|
+
mcp_ui/core.py,sha256=zU3sVHe05TmnHVdswUdoOFGS9f4hHvyhYA9cLD1OJZw,6192
|
3
|
+
mcp_ui-0.1.2.dist-info/METADATA,sha256=SSdgIy-bymElwWXkW0cpSx4w9kLffJncXVwGpJqnuk0,6533
|
4
|
+
mcp_ui-0.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
5
|
+
mcp_ui-0.1.2.dist-info/licenses/LICENSE,sha256=pAZXnNE2dxxwXFIduGyn1gpvPefJtUYOYZOi3yeGG94,1068
|
6
|
+
mcp_ui-0.1.2.dist-info/RECORD,,
|
mcp_ui-0.1.1.dist-info/RECORD
DELETED
@@ -1,4 +0,0 @@
|
|
1
|
-
mcp_ui-0.1.1.dist-info/METADATA,sha256=H5JN3O4f7KVk1NB3c8o_tX_SVNbgwBgm9anL_iR3Dkg,6583
|
2
|
-
mcp_ui-0.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
3
|
-
mcp_ui-0.1.1.dist-info/licenses/LICENSE,sha256=pAZXnNE2dxxwXFIduGyn1gpvPefJtUYOYZOi3yeGG94,1068
|
4
|
-
mcp_ui-0.1.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|