Skip to content

fix: [#986] follow tools/list pagination so no tools are dropped#987

Draft
sahrizvi wants to merge 4 commits into
fix/remote-mcp-fabric-compatfrom
fix/mcp-tools-list-pagination
Draft

fix: [#986] follow tools/list pagination so no tools are dropped#987
sahrizvi wants to merge 4 commits into
fix/remote-mcp-fabric-compatfrom
fix/mcp-tools-list-pagination

Conversation

@sahrizvi

@sahrizvi sahrizvi commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

What

listToolsLenient fetched only the first tools/list page and ignored nextCursor, so any server that paginates its tool catalog had every tool past the first page silently dropped — no error, no warning. Both the strict client.listTools() path and the permissive-schema fallback were affected.

Changes

  • Follow nextCursor, concatenating tools across pages, in listToolsLenient.
  • Thread the cursor through both the strict and lenient request paths. Once a schema error switches to the lenient path, stay lenient for the remaining pages (a non-compliant server rarely complies later).
  • Cap at MAX_TOOL_LIST_PAGES (100) to guard against a repeating/never-terminating cursor, logging a warning if hit.
  • Tests: strict multi-page pagination, and pagination that continues through the lenient fallback (cursor is passed on each page).

Notes

  • Targets fix/remote-mcp-fabric-compat — the fix lives in listToolsLenient, which that branch introduces. Rebase onto main if it lands first.
  • Compliant single-page servers are unaffected (one request, no cursor).

Fixes #986


Summary by cubic

Fixes dropped tools from paginated tool catalogs by following nextCursor in listToolsLenient. Applies to both strict and lenient paths; fixes #986.

  • Bug Fixes
    • Follow nextCursor and accumulate tools across pages in listToolsLenient.
    • Pass cursor to both client.listTools() and the lenient client.request(); stay lenient after a schema error.
    • Cap pagination with MAX_TOOL_LIST_PAGES (100) and log a warning if hit.
    • Add tests for strict multi-page pagination and lenient fallback mid-pagination.

Written for commit 5b75ca4. Summary will update on new commits.

Review in cubic

suryaiyer95 and others added 4 commits May 5, 2026 11:28
…abric, etc.)

Three coupled fixes that together let altimate-code connect to MCP servers
gated by short-lived bearer tokens — most prominently Microsoft Fabric Core
MCP — without a local proxy or per-hour config edits.

Issues addressed:
- #791 (dynamic auth for remote MCP servers): static `headers` only.
- #792 (external MCP servers not auto-activated at session start): mostly
  caused by OAuth dynamic-client-registration probes pre-empting valid
  bearer headers and ending the connect in a non-`connected` status.
- A third defect surfaced during this work: the strict
  `ListToolsResultSchema` from `@modelcontextprotocol/sdk` rejects the
  `null` annotation hints that Fabric returns, causing `listTools()` to
  throw and the connection to be marked failed even after a successful
  initialize. None of these are altimate-specific — they all reproduce on
  upstream `opencode`.

Changes:
1. `McpRemote.headersCommand: Record<string, string[]>` — header values
   produced by running an argv command (executed via `execFile`, not a
   shell, so values aren't subject to shell injection). Resolved on every
   connect so expiring tokens refresh automatically. Example:
       "headersCommand": {
         "Authorization": ["sh", "-c",
           "printf 'Bearer %s' \"$(az account get-access-token ... -o tsv)\""]
       }
2. OAuth provider is no longer attached when the user supplies an
   explicit `Authorization` header (statically or via `headersCommand`)
   and `oauth` is not specified. Prevents Entra ID's DCR rejection from
   short-circuiting a bearer-authenticated connection. Behavior is
   unchanged when `oauth` is explicitly configured.
3. `listTools()` is wrapped to retry with a permissive Zod schema on
   schema-validation errors. The fast path is unchanged for compliant
   servers; non-compliant servers (Fabric returns `null` for
   `annotations.{readOnlyHint,destructiveHint,idempotentHint,openWorldHint}`)
   no longer fail the connection.
4. `normalizeMcpConfig` was silently dropping `oauth` and (would have
   dropped) `headersCommand` when reconstructing remote entries. Both
   are now passed through. This was a pre-existing latent bug that made
   `oauth: false` configurations no-op at runtime.

Tests:
- 22 new tests across `test/mcp/mcp-bearer-auth.test.ts` (lenient schema,
  `headersCommand` resolution + execFile-not-shell semantics, Authorization
  detection, schema round-trip) and `test/mcp/headers.test.ts` (OAuth
  auto-disable on static bearer, on `headersCommand`, and explicit-OAuth
  override). All pass.
- Zero regressions vs `origin/main` baseline on existing 185 MCP tests.
- Live-verified end-to-end against `https://api.fabric.microsoft.com/v1/mcp/core`
  using `headersCommand` + `az account get-access-token`: server connected,
  29 tools registered, no proxy required.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Follow-up to f6b2e2d resolving all bot review comments:

- `isSchemaError`: narrow to `name === ZodError|$ZodError` AND an `issues`
  array, so unrelated errors carrying an `issues` property no longer trigger
  the lenient retry (Copilot).
- `resolveHeadersCommand`: wrap `execFile` failures with the header key
  (`headersCommand[<name>] failed: ...`) and append trimmed `stderr` so
  `mcp list` points to the exact failing command (Copilot).
- Drop the now-redundant `headersCommand failed:` prefix at the call site
  since the thrown message already names the header.
- Use Zod v4 `z.looseObject(...)` instead of deprecated `.loose()` for the
  lenient `tools/list` schemas (CodeRabbit).
- `config.ts`: reword `headersCommand` comment to clarify it is argv via
  `execFile` (no shell interpolation); document why `normalizeMcpConfig`
  passes malformed array shapes through (schema rejects them with an
  actionable `invalid_type` rather than silently dropping) (Copilot).
- Tests: remove unnecessary `as any` cast; assert against the production
  `LenientListToolsResultSchema` via `MCP._testing` instead of a duplicated
  copy; add an end-to-end `listToolsLenient` retry test using a real SDK
  `$ZodError`, and a test that array-shaped headers are rejected loudly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`listToolsLenient` fetched only the first `tools/list` page and ignored
`nextCursor`, so servers that paginate their tool catalog had every tool
past the first page silently dropped. Both the strict `client.listTools()`
path and the permissive-schema fallback are affected.

- Loop on `nextCursor`, concatenating `tools`, across pages.
- Pass the cursor through on both the strict and lenient request paths;
  once a schema error switches to the lenient path, stay lenient for the
  remaining pages.
- Cap at `MAX_TOOL_LIST_PAGES` (100) to guard against a repeating cursor.
- Add tests covering strict multi-page pagination and pagination that
  continues through the lenient fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9c4f12b3-bb8f-4c65-85d1-8c095e091404

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mcp-tools-list-pagination

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@suryaiyer95 suryaiyer95 force-pushed the fix/remote-mcp-fabric-compat branch from 5b5b8ad to 174896e Compare July 7, 2026 20:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants