Skip to content

Commit 606816f

Browse files
committed
good spot
1 parent 22fc9d2 commit 606816f

File tree

2 files changed

+67
-34
lines changed

2 files changed

+67
-34
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// https://github.com/idosal/mcp-ui/issues/106
2+
import { type z } from 'zod'
3+
4+
// Module-level queue for render data events
5+
const renderDataQueue: Array<{ type: string; payload: any }> = []
6+
7+
// Set up global listener immediately when module loads (only in the client)
8+
if (typeof document !== 'undefined') {
9+
window.addEventListener('message', (event) => {
10+
if (event.data?.type === 'ui-lifecycle-iframe-render-data') {
11+
renderDataQueue.push(event.data)
12+
}
13+
})
14+
}
15+
16+
export function waitForRenderData<RenderData>(
17+
schema: z.ZodSchema<RenderData>,
18+
): Promise<RenderData> {
19+
return new Promise((resolve, reject) => {
20+
// Check if we already received the data
21+
const queuedEvent = renderDataQueue.find(
22+
(event) => event.type === 'ui-lifecycle-iframe-render-data',
23+
)
24+
if (queuedEvent) {
25+
const result = schema.safeParse(queuedEvent.payload.renderData)
26+
if (!result.success) {
27+
console.error('Invalid render data', queuedEvent.payload.renderData)
28+
}
29+
return result.success ? resolve(result.data) : reject(result.error)
30+
}
31+
32+
// Otherwise, set up the normal listening logic
33+
function cleanup() {
34+
window.removeEventListener('message', handleMessage)
35+
}
36+
37+
function handleMessage(event: MessageEvent) {
38+
if (event.data?.type !== 'ui-lifecycle-iframe-render-data') return
39+
40+
const result = schema.safeParse(event.data.payload)
41+
cleanup()
42+
if (!result.success) {
43+
console.error('Invalid render data', event.data.payload)
44+
}
45+
return result.success ? resolve(result.data) : reject(result.error)
46+
}
47+
48+
window.addEventListener('message', handleMessage)
49+
})
50+
}

exercises/05.advanced/02.solution.render-data/app/utils/mcp.ts

Lines changed: 17 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -96,48 +96,31 @@ function sendMcpMessage(
9696

9797
export { sendMcpMessage }
9898

99-
// Module-level queue for render data events
100-
const renderDataQueue: Array<{ type: string; payload: any }> = []
101-
102-
// Set up global listener immediately when module loads (only in the client)
103-
if (typeof document !== 'undefined') {
104-
window.addEventListener('message', (event) => {
105-
if (event.data?.type === 'ui-lifecycle-iframe-render-data') {
106-
renderDataQueue.push(event.data)
107-
}
108-
})
109-
}
110-
11199
export function waitForRenderData<RenderData>(
112100
schema: z.ZodSchema<RenderData>,
113101
): Promise<RenderData> {
114102
return new Promise((resolve, reject) => {
115-
// Check if we already received the data
116-
const queuedEvent = renderDataQueue.find(
117-
(event) => event.type === 'ui-lifecycle-iframe-render-data',
103+
const messageId = crypto.randomUUID()
104+
105+
window.parent.postMessage(
106+
{ type: 'ui-request-render-data', messageId },
107+
'*',
118108
)
119-
if (queuedEvent) {
120-
const result = schema.safeParse(queuedEvent.payload.renderData)
121-
if (!result.success) {
122-
console.error('Invalid render data', queuedEvent.payload.renderData)
123-
}
124-
return result.success ? resolve(result.data) : reject(result.error)
125-
}
126109

127-
// Otherwise, set up the normal listening logic
128-
function cleanup() {
110+
function handleMessage(event: MessageEvent) {
111+
if (event.data?.type !== 'ui-message-response') return
112+
if (event.data.messageId !== messageId) return
129113
window.removeEventListener('message', handleMessage)
130-
}
131114

132-
function handleMessage(event: MessageEvent) {
133-
if (event.data?.type !== 'ui-lifecycle-iframe-render-data') return
134-
135-
const result = schema.safeParse(event.data.payload)
136-
cleanup()
137-
if (!result.success) {
138-
console.error('Invalid render data', event.data.payload)
139-
}
140-
return result.success ? resolve(result.data) : reject(result.error)
115+
const { response, error } = event.data.payload
116+
117+
if (error) return reject(error)
118+
if (!schema) return resolve(response)
119+
120+
const parseResult = schema.safeParse(response)
121+
if (!parseResult.success) return reject(parseResult.error)
122+
123+
return resolve(parseResult.data)
141124
}
142125

143126
window.addEventListener('message', handleMessage)

0 commit comments

Comments
 (0)