Legacy MCP and WebMCP tools have incompatible contracts and lose pagination #817

Open
opened 2026-10-02 13:15:09 +00:00 by kayg · 4 comments
Owner

Context: #427 rev-mcp-api review, #484, DESIGN §41. Source base c4a61e8cf090170f35b1bed3350d9de20c83ecd5. Priority: Medium. Source review only; product code is unchanged.

Evidence:

  • crates/calternal-server/src/mcp.rs:363 accepts q for calternal_search; apps/web/src/lib/webmcp/tools.ts:151 accepts query for the same name.
  • crates/calternal-server/src/mcp.rs:428 accepts a Note id for calternal_open; apps/web/src/lib/webmcp/tools.ts:156 instead accepts href and navigates the page.
  • crates/calternal-server/src/mcp.rs:436 accepts Home path and visibility flags for calternal_list_files; apps/web/src/lib/webmcp/tools.ts:166 accepts folderId.
  • apps/web/src/lib/webmcp/tools.ts:413 returns only the first 100 File rows and drops next_cursor, total and raw item IDs. The legacy MCP Files input has no cursor or limit. Mail reader inputs at mcp.rs:404 also omit the page cursor supported by the API.
  • mcp.rs:797 reads the Daily note for calternal_today; tools.ts:402 returns Journal, Events and Tasks from several routes. Generated registry tools already have common contracts but the legacy names remain registered.

Impact:

An agent cannot reuse a legacy call across surfaces. Legacy file and Mail list tools cannot reach later pages. The WebMCP File result also prevents an agent from obtaining the folderId needed by its own list tool without parsing a link or changing tools.

Expected:

Keep compatibility through explicit wrappers, but choose one documented shared contract for each common data tool. Give page tools cursor and limit inputs and preserve stable IDs and continuation output. Give browser navigation a distinct name from reading content. Direct legacy descriptions to the common generated action when a compatibility shape must stay.

Regression test idea:

Compare schemas and results for tools sharing a name. Use a folder and Mail thread with more than one page. Traverse every page once, preserve IDs, and verify the documented compatibility path.

Duplicate check:

Searched all issue states for calternal_list_files, parity, pagination and MCP; no specific legacy-contract drift issue found.

Context: #427 rev-mcp-api review, #484, DESIGN §41. Source base `c4a61e8cf090170f35b1bed3350d9de20c83ecd5`. Priority: Medium. Source review only; product code is unchanged. Evidence: - `crates/calternal-server/src/mcp.rs:363` accepts `q` for `calternal_search`; `apps/web/src/lib/webmcp/tools.ts:151` accepts `query` for the same name. - `crates/calternal-server/src/mcp.rs:428` accepts a Note `id` for `calternal_open`; `apps/web/src/lib/webmcp/tools.ts:156` instead accepts `href` and navigates the page. - `crates/calternal-server/src/mcp.rs:436` accepts Home `path` and visibility flags for `calternal_list_files`; `apps/web/src/lib/webmcp/tools.ts:166` accepts `folderId`. - `apps/web/src/lib/webmcp/tools.ts:413` returns only the first 100 File rows and drops next_cursor, total and raw item IDs. The legacy MCP Files input has no cursor or limit. Mail reader inputs at `mcp.rs:404` also omit the page cursor supported by the API. - `mcp.rs:797` reads the Daily note for `calternal_today`; `tools.ts:402` returns Journal, Events and Tasks from several routes. Generated registry tools already have common contracts but the legacy names remain registered. Impact: An agent cannot reuse a legacy call across surfaces. Legacy file and Mail list tools cannot reach later pages. The WebMCP File result also prevents an agent from obtaining the folderId needed by its own list tool without parsing a link or changing tools. Expected: Keep compatibility through explicit wrappers, but choose one documented shared contract for each common data tool. Give page tools cursor and limit inputs and preserve stable IDs and continuation output. Give browser navigation a distinct name from reading content. Direct legacy descriptions to the common generated action when a compatibility shape must stay. Regression test idea: Compare schemas and results for tools sharing a name. Use a folder and Mail thread with more than one page. Traverse every page once, preserve IDs, and verify the documented compatibility path. Duplicate check: Searched all issue states for `calternal_list_files`, `parity`, `pagination` and `MCP`; no specific legacy-contract drift issue found.
Author
Owner

Confirmed the contract drift in the two adapters: WebMCP Files sliced the first page and discarded stable IDs/cursor metadata, while MCP used a different search field and list shape. The Web wrapper now returns the API SearchResponse/EntryPage directly, and both MCP and Web list paths pass cursor/limit. calternal_open reads a Note; browser navigation has its own name. Legacy MCP path and q inputs remain compatibility forms and point agents to the generated shared action. Unit and integration gates are pending.

Confirmed the contract drift in the two adapters: WebMCP Files sliced the first page and discarded stable IDs/cursor metadata, while MCP used a different search field and list shape. The Web wrapper now returns the API SearchResponse/EntryPage directly, and both MCP and Web list paths pass cursor/limit. `calternal_open` reads a Note; browser navigation has its own name. Legacy MCP path and `q` inputs remain compatibility forms and point agents to the generated shared action. Unit and integration gates are pending.
Author
Owner

A deeper pagination check found that the Mail API consumes the cursor as before_received_ms plus before_id, not a cursor query field. The MCP Mail reader now accepts the page's next object, validates its stable message ID and maps both fields to the API. A regression test checks the exact query keys and the Mail cursor schema.

A deeper pagination check found that the Mail API consumes the cursor as `before_received_ms` plus `before_id`, not a `cursor` query field. The MCP Mail reader now accepts the page's `next` object, validates its stable message ID and maps both fields to the API. A regression test checks the exact query keys and the Mail cursor schema.
Author
Owner

WebMCP Mail had a pagination contract mismatch: it sent a string cursor, while the Mail API accepts before_received_ms and before_id and returns the continuation as a { received_ms, id } object. WebMCP now accepts that exact returned object, validates its timestamp and stable message ID, and maps it to the paired query fields for folder, inbox, and thread routes. Regression coverage checks all three request paths and rejects malformed cursors. Final Web gates are pending.

WebMCP Mail had a pagination contract mismatch: it sent a string `cursor`, while the Mail API accepts `before_received_ms` and `before_id` and returns the continuation as a `{ received_ms, id }` object. WebMCP now accepts that exact returned object, validates its timestamp and stable message ID, and maps it to the paired query fields for folder, inbox, and thread routes. Regression coverage checks all three request paths and rejects malformed cursors. Final Web gates are pending.
Author
Owner

Independent read-only review of job/agentfix at
2a06e563ad12fe355d4656ceee39e20b28ef860d found two remaining P2 defects.
Searches across all issue states for calternal_today, calternal_open and
cursor returned this issue. Keep both findings with #817.

  1. Legacy browser calls lose compatibility.
    apps/web/src/lib/webmcp/tools.ts:199–206 changes calternal_open from
    href navigation to required id Note reading. The former input fails
    validation. Lines 209–210 replace calternal_today with
    calternal_today_agenda without an old-name wrapper. Commit d0674d64a
    shows the old contracts. #817 requires explicit compatibility wrappers.
    Preserve the old calls while keeping distinct new data and navigation
    tools. Add tests for the old href call, old empty Today call, new Note-ID
    read, and each wrapper's access check.
  2. Valid Files cursors fail on the next page.
    crates/calternal-server/src/mcp.rs:900 and
    apps/web/src/lib/webmcp/tools.ts:129 reject cursors above 1,024 bytes.
    The browser schema at line 214 repeats that cap. The API accepts 8,192
    bytes (crates/plugins/files/src/listing.rs:18, :488). Its signed cursor
    contains the full folder path and a base64 JSON payload (:187, :521).
    A valid long folder path can return a first-page cursor that neither
    legacy adapter accepts. DESIGN §41 requires thin adapters to retain the
    route contract. Match the API limit. Add a page traversal test with a
    returned cursor between 1,024 and 8,192 bytes on both surfaces.

No build, test, server or browser was run, as required by the LIGHT job.
These are source findings. No product code was changed.
Full reports: audit-findings.md and review-agentfix.md on
job/rev2-agentfix, head 2873ca5748245104458abf8f990bda11564415b3.

Independent read-only review of `job/agentfix` at `2a06e563ad12fe355d4656ceee39e20b28ef860d` found two remaining P2 defects. Searches across all issue states for `calternal_today`, `calternal_open` and `cursor` returned this issue. Keep both findings with #817. 1. Legacy browser calls lose compatibility. `apps/web/src/lib/webmcp/tools.ts:199–206` changes `calternal_open` from `href` navigation to required `id` Note reading. The former input fails validation. Lines 209–210 replace `calternal_today` with `calternal_today_agenda` without an old-name wrapper. Commit `d0674d64a` shows the old contracts. #817 requires explicit compatibility wrappers. Preserve the old calls while keeping distinct new data and navigation tools. Add tests for the old `href` call, old empty Today call, new Note-ID read, and each wrapper's access check. 2. Valid Files cursors fail on the next page. `crates/calternal-server/src/mcp.rs:900` and `apps/web/src/lib/webmcp/tools.ts:129` reject cursors above 1,024 bytes. The browser schema at line 214 repeats that cap. The API accepts 8,192 bytes (`crates/plugins/files/src/listing.rs:18`, `:488`). Its signed cursor contains the full folder path and a base64 JSON payload (`:187`, `:521`). A valid long folder path can return a first-page cursor that neither legacy adapter accepts. DESIGN §41 requires thin adapters to retain the route contract. Match the API limit. Add a page traversal test with a returned cursor between 1,024 and 8,192 bytes on both surfaces. No build, test, server or browser was run, as required by the LIGHT job. These are source findings. No product code was changed. Full reports: `audit-findings.md` and `review-agentfix.md` on `job/rev2-agentfix`, head `2873ca5748245104458abf8f990bda11564415b3`.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#817
No description provided.