Skip to content

test-servers: mcp_app_demo preset + _meta support on tool/resource definitions #1557

Closed

Description

@cliffhall

Summary

Add an mcp_app_demo tool/resource preset to test-servers/ — a minimal MCP App widget that exercises size-changed, ui/message, log notifications, and hostContext rendering end-to-end with no external server. Requires plumbing a _meta field through the composable test server's tool/resource definitions.

Why

Every Apps-host conformance feature in this decomposition needs a fixture to test against. A bundled demo widget lets unit/integration tests (and manual verification) drive the full host surface without depending on an external ext-apps server.

Reference implementation (PR #1510)

Re-implement informed by these changes at 33fac3f:

Depends on

Nothing — Wave 1 foundation, parallel-safe. The CLI --app-info tests and Apps-host feature tests will use this preset.

Notes

  • The PR's fixture HTML contains a raw rgba(0,0,0,0.06) in an inline <style>. It's a static fixture string, not a component, so it's arguably outside the AGENTS.md color-token rule — but decide deliberately (keep with a comment, or drop the styling).
  • Update test-servers/ docs/README if the preset list is documented.

Part of the PR #1510 decomposition (see tracking issue).

Activity

  1. self-assigned this
    on Jul 1, 2026
  2. cliffhall commented on Jul 1, 2026

    @cliffhall
    MemberAuthor

    Smoke test & AGENTS.md audit — PR #1587

    Checked out v2/1557-mcp-app-demo (8059a3d1), npm install, built test-servers (tsc -p test-servers --noCheck).

    1. Smoke test (real end-to-end output)

    Method: spawned the built stdio composable test server (node test-servers/build/test-server-stdio.js) — whose default config now includes the mcp_app_demo preset — and connected a real MCP SDK client over stdio (no mocks, no Playwright). Called tools/list, resources/read on ui://demo/widget.html, and tools/call. This proves the _meta plumbing through the composable server's tool/resource definitions surfaces on the wire end-to-end:

    {
      "toolsList_meta": {
        "name": "mcp_app_demo",
        "_meta": {
          "ui": {
            "resourceUri": "ui://demo/widget.html",
            "visibility": ["model", "app"]
          }
        }
      },
      "resourcesRead_meta": {
        "uri": "ui://demo/widget.html",
        "mimeType": "text/html",
        "textIncludesDemo": true,
        "_meta": {
          "ui": {
            "csp": { "connectDomains": [], "resourceDomains": [] },
            "permissions": { "clipboard": false },
            "prefersBorder": true
          }
        }
      },
      "toolCall_result": [
        { "type": "text", "text": "mcp_app_demo rendered with title=\"Widget A\"" }
      ]
    }

    _meta.ui is present on both tools/list (resourceUri + visibility) and resources/read (csp / permissions / prefersBorder), and the tool handler echoes the input title. Plumbing confirmed.

    2. AGENTS.md compliance

    • Mantine: N/A — this PR only touches test-servers/ (server fixtures/presets) and one web integration test. No components, theme files, or UI surface.
    • TypeScript: clean. Grepped the full diff — no any, no eslint-disable, no @ts-ignore / @ts-expect-error / @ts-nocheck, no v8 ignore. _meta is typed Record<string, unknown> and threaded through with ...(x != null && { _meta: x }) spreads; the integration test narrows _meta via explicit local interface shapes rather than casting to any.
    • Integration suite: cd clients/web && npm run test:integration → 39 files / 802 tests passed. The new src/test/integration/mcp/mcp-app-demo.test.ts passes (3/3: tool-level _meta.ui.resourceUri, resource-level _meta.ui, and the handler echo). Note: test-servers/ itself has no coverage-gated suite (it is not under the per-file 90% gate); its _meta plumbing is exercised through this web integration test, which is the correct coverage vehicle.
    • rgba() fixture note: the single rgba(0,0,0,0.06) in MCP_APP_DEMO_HTML is justified by a source comment (test-server-fixtures.ts:766–770) — it is a self-contained static HTML fixture served into the sandbox iframe, not a Mantine component, so the --inspector-* tokens are out of scope. Comment is present and accurate.

    Verdict

    PASS. _meta plumbing works end-to-end over a real stdio MCP connection; integration suite green including the new test; TypeScript clean; no Mantine surface; the one rgba() exception is documented at the source. No violations found.

    Custom smoke script (run from repo root; not committed)
    // Spawn the stdio composable test server (default config now includes the
    // mcp_app_demo preset) and connect a real MCP client over stdio, proving
    // _meta plumbing on tools/list and resources/read end-to-end.
    import { Client } from "@modelcontextprotocol/sdk/client/index.js";
    import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
    import path from "path";
    
    const REPO = process.argv[2];
    const serverPath = path.join(REPO, "test-servers/build/test-server-stdio.js");
    
    const transport = new StdioClientTransport({ command: "node", args: [serverPath] });
    const client = new Client({ name: "smoke", version: "1.0.0" }, {});
    await client.connect(transport);
    
    const { tools } = await client.listTools();
    const demoTool = tools.find((t) => t.name === "mcp_app_demo");
    
    const DEMO_URI = "ui://demo/widget.html";
    const read = await client.readResource({ uri: DEMO_URI });
    const entry = read.contents[0];
    
    const callResult = await client.callTool({
      name: "mcp_app_demo",
      arguments: { title: "Widget A" },
    });
    
    console.log(JSON.stringify({
      toolsList_meta: { name: demoTool?.name, _meta: demoTool?._meta },
      resourcesRead_meta: {
        uri: entry?.uri,
        mimeType: entry?.mimeType,
        textIncludesDemo: String(entry?.text ?? "").includes("mcp-app-demo"),
        _meta: entry?._meta,
      },
      toolCall_result: callResult.content,
    }, null, 2));
    
    await client.close();
    process.exit(0);

    🤖 audit performed via Claude Code

  3. cliffhall commented on Jul 2, 2026

    @cliffhall
    MemberAuthor

    🔗 Implemented by PR #1587. Its code is already integrated into the Wave 1 rollup branch 1579-wave-1 (see rollup PR #1601 / verification #1600). #1587 currently targets v2/main and will auto-show as Merged once the rollup lands in v2/main. The Development panel doesn't link it now because the closing keyword is on a non-default base branch.

  4. added a commit that references this issue on Jul 28, 2026
    0d50b90
  5. added this to the v2.0.0 milestone on Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

v2Issues and PRs for v2

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions