fix(runtime): surface standalone authored action rows on the MCP action bridge (#3010)#3020
Merged
Conversation
…on bridge (#3010) list_actions/run_action resolved declarations exclusively from object.actions, so a standalone authored `action` metadata row — executable since #2608 via resyncAuthoredActions — was invisible to list_actions and unresolvable by run_action, even with ai.exposed set. The bridge's declaration source is now collectActionDeclarations: object.actions unioned with standalone `action` items from metadata.loadMany('action'), keyed the same way the engine registers handlers (objectName field, legacy object field, else the 'global' wildcard) so the resolved declaration always matches the handler executeAction will find. On an objectName:name clash the object-embedded declaration wins, mirroring the execution layer's artifact-wins rule. All invoke-time gates (ai.exposed fail-closed, ADR-0066 D4 capability gate, headless-invokability, sys_* fail-closed) apply unchanged downstream of the collection. Closes #3010 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R4uCgJCaSABHDz6UntuqEj
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 16 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R4uCgJCaSABHDz6UntuqEj
os-zhuang
marked this pull request as ready for review
July 16, 2026 05:20
68 tasks
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.
Summary
Closes #3010 — the last residual from the #2605 "declared-but-not-wired" family (now closed).
A standalone authored
actionmetadata row executes since #2608 (resyncAuthoredActionsregisters its body under the declarative name), but the MCP action bridge read declarations exclusively fromobject.actions, so the row was invisible tolist_actionsand unresolvable byrun_action— even withai: { exposed: true }set. A declaration-surfacing gap, not an execution gap.Changes
collectActionDeclarations(new,http-dispatcher.ts) — the bridge's single declaration source:object.actionsunioned with standaloneactionitems frommetadata.loadMany('action'). Standalone rows resolve their owning object with the same convention as the engine registration key (objectNamefield → legacyobjectfield →'global'wildcard, kept in lockstep with the ObjectQL plugin'sactionObjectKey), so the declaration the bridge resolves always matches the handlerexecuteActionwill find. On anobjectName:nameclash the object-embedded declaration wins, mirroring the execution layer's artifact-wins rule. Tolerates metadata services withoutloadMany.listActionsandresolveActionByName(backingrun_action) now iterate that collection instead ofobject.actionsonly. Everything downstream is unchanged:ai.exposedfail-closed (Security: MCP action surface must gate onai.exposed— action bodies run trusted (unbounded RLS/FLS), so invoke-time is the only agent boundary (#2849) #2849 / ADR-0011), the ADR-0066 D4 capability gate,isHeadlessInvokableAction, sys_* fail-closed, ambiguity handling, and record-context loading ('global'rows stay record-less).Tests
7 new cases in the existing MCP-bridge suite (
http-dispatcher.test.ts): object-scoped + global standalone rows listed under the engine-key object name (params summarized); shadow dedup (embedded declaration wins, no double-listing); dispatch under the declarative-name key for body actions and theglobalwildcard key; unexposed standalone rows fail closed; sys_*-owned standalone rows blocked; and a no-loadManymetadata service keeps the pre-existing behavior.packages/runtime: 535/535 tests pass,tsc --noEmitclean.packages/mcp: 72/72 pass.🤖 Generated with Claude Code
https://claude.ai/code/session_01R4uCgJCaSABHDz6UntuqEj
Generated by Claude Code