|
1 | 1 | const assert = require("node:assert/strict"); |
2 | | -const { execFileSync } = require("node:child_process"); |
| 2 | +const { readFileSync } = require("node:fs"); |
3 | 3 | const path = require("node:path"); |
4 | 4 | const test = require("node:test"); |
5 | 5 | const vm = require("node:vm"); |
6 | 6 |
|
7 | 7 | const root = path.resolve(__dirname, "../.."); |
8 | | -const python = ` |
9 | | -import json |
10 | | -
|
11 | | -import anyio |
12 | | -
|
13 | | -from docs_src.apps import tutorial001 |
14 | | -from mcp import Client |
15 | | -from mcp.client import advertise |
16 | | -from mcp.server.apps import APP_MIME_TYPE, EXTENSION_ID |
17 | | -from mcp.types import TextResourceContents |
18 | | -from stories.apps.server import RESOURCE_URI, build_server |
19 | | -
|
20 | | -
|
21 | | -async def main() -> None: |
22 | | - cases = [] |
23 | | - for name, server, uri in ( |
24 | | - ("tutorial", tutorial001.mcp, "ui://clock/app.html"), |
25 | | - ("story", build_server(), RESOURCE_URI), |
26 | | - ): |
27 | | - async with Client(server, extensions=[advertise(EXTENSION_ID, {"mimeTypes": [APP_MIME_TYPE]})]) as client: |
28 | | - resource = await client.read_resource(uri) |
29 | | - result = await client.call_tool("get_time", {}) |
30 | | - content = resource.contents[0] |
31 | | - assert isinstance(content, TextResourceContents) |
32 | | - assert content.mime_type == APP_MIME_TYPE |
33 | | - cases.append({"name": name, "html": content.text, "result": result.model_dump(mode="json", by_alias=True)}) |
34 | | - print(json.dumps(cases)) |
35 | | -
|
36 | | -
|
37 | | -anyio.run(main) |
38 | | -`; |
39 | | - |
40 | | -const cases = JSON.parse( |
41 | | - execFileSync("uv", ["run", "--frozen", "--no-sync", "python", "-c", python], { |
42 | | - cwd: root, |
43 | | - encoding: "utf8", |
44 | | - env: { |
45 | | - ...process.env, |
46 | | - PYTHONPATH: [root, path.join(root, "examples"), process.env.PYTHONPATH] |
47 | | - .filter(Boolean) |
48 | | - .join(path.delimiter), |
49 | | - }, |
50 | | - }), |
51 | | -); |
52 | | - |
53 | | -for (const { name, html, result } of cases) { |
54 | | - test(`${name} clock shows the tool-result notification`, () => { |
| 8 | +const cases = [ |
| 9 | + ["tutorial", "docs_src/apps/tutorial001.py"], |
| 10 | + ["story", "examples/stories/apps/server.py"], |
| 11 | +]; |
| 12 | + |
| 13 | +for (const [name, file] of cases) { |
| 14 | + test(`${name} clock shows CallToolResult from notification params per the MCP Apps spec`, () => { |
| 15 | + const source = readFileSync(path.join(root, file), "utf8"); |
| 16 | + const html = source.match(/CLOCK_HTML = """([\s\S]*?)"""/)?.[1]; |
| 17 | + assert.ok(html); |
55 | 18 | const script = html.match(/<script>([\s\S]*?)<\/script>/)?.[1]; |
| 19 | + const result = { content: [{ type: "text", text: "2026-06-26T12:00:00Z" }] }; |
56 | 20 | assert.ok(script); |
57 | 21 | const clock = { textContent: "..." }; |
58 | 22 | const sent = []; |
|
0 commit comments