Skip to content

Commit 3950806

Browse files
committed
Initialize MCP Apps clock views before tool results
1 parent e39dbe2 commit 3950806

16 files changed

Lines changed: 98 additions & 43 deletions

File tree

‎docs/advanced/apps.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ then come back.
2020

2121
## A clock with a face
2222

23-
```python title="server.py" hl_lines="17 20 28 30"
23+
```python title="server.py" hl_lines="36 39 47 49"
2424
--8<-- "docs_src/apps/tutorial001.py"
2525
```
2626

@@ -51,7 +51,7 @@ The model reads `content`; the iframe is for humans. A UI-capable host still fee
5151
the text result to the model, and a text-only client gets *only* that. So the
5252
canonical pattern is one tool, two answers. Look at `get_time` again:
5353

54-
```python title="server.py" hl_lines="21-25"
54+
```python title="server.py" hl_lines="40-44"
5555
--8<-- "docs_src/apps/tutorial001.py"
5656
```
5757

‎docs_src/apps/tutorial001.py‎

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,29 @@
77
<title>Clock</title>
88
<h1 id="now">...</h1>
99
<script>
10+
const parent = window.parent;
11+
let initialized = false;
1012
window.addEventListener("message", (event) => {
11-
const text = event.data?.params?.content?.[0]?.text;
12-
if (text) document.getElementById("now").textContent = text;
13+
if (event.source !== parent || event.data?.jsonrpc !== "2.0") return;
14+
if (event.data.id === 1 && event.data.result?.protocolVersion) {
15+
parent.postMessage({jsonrpc: "2.0", method: "ui/notifications/initialized"}, "*");
16+
initialized = true;
17+
return;
18+
}
19+
if (!initialized || event.data.method !== "ui/notifications/tool-result") return;
20+
const text = event.data.params?.content?.[0]?.text;
21+
if (typeof text === "string") document.getElementById("now").textContent = text;
1322
});
23+
parent.postMessage({
24+
jsonrpc: "2.0",
25+
id: 1,
26+
method: "ui/initialize",
27+
params: {
28+
appInfo: {name: "clock", version: "1.0.0"},
29+
appCapabilities: {},
30+
protocolVersion: "2026-01-26"
31+
}
32+
}, "*");
1433
</script>
1534
"""
1635

‎examples/stories/apps/server.py‎

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,29 @@
1717
<title>Current time</title>
1818
<h1 id="now">…</h1>
1919
<script>
20+
const parent = window.parent;
21+
let initialized = false;
2022
window.addEventListener("message", (event) => {
21-
const text = event.data?.params?.content?.[0]?.text;
22-
if (text) document.getElementById("now").textContent = text;
23+
if (event.source !== parent || event.data?.jsonrpc !== "2.0") return;
24+
if (event.data.id === 1 && event.data.result?.protocolVersion) {
25+
parent.postMessage({jsonrpc: "2.0", method: "ui/notifications/initialized"}, "*");
26+
initialized = true;
27+
return;
28+
}
29+
if (!initialized || event.data.method !== "ui/notifications/tool-result") return;
30+
const text = event.data.params?.content?.[0]?.text;
31+
if (typeof text === "string") document.getElementById("now").textContent = text;
2332
});
33+
parent.postMessage({
34+
jsonrpc: "2.0",
35+
id: 1,
36+
method: "ui/initialize",
37+
params: {
38+
appInfo: {name: "clock", version: "1.0.0"},
39+
appCapabilities: {},
40+
protocolVersion: "2026-01-26"
41+
}
42+
}, "*");
2443
</script>
2544
"""
2645

‎i18n/de/pages/advanced/apps.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
translation:
3-
sections: [0355618e5f4d5fe4, 0fefa31fb7c7b585, 5c53e7487c9c70cc, 8ac39614c094f2d0, dab6ff945501ab2a, bd5565c3b2d4f959, 96819ce3d63a0487]
3+
sections: [0355618e5f4d5fe4, b4ac54f338092ec6, 1419418a05511012, 8ac39614c094f2d0, dab6ff945501ab2a, bd5565c3b2d4f959, 96819ce3d63a0487]
44
tool: 1
55
---
66
# MCP Apps {#mcp-apps}
@@ -18,7 +18,7 @@ Das SDK liefert das als eingebaute Extension `Apps` (`io.modelcontextprotocol/ui
1818

1919
## Eine Uhr mit Gesicht {#a-clock-with-a-face}
2020

21-
```python title="server.py" hl_lines="17 20 28 30"
21+
```python title="server.py" hl_lines="36 39 47 49"
2222
--8<-- "docs_src/apps/tutorial001.py"
2323
```
2424

@@ -39,7 +39,7 @@ Nicht jeder Client rendert Apps. Die Spezifikation sagt unverblümt, was das fü
3939
4040
Das Modell liest `content`; der iframe ist für Menschen. Ein UI-fähiger Host füttert das Modell trotzdem mit dem Textergebnis, und ein reiner Text-Client bekommt *nur* das. Das kanonische Muster ist also: ein Tool, zwei Antworten. Sieh dir `get_time` noch einmal an:
4141

42-
```python title="server.py" hl_lines="21-25"
42+
```python title="server.py" hl_lines="40-44"
4343
--8<-- "docs_src/apps/tutorial001.py"
4444
```
4545

‎i18n/es/pages/advanced/apps.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
translation:
3-
sections: [0355618e5f4d5fe4, 0fefa31fb7c7b585, 5c53e7487c9c70cc, 8ac39614c094f2d0, dab6ff945501ab2a, bd5565c3b2d4f959, 96819ce3d63a0487]
3+
sections: [0355618e5f4d5fe4, b4ac54f338092ec6, 1419418a05511012, 8ac39614c094f2d0, dab6ff945501ab2a, bd5565c3b2d4f959, 96819ce3d63a0487]
44
tool: 1
55
---
66
# MCP Apps {#mcp-apps}
@@ -25,7 +25,7 @@ y luego vuelve.
2525

2626
## Un reloj con cara visible {#a-clock-with-a-face}
2727

28-
```python title="server.py" hl_lines="17 20 28 30"
28+
```python title="server.py" hl_lines="36 39 47 49"
2929
--8<-- "docs_src/apps/tutorial001.py"
3030
```
3131

@@ -56,7 +56,7 @@ El modelo lee `content`; el iframe es para humanos. Un host capaz de mostrar UI
5656
el resultado en texto al modelo, y un cliente solo de texto recibe *solo* eso. Así que el
5757
patrón canónico es una herramienta, dos respuestas. Mira `get_time` de nuevo:
5858

59-
```python title="server.py" hl_lines="21-25"
59+
```python title="server.py" hl_lines="40-44"
6060
--8<-- "docs_src/apps/tutorial001.py"
6161
```
6262

‎i18n/fr/pages/advanced/apps.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
translation:
3-
sections: [0355618e5f4d5fe4, 0fefa31fb7c7b585, 5c53e7487c9c70cc, 8ac39614c094f2d0, dab6ff945501ab2a, bd5565c3b2d4f959, 96819ce3d63a0487]
3+
sections: [0355618e5f4d5fe4, b4ac54f338092ec6, 1419418a05511012, 8ac39614c094f2d0, dab6ff945501ab2a, bd5565c3b2d4f959, 96819ce3d63a0487]
44
tool: 1
55
---
66
# MCP Apps {#mcp-apps}
@@ -18,7 +18,7 @@ Le SDK fournit cela sous la forme de l’extension intégrée `Apps` (`io.modelc
1818

1919
## Une horloge avec un cadran {#a-clock-with-a-face}
2020

21-
```python title="server.py" hl_lines="17 20 28 30"
21+
```python title="server.py" hl_lines="36 39 47 49"
2222
--8<-- "docs_src/apps/tutorial001.py"
2323
```
2424

@@ -39,7 +39,7 @@ Tous les clients n’affichent pas les apps. La spécification dit sans détour
3939
4040
Le modèle lit `content` ; l’iframe est pour les humains. Un hôte capable d’afficher une interface transmet quand même le résultat textuel au modèle, et un client purement textuel ne reçoit *que* cela. Le schéma canonique est donc : un outil, deux réponses. Regardez à nouveau `get_time` :
4141

42-
```python title="server.py" hl_lines="21-25"
42+
```python title="server.py" hl_lines="40-44"
4343
--8<-- "docs_src/apps/tutorial001.py"
4444
```
4545

‎i18n/hi/pages/advanced/apps.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
translation:
3-
sections: [0355618e5f4d5fe4, 0fefa31fb7c7b585, 5c53e7487c9c70cc, 8ac39614c094f2d0, dab6ff945501ab2a, bd5565c3b2d4f959, 96819ce3d63a0487]
3+
sections: [0355618e5f4d5fe4, b4ac54f338092ec6, 1419418a05511012, 8ac39614c094f2d0, dab6ff945501ab2a, bd5565c3b2d4f959, 96819ce3d63a0487]
44
tool: 1
55
---
66
# MCP Apps {#mcp-apps}
@@ -18,7 +18,7 @@ SDK इसे built-in `Apps` extension (`io.modelcontextprotocol/ui`) के
1818

1919
## चेहरे वाली घड़ी {#a-clock-with-a-face}
2020

21-
```python title="server.py" hl_lines="17 20 28 30"
21+
```python title="server.py" hl_lines="36 39 47 49"
2222
--8<-- "docs_src/apps/tutorial001.py"
2323
```
2424

@@ -49,7 +49,7 @@ model `content` पढ़ता है; iframe इंसानों के ल
4949
model को देता है, और text-only client को **सिर्फ़** वही मिलता है। इसलिए मानक
5050
pattern है: एक tool, दो जवाब। `get_time` को फिर से देखें:
5151

52-
```python title="server.py" hl_lines="21-25"
52+
```python title="server.py" hl_lines="40-44"
5353
--8<-- "docs_src/apps/tutorial001.py"
5454
```
5555

‎i18n/ja/pages/advanced/apps.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
translation:
3-
sections: [0355618e5f4d5fe4, 0fefa31fb7c7b585, 5c53e7487c9c70cc, 8ac39614c094f2d0, dab6ff945501ab2a, bd5565c3b2d4f959, 96819ce3d63a0487]
3+
sections: [0355618e5f4d5fe4, b4ac54f338092ec6, 1419418a05511012, 8ac39614c094f2d0, dab6ff945501ab2a, bd5565c3b2d4f959, 96819ce3d63a0487]
44
tool: 1
55
---
66
# MCP Apps {#mcp-apps}
@@ -18,7 +18,7 @@ SDK はこれを組み込みの `Apps` 拡張(`io.modelcontextprotocol/ui`)
1818

1919
## 見た目のある時計 {#a-clock-with-a-face}
2020

21-
```python title="server.py" hl_lines="17 20 28 30"
21+
```python title="server.py" hl_lines="36 39 47 49"
2222
--8<-- "docs_src/apps/tutorial001.py"
2323
```
2424

@@ -39,7 +39,7 @@ HTML 自体はホストの `postMessage` を待ち受けて結果を表示しま
3939
4040
モデルが読むのは `content` で、iframe は人間のためのものです。UI に対応したホストでもテキストの結果はモデルに渡されますし、テキスト専用のクライアントはそれ「だけ」を受け取ります。ですから定番のパターンは「1 つのツール、2 つの答え」です。もう一度 `get_time` を見てください。
4141

42-
```python title="server.py" hl_lines="21-25"
42+
```python title="server.py" hl_lines="40-44"
4343
--8<-- "docs_src/apps/tutorial001.py"
4444
```
4545

‎i18n/ko/pages/advanced/apps.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
translation:
3-
sections: [0355618e5f4d5fe4, 0fefa31fb7c7b585, 5c53e7487c9c70cc, 8ac39614c094f2d0, dab6ff945501ab2a, bd5565c3b2d4f959, 96819ce3d63a0487]
3+
sections: [0355618e5f4d5fe4, b4ac54f338092ec6, 1419418a05511012, 8ac39614c094f2d0, dab6ff945501ab2a, bd5565c3b2d4f959, 96819ce3d63a0487]
44
tool: 1
55
---
66
# MCP Apps {#mcp-apps}
@@ -18,7 +18,7 @@ SDK는 이를 내장 `Apps` 확장(`io.modelcontextprotocol/ui`)으로 제공합
1818

1919
## 얼굴을 가진 시계 {#a-clock-with-a-face}
2020

21-
```python title="server.py" hl_lines="17 20 28 30"
21+
```python title="server.py" hl_lines="36 39 47 49"
2222
--8<-- "docs_src/apps/tutorial001.py"
2323
```
2424

@@ -39,7 +39,7 @@ HTML 자체는 호스트의 `postMessage`를 수신하고 결과를 표시합니
3939
4040
모델은 `content`를 읽고, iframe은 사람을 위한 것입니다. UI를 지원하는 호스트도 여전히 텍스트 결과를 모델에 전달하며, 텍스트 전용 클라이언트는 **오직** 그 텍스트만 받습니다. 따라서 표준 패턴은 도구 하나에 답 둘입니다. `get_time`을 다시 살펴보세요.
4141

42-
```python title="server.py" hl_lines="21-25"
42+
```python title="server.py" hl_lines="40-44"
4343
--8<-- "docs_src/apps/tutorial001.py"
4444
```
4545

‎i18n/pt/pages/advanced/apps.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
translation:
3-
sections: [0355618e5f4d5fe4, 0fefa31fb7c7b585, 5c53e7487c9c70cc, 8ac39614c094f2d0, dab6ff945501ab2a, bd5565c3b2d4f959, 96819ce3d63a0487]
3+
sections: [0355618e5f4d5fe4, b4ac54f338092ec6, 1419418a05511012, 8ac39614c094f2d0, dab6ff945501ab2a, bd5565c3b2d4f959, 96819ce3d63a0487]
44
tool: 1
55
---
66
# MCP Apps {#mcp-apps}
@@ -25,7 +25,7 @@ e depois volte aqui.
2525

2626
## Um relógio com uma cara {#a-clock-with-a-face}
2727

28-
```python title="server.py" hl_lines="17 20 28 30"
28+
```python title="server.py" hl_lines="36 39 47 49"
2929
--8<-- "docs_src/apps/tutorial001.py"
3030
```
3131

@@ -56,7 +56,7 @@ O modelo lê `content`; o iframe é para humanos. Um host com suporte a UI ainda
5656
o resultado em texto para o modelo, e um cliente só de texto recebe *apenas* isso. Então o
5757
padrão canônico é uma ferramenta, duas respostas. Olhe `get_time` de novo:
5858

59-
```python title="server.py" hl_lines="21-25"
59+
```python title="server.py" hl_lines="40-44"
6060
--8<-- "docs_src/apps/tutorial001.py"
6161
```
6262

0 commit comments

Comments
 (0)