Skip to content

Commit 517b4c1

Browse files
committed
Use SDK from unpkg + add CSP resourceDomains for external scripts
1 parent 07e4a86 commit 517b4c1

File tree

2 files changed

+30
-57
lines changed

2 files changed

+30
-57
lines changed

examples/qr-server/server.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,21 @@ def generate_qr(
6262
return [ImageContent(type="image", data=b64, mimeType="image/png")]
6363

6464

65+
# IMPORTANT: resourceDomains needed for CSP to allow loading SDK from unpkg.com
66+
# Without this, hosts enforcing CSP will block the external script import
6567
@mcp.resource(WIDGET_URI, mime_type="text/html")
66-
def widget() -> str:
67-
return Path(__file__).parent.joinpath("widget.html").read_text()
68+
def widget() -> dict:
69+
html = Path(__file__).parent.joinpath("widget.html").read_text()
70+
return {
71+
"text": html,
72+
"_meta": {
73+
"ui": {
74+
"csp": {
75+
"resourceDomains": ["https://unpkg.com"]
76+
}
77+
}
78+
}
79+
}
6880

6981
# HACK: Bypass SDK's restrictive mime_type validation
7082
# The SDK pattern doesn't allow ";profile=mcp-app" but MCP spec requires it for widgets

examples/qr-server/widget.html

Lines changed: 16 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -25,67 +25,28 @@
2525
</head>
2626
<body>
2727
<div id="qr"></div>
28-
<script>
29-
const WIDTH = 340;
30-
const HEIGHT = 340;
31-
let requestId = 1;
28+
<script type="module">
29+
import { App, PostMessageTransport } from "https://unpkg.com/@modelcontextprotocol/[email protected]";
3230

33-
// Listen for messages from host
34-
window.addEventListener('message', e => {
35-
console.log('[Widget] Received message:', e);
36-
const msg = e.data;
37-
if (!msg || typeof msg !== 'object') return;
31+
const app = new App({ name: "QR Widget", version: "1.0.0" });
3832

39-
// Handle ui/notifications/tool-result
40-
if (msg.method === 'ui/notifications/tool-result') {
41-
const content = msg.params?.content;
42-
const img = content?.find(c => c.type === 'image');
43-
if (img) {
44-
const qrDiv = document.getElementById('qr');
45-
qrDiv.innerHTML = ''; // clear previous content
33+
app.ontoolresult = ({ content }) => {
34+
const img = content?.find(c => c.type === 'image');
35+
if (img) {
36+
const qrDiv = document.getElementById('qr');
37+
qrDiv.innerHTML = '';
4638

47-
// Optionally allowlist mimetypes
48-
const allowedTypes = ['image/png', 'image/jpeg', 'image/gif'];
49-
const mimeType = allowedTypes.includes(img.mimeType) ? img.mimeType : 'image/png';
39+
const allowedTypes = ['image/png', 'image/jpeg', 'image/gif'];
40+
const mimeType = allowedTypes.includes(img.mimeType) ? img.mimeType : 'image/png';
5041

51-
const image = document.createElement('img');
52-
image.src = `data:${mimeType};base64,${img.data}`;
53-
image.alt = "QR Code";
54-
qrDiv.appendChild(image);
55-
56-
// Report size to host
57-
window.parent.postMessage({
58-
jsonrpc: '2.0',
59-
method: 'ui/notifications/size-changed',
60-
params: { width: WIDTH, height: HEIGHT }
61-
}, '*');
62-
}
63-
}
64-
65-
// Handle ui/initialize response - MUST send ui/notifications/initialized
66-
if (msg.id && msg.result) {
67-
console.log('[Widget] Initialize response received:', msg.result);
68-
// Send initialized notification - HOST WAITS FOR THIS!
69-
window.parent.postMessage({
70-
jsonrpc: '2.0',
71-
method: 'ui/notifications/initialized',
72-
params: {}
73-
}, '*');
74-
console.log('[Widget] Sent ui/notifications/initialized');
42+
const image = document.createElement('img');
43+
image.src = `data:${mimeType};base64,${img.data}`;
44+
image.alt = "QR Code";
45+
qrDiv.appendChild(image);
7546
}
76-
});
47+
};
7748

78-
// Send ui/initialize request when ready
79-
window.parent.postMessage({
80-
jsonrpc: '2.0',
81-
id: requestId++,
82-
method: 'ui/initialize',
83-
params: {
84-
appInfo: { name: 'QR Widget', version: '1.0.0' },
85-
appCapabilities: {},
86-
protocolVersion: '2024-11-05'
87-
}
88-
}, '*');
49+
await app.connect(new PostMessageTransport(window.parent));
8950
</script>
9051
</body>
9152
</html>

0 commit comments

Comments
 (0)