rsc-runtime: first-class MCP Apps, listing title/_meta passthrough, SDK wire semantics for undefined - #48
Merged
Conversation
…McpResult (#44) Object properties whose value is undefined are dropped and undefined array elements lower to null, exactly as JSON.stringify serializes them, so handlers written against SDK serialization stop failing when an optional field stays undefined. Cycles, accessors, sparse arrays, non-finite numbers, and non-plain objects are still rejected, and the JSON-boundary error now names the offending key path.
…RscMcpServer, annotations verbatim (#43) RscMcpDefinition gains optional title and _meta; defineOperation preserves them through the JSON wire boundary (deep-frozen) and createRscMcpServer forwards both into registerTool, so MCP Apps hosts can bind widgets via _meta.ui.resourceUri. The factory also stops synthesizing annotation defaults - only declared hints reach the wire, where absent hints keep MCP-spec default semantics. Wire-level regression test taps the transport send to assert the serialized listing byte shape.
…ss servers (#42) McpApp children of McpServer lower into the owning server's apps record, so defineRscAgentBundle stays the single source of truth for widget-bearing plugins instead of a config-side splice. Lowering validates names, paths, ui:// resource URIs, target subsets, and JSON _meta, and admits the same app on several servers only as one identical shared declaration. The compiler now supports that shared case end to end: identical same-name declarations compile once into one mcp-apps/<name>.html output whose registry entry reaches every declaring server's agent-bundle/mcp-apps virtual module (CompiledMcpApp.serverId becomes serverIds), and source validation flags only conflicting redeclarations (AB4325) or resource URIs spread across app names (AB4330).
…owering suite (#44) The rsc-agent-runtime example asserted the old strict rejection of undefined inside structuredContent; lowerMcpResult now follows SDK serialization (drop object properties, null array elements), so the suite demonstrates the wire contract and keeps the strict rejections for functions, symbols, non-finite numbers, non-plain objects, sparse arrays, and cycles.
🦋 Changeset detectedLatest commit: 5bc190c The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
commit: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the three sibling framework gaps surfaced by the movie-library consumer port. They interact — one JSON wire boundary feeds all three — so they ship as one change set with one commit per issue.
Closes #42, Closes #43, Closes #44
#42 — MCP Apps in the RSC element tree, one app shared across servers
<McpApp>element as a child of<McpServer>: lowered into the owning server'smcp.servers[<name>].appsrecord, sodefineRscAgentBundlestays the single source of truth for widget-bearing plugins (no more config-side splice). Lowering validates app names (kebab-case), entry/template paths,ui://resource URIs, target subsets of the owning server, and JSON_meta(cloned and deep-frozen through the same wire boundary as results).entry,resourceUri,template,_meta; per-servertargetsmay differ) compile once into onemcp-apps/<name>.htmloutput, and the per-serveragent-bundle/mcp-appsvirtual registry includes it for every declaring server (CompiledMcpApp.serverId→serverIds). Source validation now flags only conflicting redeclarations (AB4325) and resource URIs spread across different app names (AB4330); identical shared declarations pass.#43 — listing-level
titleand_meta, annotations verbatimRscMcpDefinitiongains optionaltitleand_meta;defineOperationpreserves them (JSON-boundary validated, deep-frozen) andcreateRscMcpServerforwards both verbatim intoregisterTool, so hosts can bind widgets through_meta.ui.resourceUri.destructiveHint ?? false,idempotentHint ?? readOnly,openWorldHint ?? false), which rewrites MCP-spec semantics — an absent hint means the spec default applies host-side. It now emits exactly the declared hints;readOnlyHintalways (required by the definition), the rest only when the author declared them.InMemoryTransport, with the server transport'ssendtapped to capture the serialized JSON-RPC payload (the client-side parse rehydrates optional keys asundefined, so only the serialized listing proves byte shape).#44 —
undefinedfollows SDK wire semantics inlowerMcpResultstructuredContent/_metacloning now matchesJSON.stringify: object properties withundefinedvalues are dropped,undefinedarray elements lower tonull. Everything else stays strict (cycles, accessors, sparse arrays, non-finite numbers, non-plain objects, symbol keys), and the JSON-boundary error now names the offending key path (… must be JSON-serializable (cyclic value at self.inner)).Verification
pnpm build,pnpm typecheck,pnpm lint,pnpm test:unit— green.mcp,build,api,public-api,examples-contract,plugin-bundle,artifact-validator) — green, including new coverage: shared app compiled once + registry in both server bundles, conflicting-declaration rejection at plan time, and the restructured invalid-declaration diagnostics fixture.check(build + tests + typecheck) — green.@agent-bundle/rsc-runtimeminor (defineRscAgentBundle: no way to declare MCP Apps (mcp.servers[].apps) in the RSC element tree #42 element, createRscMcpServer drops tool listing title and _meta (breaks MCP Apps widget binding via _meta.ui.resourceUri) #43 slots) + patch (lowerMcpResult throws on undefined values inside structuredContent where MCP SDK serialization drops them #44),agent-bundleminor (defineRscAgentBundle: no way to declare MCP Apps (mcp.servers[].apps) in the RSC element tree #42 shared apps).