Skip to content

MCP: preview_* tools declare real, compact argument schemas #220

Closed

Description

@mubeda

Summary

All 13 preview_* tools on BiBCode's MCP endpoint advertise the same open schema, {"type":"object","additionalProperties":true}, plus a one-line description. Agents can't see which arguments a tool takes, so they guess, and a failed call costs a retry. The typed inputs, with field descriptions, already exist in packages/contracts/src/previewAutomation.ts. Generate compact JSON Schemas from those contracts, so tools/list tells agents the real arguments without bloating their context.

Current behavior (verified at a0ea849ef92b)

Descriptors

  • apps/server/src/production/connect_mcp.rs:1650-1664: TOOL_OPERATIONS lists 13 operations (status, open, navigate, resize, snapshot, click, type, press, scroll, evaluate, wait_for, recording_start, recording_stop). operation_wire_name is at :1666-1673, and tool_descriptors at :1675-1681.
  • preview_tool_descriptors (:1683-1704) gives every tool the description "Control the collaborative browser preview ({operation})." and inputSchema: {"type":"object","additionalProperties":true} (:1692). Only the annotations (readOnlyHint etc.) differ per operation.
  • tools/list returns these at :1023-1024.

Call path

  • :1098-1131 strips tabId and forwards the arguments as an untyped serde_json::Value to the preview broker. Failures map to PreviewAutomationExecutionError.
  • The web host casts without decoding: apps/web/src/components/preview/PreviewAutomationHosts.tsx:114, :521, :595, :624 (request.input as PreviewAutomationOpenInput, …). workspace_preview.rs:324-388 decodes the other preview RPC inputs but not automation input. I didn't trace the desktop host.
  • The Codex developer instructions already plan for guessing: apps/server/src/provider/codex/model.rs:6-15 says "A failed BiBCode preview tool call should be inspected and retried with corrected arguments".

The typed inputs already exist (packages/contracts/src/previewAutomation.ts, with field description annotations)

  • PREVIEW_AUTOMATION_V1_OPERATIONS (:29), PREVIEW_AUTOMATION_OPERATIONS (:45), PreviewAutomationTabTargetInput (:65).
  • Open (:82, with a .check filter: tabId vs reuseExistingTab: false), Navigate (:143), Resize (:182), Click (:267), Type (:306), Press (:336), Scroll (:357), Evaluate (:392), WaitFor (:421).

Other MCP tools already declare real schemas

  • device_*: apps/server/src/production/connect_mcp/device_tools.rs:374.
  • Profiler MCP: apps/server/src/diagnostics/profiler/mcp.rs:207, tested at :663.

Proposed change

  1. One source of truth. Export a JSON Schema for each preview_* input from the Effect Schemas in previewAutomation.ts, using effect's JSON Schema export (toJsonSchemaDocument). Commit them as a fixture, e.g. packages/contracts/fixtures/mcp-tools/preview-tools.json. Rust embeds it with include_str!, and vp run check:contracts fails when the fixture drifts from the contracts.

  2. Compact for agents, strict for decoding. Apply T3's compaction rules (below) only to the advertised schema:

    • no additionalProperties: true;
    • no safe-integer bounds;
    • no JSON-string fallbacks;
    • no null branch on optional fields;
    • one object for a tagged union.

    Keep the description annotations; drop titles and identifiers. Decoding keeps the full schema.

  3. Better descriptions. Replace the generic description with one sentence per operation that says what it returns, e.g. what snapshot returns and when to use wait_for.

  4. Decode at the boundary. The host decodes request.input with the contract schema (PreviewAutomationHosts.tsx), instead of casting, and returns a typed argument error the agent can act on. Check the desktop host too.

  5. Apply this to MCP server so AI agents can operate BiBCode without the UI #139. When MCP server so AI agents can operate BiBCode without the UI #139 exposes preview_*/device_* on /mcp for remote clients, it should use these generated schemas. Its design doc should require the compaction rules for any new tool.

T3 Code reference (their claims, not measured by us)

  • pingdotgg/t3code#25423, merged 2026-10-26 22:49 ART. It states the schema rules quoted above: "no additionalProperties: true, no safe-integer bounds, no JSON-string fallbacks, no null branch on optional fields, and one object for a tagged union. Decoding still uses the full schema."
  • They also merged related tools into action tools (e.g. preview_act), going from 84 to 66 tools.
  • They report the tool list shrinking from 65,789 to 43,175 characters (−34%), plus an eval table. These are T3's numbers; we haven't measured BiBCode's tools/list size.

Acceptance criteria

  • Baseline first: record the current tools/list byte size and the preview_* share of it in the PR description.
  • Contracts: packages/contracts/src/previewAutomation.test.ts covers the schema export:
    • every operation in PREVIEW_AUTOMATION_OPERATIONS gets a schema;
    • no generated schema contains additionalProperties: true, safe-integer bounds or null branches on optional fields;
    • Open's tabId / reuseExistingTab rule is kept in its description.
  • vp run check:contracts regenerates and checks the new fixture under packages/contracts/fixtures/.
  • Rust tests in apps/server/src/production/connect_mcp/tests.rs:
    • origin_snapshot_and_tool_metadata_cover_wire_variants (:396, asserts at :467-479) asserts each preview_* descriptor uses the fixture schema, with no additionalProperties: true;
    • mcp_json_rpc_and_preview_tools_preserve_wire_results (:1189, the tools/list call at :1283) still passes;
    • a new test bounds the serialized tools/list size so it can't silently regress.
  • Web: apps/web/src/components/preview/PreviewAutomationHosts.test.tsx covers an invalid input (e.g. click without a target), which returns a typed argument error instead of a runtime failure.
  • Desktop e2e: vp run test:ui:desktop, including apps/desktop/e2e/specs/platform-capabilities.e2e.ts and main-window.e2e.ts.
  • Manual: a Codex or Claude thread drives preview_open → preview_click → preview_snapshot on a local page with no argument-retry errors.
  • Gates: cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings, cargo test -p bibcode-server connect_mcp, vp check (zero warnings), vp run typecheck, vp run check:contracts, Release Smoke.
  • UI.md and vercel-react-best-practices reviews are recorded if any UI changes (none expected). docs/testing/ runbooks are reviewed and updated or stated "remain accurate".
  • Profiler coverage line in the delivery report. Tool arguments (URLs, typed text, evaluated scripts) never reach profiler records or logs.

Living docs to update

  • docs/architecture/overview.md (the MCP / preview_* section near :2553): the schema source and the compaction rules.
  • docs/superpowers/specs/2026-10-09-desktop-preview-automation-design.md: a note that MCP schemas are generated from the contracts.
  • docs/plans/2026-10-24-mcp-operate-server-design.md (MCP server so AI agents can operate BiBCode without the UI #139): reference these schemas.

Not in scope

  • Merging tools into action tools the way T3 did (84 → 66). That's a separate decision once we have our own size numbers.
  • Exposing preview_*/device_* on /mcp to remote clients, and per-call scope checks (MCP server so AI agents can operate BiBCode without the UI #139).
  • Changing device_* or profiler MCP schemas, which are already explicit.

Related

Open questions

  • Should the advertised schema live in a committed fixture (reviewable diffs) or be generated at build time? This issue assumes a fixture checked by check:contracts.
  • Does the desktop preview host validate automation input today? I only traced the web host's casts.
  • Should status, snapshot and recording ops advertise the tab-target input (:65) or an empty object?

Activity

  1. mubeda commented on Oct 31, 2026

    @mubeda
    OwnerAuthor

    Triage, 2026-10-31. Not going into PR #213 (#139 MCP batch). It goes in a follow-up PR after that release. Batch 3 (MCP), the first MCP follow-up.

    Planned order after the release: (1) #222 copy lint; (2) quick fixes #228, #221, #229, #231, #232; (3) MCP #220; (4) performance #225, then #223, #226, #227; (5) features #230, #224, #211, #212; (6) #210 after a Windows repro.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementRequested improvement or new capability.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions