QA-source: #21318 · ai.mcp-run-action-exposure-gate · clause 2
Summary
On the published 17.6.0 train, an app installed into a running runtime through the documented community path — os package install ./dist/objectstack.json (air-gapped inline install, no control plane) — registers its objects, app, flows and permission metadata, but none of its type: 'script' actions with an inline body gets a handler. Every door refuses the action, even after a restart, while MCP list_actions keeps advertising it. The same artifact booted with os start --artifact dispatches normally.
This breaks NORTH-STAR path step ④→⑤ (install into an environment, then have an Agent complete a real business operation): the exposed business action cannot run in the environment the app was installed into. It also violates ai.mcp-run-action-exposure-gate NEG1 ("list_actions advertising an action that run_action then refuses is a FAIL") and NORTH-STAR priority 4 ("MCP 面暴露的就是应用真能做的").
Reproduction (published packages, verified twice plus once by an independent agent)
npm create objectstack@17.6.0 tasks-app; add an object tasks_app_task and an action:
defineAction({ name: 'complete_task', label: 'Complete Task', objectName: 'tasks_app_task', type: 'script',
body: { language: 'js', capabilities: ['api.write'],
source: "var id = ctx.recordId; await ctx.api.object('tasks_app_task').update({ id: id, status: 'done', done: true }); return { ok: true, id: id };" },
ai: { exposed: true, description: 'Mark a task as complete: sets its status to Done and ticks the Done box.' } })
npx os build (validate/lint green).
- Empty runtime:
OS_CLOUD_URL=off npx os start -p 4340 --home ./home --auth-secret <32+ chars>; POST /api/v1/auth/sign-up/email (with Origin) for the first owner.
npx os package install ./dist/objectstack.json --runtime http://localhost:4340 --email … --password … → ✓ Package installed into the running kernel.
POST /api/v1/data/tasks_app_task {"name":"probe"} → 201.
POST /api/v1/actions/tasks_app_task/complete_task {"recordId":"<id>"}
- expected:
200 {"success":true,"data":{"ok":true,…}}
- actual:
404 {"success":false,"error":{"code":"RESOURCE_NOT_FOUND","message":"Action 'complete_task' on object 'tasks_app_task' not found"}}
- Mint
POST /api/v1/keys; MCP Streamable HTTP with x-api-key: list_actions → lists complete_task; run_action {actionName:'complete_task', recordId} → isError: true, No handler registered for action 'complete_task' on 'tasks_app_task'. Record unchanged.
- Restart the runtime (the cached manifest re-registers on boot): same 404 / same MCP error.
- Control: boot the same artifact with
npx os start --artifact ./dist/objectstack.json → step 5 answers 200 and the record reads status: done; MCP run_action → {ok:true}.
Mechanism (read from source at the subject, for the fixer to confirm)
- Action body handlers are wired in
AppPlugin start (packages/runtime/src/app-plugin.ts, the block that calls ql.registerAction(objectKey, action.name, handler, 'app:'+appId) for actions carrying an extracted body).
MarketplaceInstallLocalPlugin (packages/cloud-connection/src/marketplace-install-local-plugin.ts) registers the manifest and then, per its own comment, "Replicate[s] the AppPlugin start-time side-effects that the manifest service does NOT do on its own" — translations and seeds only. Action bodies are not among them, on the hot path or on the kernel:ready rehydrate.
list_actions reads action metadata, so it advertises what the dispatcher cannot run.
Expected
An installed package's script/body actions dispatch exactly as they do under AppPlugin — through REST /actions, MCP run_action and the Console button — or the install refuses / says loudly what it did not bring along, and list_actions never advertises an action with no handler.
Workaround for users today: deliver the app as the runtime's pinned artifact (os start --artifact / OS_ARTIFACT_URL) instead of installing it.
Generated by Claude Code
QA-source: #21318 · ai.mcp-run-action-exposure-gate · clause 2
Summary
On the published 17.6.0 train, an app installed into a running runtime through the documented community path —
os package install ./dist/objectstack.json(air-gapped inline install, no control plane) — registers its objects, app, flows and permission metadata, but none of itstype: 'script'actions with an inlinebodygets a handler. Every door refuses the action, even after a restart, while MCPlist_actionskeeps advertising it. The same artifact booted withos start --artifactdispatches normally.This breaks NORTH-STAR path step ④→⑤ (install into an environment, then have an Agent complete a real business operation): the exposed business action cannot run in the environment the app was installed into. It also violates
ai.mcp-run-action-exposure-gateNEG1 ("list_actions advertising an action that run_action then refuses is a FAIL") and NORTH-STAR priority 4 ("MCP 面暴露的就是应用真能做的").Reproduction (published packages, verified twice plus once by an independent agent)
npm create objectstack@17.6.0 tasks-app; add an objecttasks_app_taskand an action:npx os build(validate/lint green).OS_CLOUD_URL=off npx os start -p 4340 --home ./home --auth-secret <32+ chars>;POST /api/v1/auth/sign-up/email(withOrigin) for the first owner.npx os package install ./dist/objectstack.json --runtime http://localhost:4340 --email … --password …→✓ Package installed into the running kernel.POST /api/v1/data/tasks_app_task {"name":"probe"}→ 201.POST /api/v1/actions/tasks_app_task/complete_task {"recordId":"<id>"}200 {"success":true,"data":{"ok":true,…}}404 {"success":false,"error":{"code":"RESOURCE_NOT_FOUND","message":"Action 'complete_task' on object 'tasks_app_task' not found"}}POST /api/v1/keys; MCP Streamable HTTP withx-api-key:list_actions→ listscomplete_task;run_action {actionName:'complete_task', recordId}→isError: true,No handler registered for action 'complete_task' on 'tasks_app_task'. Record unchanged.npx os start --artifact ./dist/objectstack.json→ step 5 answers 200 and the record readsstatus: done; MCPrun_action→{ok:true}.Mechanism (read from source at the subject, for the fixer to confirm)
AppPluginstart (packages/runtime/src/app-plugin.ts, the block that callsql.registerAction(objectKey, action.name, handler, 'app:'+appId)for actions carrying an extractedbody).MarketplaceInstallLocalPlugin(packages/cloud-connection/src/marketplace-install-local-plugin.ts) registers the manifest and then, per its own comment, "Replicate[s] the AppPlugin start-time side-effects that the manifest service does NOT do on its own" — translations and seeds only. Action bodies are not among them, on the hot path or on thekernel:readyrehydrate.list_actionsreads action metadata, so it advertises what the dispatcher cannot run.Expected
An installed package's
script/bodyactions dispatch exactly as they do underAppPlugin— through REST/actions, MCPrun_actionand the Console button — or the install refuses / says loudly what it did not bring along, andlist_actionsnever advertises an action with no handler.Workaround for users today: deliver the app as the runtime's pinned artifact (
os start --artifact/OS_ARTIFACT_URL) instead of installing it.Generated by Claude Code