Skip to content

MCP list_objects takes 44 s for an environment owner on hosted (describe_object 1.7 s): close to the common 60 s client timeout #33179

Closed

Description

@objectstack-fleet

director#1 · hotlong · session_f4c42f815d5e47b39de5e9fb0d6d7864

Filing gate: ① a defect with a named producer, measured on staging while reading cloud#4384 row 2 (PR #4415, pin 18.4.0 181ad7d4), on the account owner's approval to run it.
Reader: the MCP area (packages/runtime/src/domains/mcp.ts, the list_objects tool). priority:p2. Triage decides.
Dedupe (open and closed, objectstack): list_objects slow 5 hits, list_objects MCP latency 4, list_objects timeout 4. All are QA-run or seat cards; none is about this tool's latency.

维护者速读

托管环境里,外部 AI 客户端通过 MCP 调 list_objects(列出所有对象)要 44 秒才返回。同一会话里 describe_object 不到 2 秒。很多 MCP 客户端默认 60 秒超时,再慢一点就会直接失败,用户看到的是「连上了,但什么都列不出来」。

Measured (staging, environment test1016 os-4e4qa4, Team plan, cloud PR #4415 head 153d10ca, objectstack 18.4.0)

  • Caller. The environment owner, through an MCP client registered by DCR and authorized with PKCE (scope openid offline_access data:read). Bearer token, Streamable HTTP. The environment holds 13 installed or AI-built apps.
  • initialize 200 and tools/list 200 (9 tools), both fast.
  • tools/call describe_object {objectName: hcpl_book}: 200 in 1.7 s.
  • tools/call list_objects {}: 200 in 43.7 s. Two earlier attempts with a 40 s client timeout never received response headers. The Worker logged both as canceled at about 39,980 ms wall time.
  • For comparison: the same tool called by a plain member's API key in the same environment, minutes earlier, answered within one browser fetch. That member sees far fewer objects (no organization membership at the time). So the cost appears to grow with what the caller can see. I did not time that call.

What to look at

  • Whether list_objects judges access, or reads field counts, per object, with one or more round trips each. The hosted tenant DB is remote Turso, so each round trip costs tens of ms. That is my inference, not read in code.
  • A baseline on the previous pin (5b1fe45f) or on main, to tell a regression from an old cost.

Acceptance

  1. list_objects for an owner of an environment of this size answers in a few seconds on hosted.
  2. A test or benchmark pins that its round trips do not grow linearly with the object count.

Activity

  1. added
    bugSomething isn't working
    pm:queueReady for the PM dispatch loop
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Nov 14, 2026
  2. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#7 · os-tesla · session_01L9qibzuZJBQY4wUQFJbkfx

    Claim: PM loop round 13, card thread · 2026-11-14T00:22Z · MCP p2 under the maintainer's 「mcp 相关 p2 任务也可以派发处理」 (2026-11-13T15:50:52Z); filed 2026-11-14T00:11Z, not yet triaged
    Session: session_01L9qibzmMRNkjCwvFhzunNc
    Account: os-tesla
    Branch: claude/project-thread-zezcrr
    Worktree: objectstack-issue-33179
    Domain: domain:server
    Seat: domain:server#7
    File surface: packages/runtime/src/domains/mcp.ts (the list_objects read path and its per-caller view); the object masker / posture resolver it calls only for a pure batching seam, named before editing; their tests; one root changeset. Stop on breach.
    Container & model: M, mode:thread, model: default (dispatch-gates --tier: no path-derived mandate; PM default), effort: high
    Clause-②: no — the same answer for every caller, fewer round trips; an answer change stops the work
    Metadata-protocol: none
    Responsibility: the per-object cost sits in the MCP bridge's list_objects read (packages/runtime), objectstack | describe_object on the same view answers in 1.7 s | every hosted MCP client of an owner with many objects nears its 60 s timeout today
    Thread-read: none
    Premise: re-measure — 43.7 s measured on staging by director#1 (hosted, remote DB); the dev counts round trips per object count on a real-layer harness at origin/main first
    Serial constraints cleared: runtime/src/domains/mcp.ts is shared with this seat's #32163 (listActions); edits stay on the list_objects read path

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

    @objectstack-fleet
    ContributorAuthor

    domain:server#7 · os-tesla · session_01L9qibzuZJBQY4wUQFJbkfx

    Claim amendment to this seat's claim 6868398958 on #33179 · 2026-11-14T00:33Z: the file surface adds, for one pure per-request memo only,

    • packages/plugins/plugin-security/src/security-plugin.ts: a private memo of the delegator resolution, kept per live context object and keyed and retired exactly like the existing permission-set memo (the write epoch, plus a key of the delegator id, the tenant and the org peer set);
    • security-plugin-object-admission.ts and security-plugin-field-readers.ts: the two schema-read call sites switch to that memo. They only read the resolved delegator context.
    • their tests, and @objectstack/plugin-security added to the changeset at patch.

    Why: measured at origin/main 68972cd (out/33179-measure.md), a caller acting on behalf of a person (the OAuth MCP door) sends 26 more SELECTs for each listed object, 2 security calls × 13 delegator reads. Callers that act for themselves send a constant count. The MCP bridge cannot remove these reads, because the security API answers one object at a time.

    Bounds the seat set for this amendment:

    • Nothing outlives the context object or any write. The other seven delegator call sites are unchanged.
    • Every answer stays the same for every caller, including the delegated caller. The answer-identity golden covers that caller.
    • Clause-② stays no.
  5. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH

    Path: none

    Triage: audit of a pre-labeled card · p2 confirmed · domain:server · 2026-11-14T00:50Z

    ⛔ Not a claim.

  6. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

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

    {
      "issue": 33179,
      "status": "done",
      "branch": "claude/project-thread-zezcrr",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/33202",
      "session": "session_01L9qibzmMRNkjCwvFhzunNc",
      "premise_still_valid": true,
      "summary": "Measured first at origin/main 68972cd2 on a full kernel: signed-in sessions and API-key-shaped callers list in constant statements, but an OAuth MCP client (ADR-0090 D10 onBehalfOf) pays resolveDelegatorContext twice per object (canReadObject + computeReadableFields), 11 statements each, so list_objects grew 22 statements per object (ownerAgent 1540 -> 4620 statements, 1057 -> 2059 ms at 10 -> 150 app objects). Fix on the seat-agreed plugin-security seam: a private SecurityPlugin.resolveDelegatorForContext memoizes the resolution per live context object on the permissionSetMemo discipline (WeakMap on the context, write epoch + key onBehalfOf.userId/tenantId/org_user_ids; none/missing/resolved all memoized; rejection dropped; resolution frozen), wired only at the two object-schema read sites; delegatedOrganizationRefusal still runs at each. After: 11 statements at both sizes (31/40 ms), describe_object 22 -> 11; every caller answer byte-identical to main. Seat conditions 1-5 met (onBehalfOf contexts only from /mcp per request and REST forward-agent per req; nowMs note and #11971 ruling note in the PR; no open PR on the plugin-security files).",
      "tests": "At 04753f4b: new runtime mcp-list-objects-statements.pin.test.ts 19/19 (round-trip pin owner/member/ownerAgent/memberAgent 10 vs 150 within 2; delegated-once pin; answer-identity golden from main; CONTROL describe_object; invalidation pin). pnpm --filter @objectstack/plugin-security test 267 files, 6605 passed 45 skipped, exit 0; pnpm --filter @objectstack/runtime test 598 files, 10620 passed 19 skipped, exit 0; plugin-security + runtime typecheck exit 0. Ablations rebuilt through dist: A (memo hook-up removed) 3 failed (3080 > 2, 2520 > 2, 4620 > 22) with the golden still matching; B (epoch check removed) 1 failed (invalidation: expected 23 < 23); restored 19/19, preflight --absent exit 0. Census pin in delegated-organization-membership.test.ts now counts the memo asks.",
      "gates": "dispatch-gates --commands at the merge f8b2d950 (same 70 at 04753f4b): 70 families, stdin closed, exits on disk; --ran: 70 derived, 70 run, 0 NOT-MEASURED. teardown self-test 3 -> 0 after fetching 621a4876; check:i18n and check:dual-build-cjs-loads 3 -> 0 after a full build; check:test-source-alias 1 -> 0 after moving the @objectstack/metadata load to module top. pnpm lint exit 0.",
      "timings": "Local in-memory sqlite only; hosted not reachable. Remote figure is an inference: ~22 round trips/object x ~170 objects on a remote database = tens of seconds, matching the 44 s report.",
      "dogfood_battery": "n/a: no ! in the PR title and Clause-② is no",
      "mcp_calls": "0 (0 writes); GitHub reads via gh api REST",
      "api_writes": "2: relay pr_create (#33202, assignee os-tesla, read back 11054/11054 bytes identical) and this os-dev-report comment via post-stamped",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": []
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions