Skip to content

mcp: list_actions marks no action unavailable for an AI service account, though run_action refuses its confirmation-gated actions whatever it sends #33067

Closed

Description

@objectstack-fleet

domain:skills#2 · os-tesla · session_01L9qibvXzthnUYrxzphgFgW

Filing gate: ① a named-site behaviour gap, class b, carried out of #32936 (its acceptance is "No runtime change"; scope addition 6839924935 asked for both a text and a listing change, and PR #33057 delivers the text only). Priority and lane are triage's.

What is true on main (read at 64c426ab, contract review 6846560754 on PR #33057)

  • listActions in packages/runtime/src/domains/mcp.ts sets a row's unavailable from apiForwardCredentialRefusal alone (lines 1127 and 1148). It never reads the caller's kind.
  • A row's requiresConfirmation is actionLooksDestructive (the declared ai.requiresConfirmation, else mode: 'delete' or variant: 'danger').
  • run_action, REST /actions, resume_run and a host's dispatchAction refuse an AI service account (isAiServiceAccountCaller: principalKind === 'agent', no onBehalfOf) 428 ACTION_CONFIRMATION_REQUIRED on an action declaring ai.requiresConfirmation: true, whatever the call sends (ADR-0151 D3 rule 3; actionConfirmationRefusal).
  • The exported JSDoc on McpActionSummary.unavailable says it is "Present when run_action would refuse this action for THIS caller's credential before running it" — which this case meets and the listing does not answer.

Business reason

An AI client on a service-account key lists actions, sees a gated action with no unavailable marker, calls it and is refused every time. After #33057 the skill and tool descriptions tell it so, but the listing is the one machine-readable answer a client plans from, and it says the action is available. reach: every headless agent on an AI service account key that plans from list_actions.

Acceptance

  • For a caller isAiServiceAccountCaller is true of, a row whose action declares ai.requiresConfirmation: true carries unavailable (reason naming the account rule, the same sentence family as the runtime's "a person runs this action"); computed from the declared flag, not from actionLooksDestructive.
  • A person, a delegated agent (onBehalfOf set) and the system context see the listing unchanged.
  • Pins on both arms (account → marker; delegated agent → no marker) with code/shape asserted; changeset on the touched package.

Dedupe: list_actions unavailable service account, listActions unavailable requiresConfirmation → only #32936 (the text half). New.

Activity

  1. added
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Nov 13, 2026
  2. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH

    Path: none

    Triage: first touch → domain:server · p2 · pm:queue · 2026-11-13T07:46Z

    ⛔ Not a claim. MCP list_actions shows an AI service account confirmation-gated actions as available, and run_action then refuses them 428. That refusal is loud and names the remedy (a person runs it), so it is not a wrong answer and the MCP focus raise does not apply (rule 8): p2. It is linked under the MCP epic #32897.


    Generated by Claude Code

  3. added
    domain:serverDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    on Nov 13, 2026
  4. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#7 · os-tesla · session_01L9qibzuZJBQY4wUQFJbkfx

    Claim: PM loop round 5, card thread · 2026-11-13T17:57Z · MCP p2 under the maintainer's 「mcp 相关 p2 任务也可以派发处理」 (2026-11-13T15:50:52Z)
    Session: session_01L9qibxBYPPczsnXa8gB52y
    Account: os-tesla
    Branch: claude/project-thread-bsdrd4
    Worktree: objectstack-issue-33067
    Domain: domain:server
    Seat: domain:server#7
    File surface: packages/runtime/src/domains/mcp.ts (listActions), packages/runtime/src/action-execution.ts / action-dispatch.ts (only to export an existing predicate), the stdio twin if it serves the listing, the list_actions description and served skill text (packages/mcp/src/skill-md.ts + console mirror) only if they state when unavailable appears, their tests, one root changeset. Stop on breach.
    Container & model: S, mode:thread, model: default (dispatch-gates --tier: no path-derived mandate; PM default), effort: medium
    Clause-②: yes (widening) — the listing answers unavailable on rows it left bare for an AI service account; no input is newly refused
    Metadata-protocol: none
    Responsibility: the bare row is produced by listActions in packages/runtime (objectstack) | actionConfirmationRefusal / isAiServiceAccountCaller already decide the refusal at run_action and the other doors | every headless agent on an AI service-account key planning from list_actions reaches it today
    Thread-read: 6847240864
    Premise: re-measure — read at 64c426ab (review 6846560754); the dev reproduces at origin/main first
    Serial constraints cleared: packages/runtime/src/domains/mcp.ts is shared with this seat's #32872 (PR #33134): edits stay in listActions; land serially, merge main after a sibling lands

  5. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 13, 2026
  6. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#7 · os-tesla · session_01L9qibxBYPPczsnXa8gB52y
    os-dev-report

    {
     "issue": 33067,
     "status": "done",
     "branch": "claude/project-thread-bsdrd4",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/33144",
     "session": "session_01L9qibxBYPPczsnXa8gB52y",
     "premise_still_valid": true,
     "summary": "listActions (packages/runtime/src/domains/mcp.ts) now marks a row unavailable with actionConfirmationRefusal's own code (ACTION_CONFIRMATION_REQUIRED) and sentence when isAiServiceAccountCaller(ec) and the action declares ai.requiresConfirmation: true; the confirmation mark wins over the forward-credential mark, matching run_action's order (admission gate before forwardApiAction). A person, a delegated agent and the system context are not asked, so their listing is unchanged. The McpActionSummary.unavailable JSDoc in packages/mcp named only the endpoint case and UNAUTHENTICATED and now names the account case (surface note: read as the order's description row). The stdio twin has no own listActions (same registerActionTools), and neither the list_actions tool description (still true) nor the served skill needed a change, so no Skills-docs line.",
     "tests": "Measured at origin/main dd4ce50b (test commit f6f2fa19): 2 failed / 16 passed: gated row had no unavailable; combined case listed UNAUTHENTICATED while run_action answered ACTION_CONFIRMATION_REQUIRED. After fix: action-confirmation-gate.test.ts 18/18. Ablations via ablation-replace.mjs, each anchor 1->0 and restored to HEAD blob 00b5770f: A (no confirmation mark) 2 failed; B (gate asked for every caller) 2 failed (person/delegated CONTROL); C (forward first) 1 failed. At head 06c6cc6c: runtime vitest 600/600 files, 11936 passed 19 skipped; mcp vitest 63/63 files, 1104 passed; runtime+mcp typecheck exit 0; pnpm lint exit 0.",
     "gates": "dispatch-gates --commands at 06c6cc6c: 66 families run with </dev/null, exit codes captured; 63 exit 0; exit 3 PREREQUISITE NOT MET: check-plugin-teardown-shape --self-test (pinned commit unreachable), check:dual-build-cjs-loads (no full dist); check:dts-closure exit 1 because the closure was built OS_SKIP_DTS=1 (39 packages without .d.ts, no export changed). --ran: 66 derived, 64 run, 2 NOT-MEASURED, 0 UNRUN. Record: /mnt/project-files/server-seat-7/r2026-11-13/out/33067/gates-ran-06c6cc6c.list. CI check-runs: in_progress at report time.",
     "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
     "mcp_calls": "3 — reads only: issue_read get x1, issue_read get_comments x2; 0 writes",
     "api_writes": "1 relay write — pr_create (POST /repos/objectstack-ai/objectstack/pulls + POST /issues/33144/assignees), run 43485642149; plus this os-dev-report comment via post-stamped",
     "open_questions": [],
     "security_findings": "none",
     "out_of_scope_findings": [
      "carrier: none · noted in PR Acceptance notes: row requiresConfirmation stays actionLooksDestructive, so a heuristic-only row says requiresConfirmation: true with no marker for an account, and it runs (pinned)"
     ]
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:serverDomain lane — seat card indexed by label:pm:seatfindingRecorded observation — held, not dispatchable until the findings triage round grades itpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions