Skip to content

feat(mcp): native business-action execution (list_actions / run_action) - #2307

Merged
os-zhuang merged 2 commits into
mainfrom
feat/mcp-action-tools
Jun 24, 2026
Merged

feat(mcp): native business-action execution (list_actions / run_action)#2307
os-zhuang merged 2 commits into
mainfrom
feat/mcp-action-tools

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Why

The open-source MCP server (@objectstack/mcp, Apache-2.0) let a BYO-AI client (Claude Code, Cursor, …) read/write records but could not operate the app — run its business actions/flows. Action execution rode on @objectstack/service-ai's toolRegistry, which MCP only ever optionally duck-typed (it must not hard-depend on service-ai). Now that the in-UI ask agent moved out to the closed cloud @objectstack/service-ai-studio, the framework AI runtime is headless and that bridged registry is empty in a CE runtime → MCP could CRUD rows but never trigger business logic.

An action is an open mechanism, not closed "intelligence", so this moves action execution into the open framework — bridged directly from the framework's action mechanism, with no dependency on @objectstack/service-ai.

What

Two native MCP tools, registered exactly like the existing object-CRUD tools (registerObjectToolsregisterActionTools), bound to the caller's principal:

ToolDescription
list_actionsEnumerate the invokable business actions the caller may run — permission- + visibility-filtered.
run_actionInvoke an action by name with { recordId, params }.

@objectstack/mcp (mcp-http-tools.ts)

  • New registerActionTools() + McpActionBridge seam (McpActionSummary, RegisterActionToolsOptions). Owns the tool shape; delegates execution + security to the runtime bridge — the same split as registerObjectTools.
  • Wired into handleHttpRequestby capability: only registered when the runtime bridge can resolve the action mechanism (graceful degradation, mirroring how record resources need a dataEngine). package.json unchanged — no @objectstack/service-ai.
  • run_action marked destructiveHint (clients confirm); sys_*-object actions rejected fail-closed at the tool layer.

@objectstack/runtime (http-dispatcher.ts)

  • buildMcpBridge now implements listActions + runAction, resolving + dispatching through the framework's own mechanism — IDataEngine.executeAction (script/body) / automation flow runner (flow) — bound to the request's ExecutionContext: the same permission + RLS path the REST /actions/... route uses.
  • list_actions enumerates headless-invokable actions (script/flow; UI-only url/modal/form excluded), filtered to what the caller may run; sys_*-object actions held back fail-closed.
  • The ADR-0066 D4 requiredPermissions capability gate is now single-sourced (actionPermissionError) and enforced for both the REST action route and run_action (no drift).

Referenced the now-moved service-ai/src/tools/action-tools.ts for the resolution + invocation contract (resolve by name → dispatch via the action's targetexecuteAction), and reproduced it directly in the runtime bridge without importing service-ai.

Permissions / safety

  • Actions run AS the caller (same RLS posture as the CRUD tools): row-context records load under RLS; an unseen record reads as not-found.
  • requiredPermissions enforced before dispatch; list_actions hides what the caller can't run.
  • sys_*-object actions excluded by default (fail-closed) at both the tool layer and the bridge.

Tests

  • packages/mcp/src/mcp-action-tools.test.ts — tool shapes, capability-gated registration, delegation, fail-closed sys_* guard, tool-error surfacing (mirrors the object-tools http test).
  • packages/runtime/src/http-dispatcher.test.ts — real buildMcpBridge: list_actions filtering, run_action dispatching via the action's target handler key, ADR-0066 D4 gate deny/allow, modal/unknown rejection.
  • examples/app-todo/test/mcp-actions.test.tsCE end-to-end: boots the real ObjectQL engine + app-todo handlers (NO service-ai) and drives the real MCPServerRuntime over JSON-RPC. run_action complete_task flows through engine.executeAction → the registered handler → the driver; verifies the record becomes completed, plus the fail-closed sys_* guard and the requiredPermissions gate (deny → allow) end-to-end. (pnpm --filter @objectstack/example-todo test:mcp)
✅ MCP action E2E PASSED — CE runtime lists + executes business actions, permission-enforced

Full suites green: @objectstack/mcp 53/53, @objectstack/runtime 441/441. Both packages typecheck clean.

Acceptance

  • CE runtime (open framework + @objectstack/mcp, no cloud studio) lists and executes the app's business actions, permission-enforced.
  • @objectstack/mcp has no @objectstack/service-ai dependency; existing object tools unchanged.
  • Unit + integration tests added under packages/mcp/src/ and packages/runtime/src/.
  • Verified end-to-end via an MCP client over JSON-RPC against a seeded local env.

🤖 Generated with Claude Code

Make the open-source MCP server (`@objectstack/mcp`) natively expose business
action execution, so a self-host / Community-Edition runtime (open framework +
@objectstack/mcp, no cloud studio) can *operate* an app over MCP — run its
business actions/flows — not just CRUD records.
Action execution previously rode on `@objectstack/service-ai`'s tool registry,
which the MCP server only ever optionally duck-typed. With the in-UI `ask`
agent moved out to the closed cloud package, that registry is empty in a
headless CE runtime, so BYO-AI via MCP could read/write rows but never trigger
business logic. Action execution is an open *mechanism*, so it now lives in the
open framework.
@objectstack/mcp:
- `registerActionTools()` registers two native tools alongside the object-CRUD
tools, via a `McpActionBridge` seam (mirrors `registerObjectTools` exactly):
- `list_actions` — enumerate invokable business actions the caller may run.
- `run_action` — invoke an action by name with { recordId, params }.
- Wired into `handleHttpRequest` by capability (only when the runtime bridge can
resolve the action mechanism — graceful degradation, like record resources
needing a dataEngine). No dependency on `@objectstack/service-ai`.
@objectstack/runtime:
- `buildMcpBridge` now resolves + dispatches actions through the framework's own
mechanism — `IDataEngine.executeAction` (script/body) / automation flow runner
(flow) — bound to the caller's ExecutionContext: the same permission + RLS
path the REST `/actions/...` route uses. `list_actions` is permission- and
visibility-filtered; `sys_*`-object actions are held back fail-closed.
- The ADR-0066 D4 `requiredPermissions` capability gate is single-sourced
(`actionPermissionError`) and enforced for both the REST route and run_action.
Tests:
- mcp: `mcp-action-tools.test.ts` — tool shapes, delegation, capability gating,
fail-closed sys_* guard, tool-error surfacing.
- runtime: `http-dispatcher.test.ts` — real `buildMcpBridge` listActions filtering,
run_action executeAction dispatch (via the action's target), ADR-0066 D4 gate.
- examples/app-todo: `mcp-actions.test.ts` — CE E2E booting the real engine (no
service-ai) and driving the MCP runtime over JSON-RPC: list + execute a real
action end-to-end, permission-enforced.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercelBot commented Jun 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
specCanceledCanceledJun 24, 2026 5:03pm

Request Review

@github-actionsgithub-actionsBot added size/xl documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file tests tooling and removed size/xl labels Jun 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR is very large. Consider breaking it into smaller PRs for easier review.

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/mcp, @objectstack/runtime.

17 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/concepts/cloud-artifact-api.mdx(via packages/runtime)
  • content/docs/concepts/implementation-status.mdx(via @objectstack/runtime)
  • content/docs/concepts/north-star.mdx(via packages/runtime)
  • content/docs/concepts/packages.mdx(via @objectstack/mcp, @objectstack/runtime)
  • content/docs/guides/api-reference.mdx(via @objectstack/runtime)
  • content/docs/guides/authentication.mdx(via @objectstack/runtime)
  • content/docs/guides/cloud-deployment.mdx(via @objectstack/runtime)
  • content/docs/guides/deployment-vercel.mdx(via @objectstack/runtime)
  • content/docs/guides/driver-configuration.mdx(via @objectstack/runtime)
  • content/docs/guides/hook-bodies.mdx(via @objectstack/runtime)
  • content/docs/guides/packages.mdx(via @objectstack/mcp, @objectstack/runtime)
  • content/docs/guides/plugin-chatbot-integration.mdx(via @objectstack/runtime)
  • content/docs/guides/production-readiness.mdx(via @objectstack/runtime)
  • content/docs/guides/single-project-mode.mdx(via @objectstack/runtime)
  • content/docs/protocol/objectos/http-protocol.mdx(via @objectstack/runtime)
  • content/docs/protocol/objectos/index.mdx(via @objectstack/runtime)
  • content/docs/protocol/objectos/lifecycle.mdx(via @objectstack/runtime)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@os-zhuang
os-zhuang merged commit 7d7fee7 into mainJun 24, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the feat/mcp-action-tools branch June 24, 2026 17:07
os-zhuang added a commit that referenced this pull request Jul 6, 2026
fix(app-shell): give freshly-created records a back link to their origin (#2307)
objectui@5eea2017aa42103a0f84b493aa5b3cb716361b1a
os-zhuang added a commit that referenced this pull request Jul 9, 2026
…2714 Phase 0) (#2715)
The skill's tool list covered 7 of the 9 native tools — the business-action
pair from #2307 was missing, so skill-installed agents never learned they can
run app-registered actions directly. Adds the two tools with their real
signatures, weaves actions into the intro / when-to-use / discover / workflow
sections, and teaches action preference over hand-editing records.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependenciesPull requests that update a dependency filedocumentationImprovements or additions to documentationsize/xlteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@os-zhuang