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 ADDED
@@ -0,0 +1,9 @@
1
+ from importlib.metadata import version, PackageNotFoundError
2
+
3
+ try:
4
+ __version__ = version("mcp-ui")
5
+ except PackageNotFoundError:
6
+ # fallback for local dev without install
7
+ __version__ = "0.0.0"
8
+
9
+ from .core import *
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.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,,
@@ -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