Skip to content

fix: harden third-party tool result serialization - #2892

Merged
wolfib merged 4 commits into
mainfrom
harden-third-party-execution
Oct 1, 2026
Merged

wolfib merged 4 commits into
mainfrom
harden-third-party-execution

Conversation

@wolfib

@wolfib wolfib commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Hardens McpPage.executeThirdPartyDeveloperTool against CDP returnByValue serialization failures and edge-case values returned by third-party developer tools.

Previously, if a tool returned values that CDP Runtime.evaluate (returnByValue: true) could not serialize (such as Symbol, BigInt, circular arrays, deeply nested objects, or Object.create(null)), pptrPage.evaluate either threw or resolved to undefined, resulting in TypeError: Cannot read properties of undefined (reading 'stashed') and leaving window.__dtmcp.stashedElements uncleaned.

Changes

  • In-page JSON serialization: Stringify the processed tool result inside pptrPage.evaluate and JSON.parse it in Node so CDP only transfers a flat {result?: string, stashed: number} object by value.
  • Improved processToolResult handling:
    • Track active ancestors using a Set with try ... finally cleanup across both arrays and plain objects, fixing circular array recursion while preserving shared (non-circular DAG) references.
    • Treat Object.create(null) (proto === null) as a plain object and fall back to <Object instance> for non-plain objects with missing or anonymous constructors.
    • Convert symbol and bigint values to string representations.
  • Defensive cleanup & error handling:
    • Throw a descriptive error if pptrPage.evaluate resolves to undefined.
    • Ensure window.__dtmcp.stashedElements is only populated after serialization succeeds and is always cleared in a finally block.
    • Avoid appending undefined to response lines when a tool returns undefined.

@wolfib wolfib changed the title fix: harden third-party tool result serialization fix: harden third-party tool result serialization Oct 1, 2026
@wolfib
wolfib requested a review from OrKoN October 1, 2026 12:06

@OrKoN OrKoN left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please check the following:

  1. Remove the if (!result) guard, the | undefined return annotation on the evaluate callback in src/McpPage.ts, and the matching throws a descriptive error ... when evaluate resolves to undefined test in tests/McpPage.test.ts. Puppeteer only turns evaluate into undefined for "Object reference chain is too long" or "Object couldn't be returned by value" (see rewriteError in cdp/ExecutionContext.ts), and the new flat {result: string, stashed: number} return value can't trigger either one, so this branch can't run, its error message is misleading, and the test covers a case that can't happen (AGENTS.md: "Only test real scenarios").

  2. In executeThirdPartyDeveloperTool (src/McpPage.ts), don't let the cleanup pptrPage.evaluate in finally replace the original error, and skip it when result.stashed === 0. If evaluateHandle fails because the page navigated or closed, the cleanup call fails too and its error hides the real cause; the page now only sets stashedElements when it's non-empty, so the extra CDP round trip on every call with no stashed elements isn't needed.

  3. When the tool returns undefined, add an explicit response line (e.g. Tool returned no result.) in src/McpPage.ts instead of adding nothing. Right now the agent gets an empty tool response with no sign that the call succeeded, which is easy to read as a failure.

@wolfib
wolfib requested a review from OrKoN October 1, 2026 13:41
@wolfib
wolfib added this pull request to the merge queue Oct 1, 2026
Merged via the queue into main with commit 4418a7b Oct 1, 2026
23 checks passed
@wolfib
wolfib deleted the harden-third-party-execution branch October 1, 2026 14:08
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.

2 participants