mcp-ui 0.1.0__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.0
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
@@ -22,29 +21,30 @@ Classifier: Programming Language :: Python :: 3.12
22
21
  Requires-Python: >=3.9
23
22
  Description-Content-Type: text/markdown
24
23
 
25
-
26
24
  # MCP-UI Python SDK
27
25
 
28
26
  This library is a Python port of the MCP-UI TypeScript SDK.
29
- It provides strongly typed helpers for creating UI resources and UI actions in MCP servers, with good DX (developer experience), type safety, and MCP-compatible JSON output.
27
+ It provides strongly typed helpers for creating UI resources and UI actions in MCP servers, with good type safety, and MCP-compatible JSON output.
30
28
 
31
29
  ---
32
30
 
33
31
  ## 📦 Installation
34
32
 
35
- \`\`\`bash
33
+ ```bash
36
34
  pip install mcp-ui
37
- \`\`\`
35
+ ```
38
36
 
39
37
  ---
40
38
 
41
39
  ## 🚀 Core Concepts
42
40
 
43
41
  ### What is a UI Resource?
42
+
44
43
  A **UI resource** is a unit of UI data (e.g., an HTML snippet, iframe, or Remote DOM script) that MCP clients can render.
45
44
  This SDK helps you create them consistently with correct metadata and encodings.
46
45
 
47
46
  ### What is a UI Action?
47
+
48
48
  A **UI action result** represents an action the MCP client should take (e.g., open a link, show a prompt, call a tool).
49
49
 
50
50
  ---
@@ -55,7 +55,7 @@ A **UI action result** represents an action the MCP client should take (e.g., op
55
55
 
56
56
  #### Raw HTML
57
57
 
58
- \`\`\`python
58
+ ```python
59
59
  from mcp_ui import RawHtmlContent, CreateUIResourceOptions, create_ui_resource
60
60
 
61
61
  options = CreateUIResourceOptions(
@@ -66,11 +66,11 @@ options = CreateUIResourceOptions(
66
66
 
67
67
  resource = create_ui_resource(options)
68
68
  print(resource)
69
- \`\`\`
69
+ ```
70
70
 
71
71
  **Output:**
72
72
 
73
- \`\`\`json
73
+ ```json
74
74
  {
75
75
  "type": "resource",
76
76
  "resource": {
@@ -81,11 +81,11 @@ print(resource)
81
81
  "_meta": null
82
82
  }
83
83
  }
84
- \`\`\`
84
+ ```
85
85
 
86
86
  #### External URL (iframe)
87
87
 
88
- \`\`\`python
88
+ ```python
89
89
  from mcp_ui import ExternalUrlContent, CreateUIResourceOptions, create_ui_resource
90
90
 
91
91
  options = CreateUIResourceOptions(
@@ -95,11 +95,11 @@ options = CreateUIResourceOptions(
95
95
  )
96
96
 
97
97
  iframe_res = create_ui_resource(options)
98
- \`\`\`
98
+ ```
99
99
 
100
100
  **Output:**
101
101
 
102
- \`\`\`json
102
+ ```json
103
103
  {
104
104
  "type": "resource",
105
105
  "resource": {
@@ -110,11 +110,11 @@ iframe_res = create_ui_resource(options)
110
110
  "_meta": null
111
111
  }
112
112
  }
113
- \`\`\`
113
+ ```
114
114
 
115
115
  #### Remote DOM (React)
116
116
 
117
- \`\`\`python
117
+ ```python
118
118
  from mcp_ui import RemoteDomContent, CreateUIResourceOptions, create_ui_resource
119
119
 
120
120
  options = CreateUIResourceOptions(
@@ -124,11 +124,11 @@ options = CreateUIResourceOptions(
124
124
  )
125
125
 
126
126
  remote_res = create_ui_resource(options)
127
- \`\`\`
127
+ ```
128
128
 
129
129
  **Output (blob is Base64-encoded):**
130
130
 
131
- \`\`\`json
131
+ ```json
132
132
  {
133
133
  "type": "resource",
134
134
  "resource": {
@@ -139,7 +139,7 @@ remote_res = create_ui_resource(options)
139
139
  "_meta": null
140
140
  }
141
141
  }
142
- \`\`\`
142
+ ```
143
143
 
144
144
  ---
145
145
 
@@ -147,7 +147,7 @@ remote_res = create_ui_resource(options)
147
147
 
148
148
  You can attach metadata to resources. Keys are automatically prefixed with \`mcpui.dev/ui-\`.
149
149
 
150
- \`\`\`python
150
+ ```python
151
151
  options = CreateUIResourceOptions(
152
152
  uri="ui://demo/meta",
153
153
  content=RawHtmlContent(type="rawHtml", htmlString="<p>Meta Example</p>"),
@@ -156,11 +156,11 @@ options = CreateUIResourceOptions(
156
156
  )
157
157
 
158
158
  meta_res = create_ui_resource(options)
159
- \`\`\`
159
+ ```
160
160
 
161
- **Output includes \`_meta\`:**
161
+ **Output includes \`\_meta\`:**
162
162
 
163
- \`\`\`json
163
+ ```json
164
164
  {
165
165
  "type": "resource",
166
166
  "resource": {
@@ -173,7 +173,7 @@ meta_res = create_ui_resource(options)
173
173
  }
174
174
  }
175
175
  }
176
- \`\`\`
176
+ ```
177
177
 
178
178
  ---
179
179
 
@@ -181,14 +181,14 @@ meta_res = create_ui_resource(options)
181
181
 
182
182
  #### Tool Call
183
183
 
184
- \`\`\`python
184
+ ```python
185
185
  from mcp_ui import ui_action_result_tool_call
186
186
  action = ui_action_result_tool_call("searchTool", {"query": "MCP SDK"})
187
- \`\`\`
187
+ ```
188
188
 
189
189
  **Output:**
190
190
 
191
- \`\`\`json
191
+ ```json
192
192
  {
193
193
  "type": "tool",
194
194
  "payload": {
@@ -196,50 +196,50 @@ action = ui_action_result_tool_call("searchTool", {"query": "MCP SDK"})
196
196
  "params": { "query": "MCP SDK" }
197
197
  }
198
198
  }
199
- \`\`\`
199
+ ```
200
200
 
201
201
  #### Prompt
202
202
 
203
- \`\`\`python
203
+ ```python
204
204
  from mcp_ui import ui_action_result_prompt
205
205
  action = ui_action_result_prompt("Please confirm your choice")
206
- \`\`\`
206
+ ```
207
207
 
208
208
  **Output:**
209
209
 
210
- \`\`\`json
210
+ ```json
211
211
  {
212
212
  "type": "prompt",
213
213
  "payload": { "prompt": "Please confirm your choice" }
214
214
  }
215
- \`\`\`
215
+ ```
216
216
 
217
217
  #### Link
218
218
 
219
- \`\`\`python
219
+ ```python
220
220
  from mcp_ui import ui_action_result_link
221
221
  action = ui_action_result_link("https://example.com")
222
- \`\`\`
222
+ ```
223
223
 
224
224
  **Output:**
225
225
 
226
- \`\`\`json
226
+ ```json
227
227
  {
228
228
  "type": "link",
229
229
  "payload": { "url": "https://example.com" }
230
230
  }
231
- \`\`\`
231
+ ```
232
232
 
233
233
  #### Intent
234
234
 
235
- \`\`\`python
235
+ ```python
236
236
  from mcp_ui import ui_action_result_intent
237
237
  action = ui_action_result_intent("share", {"platform": "twitter"})
238
- \`\`\`
238
+ ```
239
239
 
240
240
  **Output:**
241
241
 
242
- \`\`\`json
242
+ ```json
243
243
  {
244
244
  "type": "intent",
245
245
  "payload": {
@@ -247,40 +247,43 @@ action = ui_action_result_intent("share", {"platform": "twitter"})
247
247
  "params": { "platform": "twitter" }
248
248
  }
249
249
  }
250
- \`\`\`
250
+ ```
251
251
 
252
252
  #### Notification
253
253
 
254
- \`\`\`python
254
+ ```python
255
255
  from mcp_ui import ui_action_result_notification
256
256
  action = ui_action_result_notification("Saved successfully!")
257
- \`\`\`
257
+ ```
258
258
 
259
259
  **Output:**
260
260
 
261
- \`\`\`json
261
+ ```json
262
262
  {
263
263
  "type": "notify",
264
264
  "payload": { "message": "Saved successfully!" }
265
265
  }
266
- \`\`\`
266
+ ```
267
267
 
268
268
  ---
269
269
 
270
270
  ## 📖 API Reference
271
271
 
272
272
  ### create_ui_resource(options: CreateUIResourceOptions) → Dict[str, Any]
273
+
273
274
  Creates a UI resource for MCP. Returns a JSON-serializable dict.
274
275
 
275
276
  **Parameters:**
276
- - \`uri\`: must start with \`ui://\`
277
- - \`content\`: one of \`RawHtmlContent\`, \`ExternalUrlContent\`, \`RemoteDomContent\`
278
- - \`encoding\`: \`"text"\` or \`"blob"\`
279
- - \`uiMetadata\`: UI-specific metadata (auto-prefixed)
280
- - \`metadata\`: General metadata
281
- - \`resourceProps\`: Extra resource fields
277
+
278
+ - `uri`: must start with `ui://`
279
+ - `content`: one of `RawHtmlContent`, `ExternalUrlContent`, `RemoteDomContent`
280
+ - `encoding`: `"text"` or `"blob"`
281
+ - `uiMetadata`: UI-specific metadata (auto-prefixed)
282
+ - `metadata`: General metadata
283
+ - `resourceProps`: Extra resource fields
282
284
 
283
285
  **Type System:**
286
+
284
287
  - **Content payloads**:
285
288
  - RawHtmlContent(htmlString)
286
289
  - ExternalUrlContent(iframeUrl)
@@ -296,11 +299,11 @@ Creates a UI resource for MCP. Returns a JSON-serializable dict.
296
299
  ## ⚙️ Notes
297
300
 
298
301
  - Internally uses dataclasses for type safety, but always returns dicts (via \`asdict()\`) for MCP compatibility.
299
- - Enforces URI format (\`ui://\` prefix).
302
+ - Enforces URI format (`ui://` prefix).
300
303
  - Auto-encodes blob resources in Base64.
301
304
 
302
305
  ---
303
306
 
304
307
  ## 📄 License
305
308
 
306
- MIT – same as the original MCP-UI SDK.
309
+ MIT – Similar to the original MCP-UI Typescript server SDK.
@@ -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.0.dist-info/METADATA,sha256=AdeUTQHlblMMQdW8JJ7CzsbVY0a0PbPQCV7G01UN0mc,6724
2
- mcp_ui-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
3
- mcp_ui-0.1.0.dist-info/licenses/LICENSE,sha256=pAZXnNE2dxxwXFIduGyn1gpvPefJtUYOYZOi3yeGG94,1068
4
- mcp_ui-0.1.0.dist-info/RECORD,,
File without changes