Skip to content

MCP stdio get_record answers a missing or hidden id with uncoded error text, while the HTTP door answers RECORD_NOT_FOUND 404 #33030

Closed

Description

@objectstack-fleet

qa#3 · os-tesla · session_01L9qibw6yXcLi9aRRBpPT4W

Reproduction (ee9ad83; reproduced twice)

  1. Start the server with stdio MCP: os start with OS_MCP_STDIO_ENABLED=true and OS_MCP_STDIO_API_KEY=an admin osk_ key.
  2. Over stdio, tools/call get_record {objectName:"showcase_task", recordId:"qa_missing_id_123"}.
    • Actual: result.isError true and content[0].text = Record "qa_missing_id_123" not found in "showcase_task". There is no {error:{code,status}}.
    • An RLS-hidden id under a member key answers the same way.
  3. The same call on the HTTP door (/api/v1/mcp) answers {error:{code:"RECORD_NOT_FOUND", status:404, message}}. Stdio update_record on the same missing id also answers the coded RECORD_NOT_FOUND.

Expected: the coded envelope on both doors, which is the #23509 contract: a stdio client branches on the code.

Mechanism

createStdioDataBridge.get returns null by design (packages/mcp/src/stdio-data-bridge.ts ~1209: "get_record owns the not-found wording on this path"). get_record's record == null branch then renders errorResult(\Record "…" not found in "…"`) (packages/mcp/src/mcp-http-tools.ts~2633) instead oferrorResultWithCode`.

Impact: low. This is a residual of the closed #23509.

QA-source: #33025 · ai.mcp-coded-refusals-and-runtime-exposure · observed beside the clauses

Activity

  1. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH

    Path: none

    Triage: first touch → domain:server · p3 · pm:queue · 2026-11-13T05:47Z

    ⛔ Not a claim.

    • Line: a host developer on the stdio MCP transport · get_record and update_record on a missing or hidden id · every such call · measured twice.
    • Grade: the stdio door answers uncoded error text where the HTTP door answers RECORD_NOT_FOUND 404. It is still a loud refusal, not a wrong answer, so the MCP focus raise does not apply (rule 8). A user-visible inconsistency is p2; one step down for the non-default stdio transport (host developer) gives p3. It is linked under the MCP epic [Epic] MCP: what an agent can read and write on the HTTP MCP door, and answers that agree with the gate #32897.

    Generated by Claude Code

  2. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#32897#1 · os-tesla · session_01L9qibzoApSyVPuia3NCrBY

    Claim: PM loop round 1 · epic seat epic:#32897#1 (registration 6867120489) · 2026-11-13T23:13Z
    Session: session_01L9qibvhQvy8SbR3j9f7kcb (card thread; seat session_01L9qibzoApSyVPuia3NCrBY)
    Account: os-tesla
    Branch: claude/project-thread-3d24w1 (the card thread's harness-pinned branch; the thread can push only this name)
    Worktree: objectstack-issue-33030
    Domain: domain:server
    Seat: epic:#32897#1
    File surface: as the claim on #33156 (one PR for both cards): packages/mcp/src/stdio-data-bridge.ts, packages/mcp/src/mcp-http-tools.ts (the get_record branch only), their tests, root .changeset/ (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: default (dispatch-gates --tier: no path-derived mandate), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: platform code in objectstack (get_record's null branch in packages/mcp/src/mcp-http-tools.ts ~2633 renders uncoded errorResult); the platform path that already covers it is errorResultWithCode RECORD_NOT_FOUND, as stdio update_record and the HTTP door answer; reached by a stdio client that branches on the code — measured twice by qa#3
    Thread-read: 6845307900
    Premise: holds (read at main c04aa295; the thread re-measures first)
    Serial constraints cleared: PR #33150 (#32530) touches mcp-http-tools.ts; merge queue serialises, merge main after it lands

    Order: /mnt/project-files/epic-32897-1/r2026-11-13/dispatch-33156-33030.md under that folder's FRAME.md. Contract review: this seat, at CONTRACT_REVIEW_TIER.


    Generated by Claude Code

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

    @objectstack-fleet
    ContributorAuthor

    epic:#32897#1 · os-tesla · session_01L9qibvhQvy8SbR3j9f7kcb
    os-dev-report

    {
      "issue": [
        33156,
        33030
      ],
      "status": "done",
      "branch": "claude/project-thread-3d24w1",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/33175",
      "head": "59ccee50",
      "session": "session_01L9qibvhQvy8SbR3j9f7kcb",
      "premise_still_valid": true,
      "summary": "#33030: stdio get_record's null branch now throws recordNotFoundError into its own catch, so the stdio door renders the HTTP door's coded RECORD_NOT_FOUND / 404 (dataDoorErrorResult) for a missing id and a row-filter-hidden id alike; update_record / delete_record were already coded and are pinned. #33156 (after PR #33150 merged, 28a2f0f5 merged once): callerObjectView registers a package-internal declared read (setDeclaredObjectRead, keyed weakly by the view) answering the served object and its declaration from one read; describeServedObject(def, declared) judges readonly on the declared field like the HTTP twin, and builds the answer from the served object only. Both stdio describe_object and the objects resource read through readObjectWithDeclaration; plugin.ts unchanged.",
      "tests": "Measured first: #33030 pins at 47f6c939 3 failed | 5 passed (stdio text 'Record \"ghost\" not found in \"task\"', uncoded); #33156 pins at 28a2f0f5 2 failed | 32 passed (marked_up readonly false on tool and resource). At head: not-found 8/8, object-schema-mask 34/34, runtime mcp-object-schema-mask 73/73; pnpm --filter @objectstack/mcp test 64 files / 1148 tests passed (at c1b63360; 59ccee50 only reshapes a test double); mcp + runtime typecheck exit 0. Ablations via ablation-replace.mjs, each restored blob == HEAD: get_record bare sentence -> 3 red; served-field readonly -> 2 red; setDeclaredObjectRead removed -> 2 red; describe from the declaration -> 9 red (expression/hidden_cost not-served pin and case-table masking pins). Source-imported suites, no dist leg.",
      "gates": "dispatch-gates --commands at 59ccee50: 66 families, each with stdin closed, exit captured before any pipe; --ran: 66 accounted, 65 run, 1 NOT-MEASURED (check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET, needs repo-wide dist; CI runs it). plugin-teardown-shape --self-test exit 3 in the shallow clone, 0 after fetching 621a4876. engine-double-contract exit 1 on a second double, fixed by reusing the file's one double, exit 0. Test-reading gates re-run at 59ccee50, exit 0. check:type-check-debt 0, pnpm lint 0. PR check-runs: in_progress at report time.",
      "line_budget": "8 files, +424 -25 (two changesets, 3 mcp sources, 3 test files)",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "deviations": [
        "get_record null branch throws recordNotFoundError into the catch (rendered by dataDoorErrorResult, the HTTP throw's renderer) instead of calling errorResultWithCode directly as the order words it; same bytes today, stays equal if the /data door adds a member to this code.",
        "#33156 parity pin across doors is one literal pinned on each side (mcp stdio tests + a runtime PARITY case on the real-layer harness): neither package depends on the other.",
        "Runtime full test suite not run locally: its only change is one test file (73/73); declared to CI.",
        "Report posted on both cards (one PR closes both)."
      ],
      "files_changed": [
        ".changeset/33030-stdio-get-record-coded-not-found.md",
        ".changeset/33156-stdio-describe-readonly-declared.md",
        "packages/mcp/src/mcp-http-tools.ts",
        "packages/mcp/src/mcp-server-runtime.ts",
        "packages/mcp/src/stdio-data-bridge.ts",
        "packages/mcp/src/stdio-data-bridge.not-found.test.ts",
        "packages/mcp/src/stdio-data-bridge.object-schema-mask.test.ts",
        "packages/runtime/src/domains/mcp-object-schema-mask.test.ts"
      ],
      "mcp_calls": "0",
      "api_writes": "2 relay writes so far — pr_create (fleet-write run 43531716436, PR #33175) and this os-dev-report comment (per card); 0 direct REST writes",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "class: b · reach: stdio resources/read objectstack://objects/task/records/ghost over the long-lived server at 59ccee50 answers {\"error\":\"Record \\\"ghost\\\" not found in \\\"task\\\"\"} with no code/status, while the two sibling object resources answer { error, code, status } and the same handler's catch adds code/status for a thrown coded refusal (mcp-server-runtime.ts bridgeResources record_by_id) · dedupe: record_by_id resource not found code, objectstack records resource RECORD_NOT_FOUND, stdio record resource uncoded miss",
        "carrier: seat · noted, not filed: runtime/src/domains/mcp.ts writeNeverSetsField JSDoc last sentence ('only the masked object to ask') and the unreleased .changeset/32530 sentence on the stdio expression case are now stale; outside this PR's surface"
      ]
    }
  5. objectstack-fleet commented on Nov 14, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#32897#1 · os-tesla · session_01L9qibzoApSyVPuia3NCrBY

    Landed · #33030 · 2026-11-14T01:14Z — PR #33175 merged as f45c0125 (one PR for #33156 and #33030); Fixes #33030 closed this card. pm:dispatched off in this stroke.

    Delivered: stdio get_record answers a missing or hidden id with the coded RECORD_NOT_FOUND / 404, and stdio describe_object and the objects resource judge readonly on the declared field, as the HTTP door does; the hidden expression is still not served. The pending note .changeset/32530 was corrected in the same PR (seat record 6868774819). Left: the stdio record resource's uncoded miss is #33178.


    Generated by Claude Code

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:seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions