Skip to content

Read MCP Apps tool results from notification params - #3679

Open
Kludex wants to merge 3 commits into
mainfrom
codex/correct-clock-notification-example
Open

Kludex wants to merge 3 commits into
mainfrom
codex/correct-clock-notification-example

Conversation

@Kludex

@Kludex Kludex commented Oct 11, 2026

Copy link
Copy Markdown
Member

Summary

Read the MCP Apps tool-result notification from params in the clock tutorial and story server. JSON-RPC notifications do not carry a result field.

Fixes #3662

Checks

  • uv run --frozen ruff check docs_src/apps/tutorial001.py examples/stories/apps/server.py
  • uv run --frozen ruff format --check docs_src/apps/tutorial001.py examples/stories/apps/server.py
  • uv run --frozen python -m py_compile docs_src/apps/tutorial001.py examples/stories/apps/server.py
  • Claude Code CLI review: no findings

AI Disclaimer

This PR was developed with the assistance of either Claude or Codex. I've reviewed and verified the changes.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-11T13:15:18.826203Z bf951da PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Turn on auto-fix | Re-trigger cubic

@github-actions

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

📚 Documentation preview

Preview https://pr-3679.mcp-python-docs.pages.dev
Deployment https://cd64f98f.mcp-python-docs.pages.dev
Commit 3950806
Triggered by @Kludex
Updated 2026-10-11 15:00:50 UTC

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bf951daadd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs_src/apps/tutorial001.py Outdated

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Beyond the inline finding (which is pre-existing in the example, not introduced here), I also checked that params.content[0].text matches the ext-apps ui/notifications/tool-result notification shape, that these two embedded listeners are the only event.data readers in the repo, and that the prose in docs/advanced/apps.md never names the old result path, so no doc text needs updating.

Extended reasoning...

The diff is a one-line change in two embedded HTML strings (docs_src/apps/tutorial001.py and examples/stories/apps/server.py) switching the clock example's postMessage listener from event.data.result to event.data.params, which is the correct payload location for a JSON-RPC notification. No Python, SDK, or test code changes and line counts are preserved so the hl_lines references in docs/advanced/apps.md still align. The only security-relevant surface is the example iframe's unchecked message origin, which predates this PR and is covered by the inline finding.

Comment thread examples/stories/apps/server.py Outdated

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Still open from earlier reviews (1):

  • Unresolved: 1 minor or pre-existing.

Comment on lines +40 to +51
const cases = JSON.parse(
execFileSync("uv", ["run", "--frozen", "--no-sync", "python", "-c", python], {
cwd: root,
encoding: "utf8",
env: {
...process.env,
PYTHONPATH: [root, path.join(root, "examples"), process.env.PYTHONPATH]
.filter(Boolean)
.join(path.delimiter),
},
}),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 nit (optional): contributors running node --test get a Python subprocess spawned at module load, which the repo's test rules reserve for a last resort. tests/docs_src/test_apps.test.js:40 shells out to uv run --frozen --no-sync python -c before any test is registered, so a missing uv or an unsynced .venv fails the whole file with a spawn error instead of a test assertion. Fix: keep the JS test self-contained by reading CLOCK_HTML out of docs_src/apps/tutorial001.py and examples/stories/apps/server.py as text and pinning the expected tool-result text, leaving the server-side result shape to the in-memory tests in tests/docs_src/test_apps.py.

Why this was flagged

A developer runs node --test tests/docs_src/test_apps.test.js locally without having run uv sync, or without uv on PATH. At tests/docs_src/test_apps.test.js:40-51 the module-level execFileSync("uv", ["run", "--frozen", "--no-sync", "python", "-c", python], ...) runs before any test() is registered, so the failure surfaces as an uncaught spawn or ImportError for the file rather than a named test result. On the base branch there is no such file; tests/docs_src/test_apps.py already drives both tutorials in-memory. AGENTS.md says to prefer in-memory execution and use subprocesses only as a last resort, and the delegated test-quality guide says subprocesses are for when the process boundary is itself under test; here the boundary is incidental, since the only Python-sourced inputs (the HTML string and the expected text) are static literals the Node side could read from the source files. In CI the prior uv sync --frozen --all-extras --python 3.10 step at .github/workflows/shared.yml:27 makes the subprocess succeed, so nothing breaks there.

Verification: Anyone runs node --test tests/docs_src/test_apps.test.js where uv is not on PATH or the .venv has not been synced. tests/docs_src/test_apps.test.js:39-51 executes execFileSync on uv at module top level, so a missing uv throws out of module evaluation and the whole file errors rather than reporting a named failing test. The added code violates AGENTS.md:75-76 as written.

},
}),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 nit (optional): maintainers get a test that pins the params wire shape without saying where that shape comes from. tests/docs_src/test_apps.test.js:52 names the behaviour but neither the test nor the file says the payload under params is the ext-apps spec's ui/notifications/tool-result notification (spec-mandated), which is exactly the params vs result confusion behind #3662. Fix: add a one-line comment or test description stating the provenance, e.g. that ui/notifications/tool-result carries a CallToolResult as its JSON-RPC params per the MCP Apps spec, so a future reader knows a failure here is a regression and not an SDK choice.

Why this was flagged

The condition is a later reader or agent editing the clock HTML or this test and asking whether the host posts the tool result under params or result; they arrive via tests/docs_src/test_apps.test.js:51-75. The test at tests/docs_src/test_apps.test.js:72 dispatches { jsonrpc, method: "ui/notifications/tool-result", params: result } and asserts at :73, but no docstring or comment states that this shape is mandated by the ext-apps spec rather than invented for the test. On the base branch there is no JS test at all, so nothing is lost at runtime; the gap is that the new test does not carry the triage key the repository's test-quality bar asks for (AGENTS.md:68 incorporates .claude/skills/test-quality/SKILL.md, whose bullet at SKILL.md:16-19 asks docstrings to be honest about provenance: spec-mandated, SDK-defined, or pinned gap). No safeguard covers this: pyright, ruff and pytest do not read this file, and the Python test tests/docs_src/test_apps.py does not exercise the notification shape.

Verification: nit. A later reader of tests/docs_src/test_apps.test.js asks where the params-carrying ui/notifications/tool-result shape comes from. The file has no header or per-test comment; the only statements of the wire shape are the test name at :52 and the dispatch at :72. Nothing in the file says this shape is the ext-apps spec's notification rather than a test-invented payload. SKILL.md:16-19 requires stating provenance.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — the latest push adds the ui/initialize handshake and the parent-source/method guard, which closes the one substantive point from the earlier review; the remaining inline notes are optional nits.

What was reviewed:

  • The handshake in both clock scripts (request id 1 with appInfo/appCapabilities/protocolVersion, initialized notification on the response, tool-result gated behind initialized) matches the ext-apps flow, and the two files are byte-identical in their script bodies.
  • The shifted hl_lines in docs/advanced/apps.md (36 39 47 49 and 40-44) line up with Apps(), @ apps.tool, add_html_resource, MCPServer(...) and the get_time body in the new docs_src/apps/tutorial001.py.
  • All twelve i18n pages carry the same two new section hashes (sections 1 and 2, the only ones whose English text changed) with the rest unchanged, consistent with translate-tool output rather than hand edits.
  • The new CI step runs in the pre-commit job after uv sync --frozen --all-extras, so the uv run --frozen --no-sync subprocess in the Node test has a synced environment there; README does not embed this snippet, so no snippet regeneration is needed.
Extended reasoning...

The change rewrites the inline iframe JavaScript in the docs snippet docs_src/apps/tutorial001.py and the example examples/stories/apps/server.py to perform the MCP Apps UI handshake and read tool results from notification params, shifts hl_lines in docs/advanced/apps.md and the twelve generated i18n copies, adds a Node vm-based test in tests/docs_src/test_apps.test.js, and wires that test into .github/workflows/shared.yml. It touches no SDK runtime code and no auth, crypto or permission surface; the only security-relevant aspect is the iframe listener, which now checks event.source against window.parent and filters on method, addressing the earlier inline finding. The diff is small and self-contained, the handshake is exercised deterministically by the new test, the hl_lines and i18n hashes check out against the new English content, there is no CODEOWNERS file, and no third-party objection is outstanding, so approval is warranted; the two open inline nits from the prior run are optional and do not block.

Still open from earlier reviews (2):

  • Unresolved: 2 minor or pre-existing.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug in the MCP Apps documentation.

1 participant