#AI Agent#MCP#DevOps

Agent Tool Capability Catalog: Paginated Discovery, Generation Freezing, and Invocation Admission

A successfully fetched tool list does not mean the Agent sees a complete and still-authorized capability set. This article combines paginated discovery, descriptor validation, policy filtering, and invocation binding into a versioned catalog generation that blocks half-loaded catalogs and late-arriving changes from reaching the execution plane.

Key Points

  • Topic Focus: AI Agent、MCP、DevOps
  • Suitable for: indie hackers, full-stack engineers, AI application developers
  • Core value: quickly understand background, practical paths, common pitfalls, and engineering advice
Author @admin·Published July 26, 2026·Updated July 26, 2026

Stitching the tool list returned at Server startup into the system prompt looks like "capability discovery" is done, but it actually leaves three timing windows: the process restarts while pagination has only fetched half the pages, an admin revokes write permissions mid-session, and stale plans still call tools with original parameters after their descriptions are updated. Even with correct model selection, the call may target a tool that no longer exists, has not been fully discovered, or no longer belongs to the current principal.

MCP Tool Schema Design discusses how a single tool's parameters can be predictable, while this article handles when multiple dynamic tools constitute a usable catalog. MCP Server Authentication Boundaries distinguishes user, Server, and downstream identities; the catalog can only reflect a specific authorized view, and cannot treat the full set the admin sees as every user's capabilities.

The Catalog Is a Snapshot Artifact, Not a Mutable Array

Publish toolCatalogSnapshot.v1, pinning serverIdentity, transport endpoint digest, protocol version, audienceClass, discovery start time, policy epoch, catalog generation, page collection, tool descriptor digests, and expiry time. audienceClass can only be server-provable permission classes; it must not contain bearer tokens, and different tenants must not share caches. Each tool records a stable ID, name, input/output schema digest, risk category, whether it may have side effects, and the source contract text; the model-friendly short description is a derived field that cannot override the source contract.

yaml
schema: toolCatalogSnapshot.v1
server: inventory-mcp@sha256:7c1...
catalogGeneration: 184
audienceClass: tenant-admin.policy-42
policyEpoch: 917
protocol: mcp-2025-11-25
pageRoot: sha256:6a0...
tools:
  - id: inventory.reserve@3
    descriptor: sha256:51e...
    effectClass: RESERVATION
state: COMPLETE

MCP's tools/list supports cursor pagination, and the Server can also declare listChanged and send notifications/tools/list_changed. But the notification is only a non-transactional invalidation hint; the protocol neither guarantees a notification for every change nor provides a universal cross-page snapshot token. Two consecutive identical results can still have gone through an A-B-A change and cannot prove completeness. To enter the admission catalog, the adapter must obtain a monotonically increasing catalogRevision or snapshot token provided by the Server, and bind every page and the final recheck to the same generation; alternatively, a gateway that proxies all changes can issue that generation from linearizable storage. A source with only plain MCP pagination can only be marked UNVERSIONED_OBSERVATION, used for candidate display, and cannot grant eligibility to autonomous write calls. OpenAPI documents are likewise just a source; whether an operation is callable is still computed by the policy layer.

Discovery states are limited to DISCOVERING -> VALIDATING -> SNAPSHOT_PROVEN -> ELIGIBLE; when the source generation is missing, it enters UNVERSIONED_OBSERVATION; on exceptions it enters INCOMPLETE or QUARANTINED; after being superseded it enters RETIRED. Only SNAPSHOT_PROVEN can proceed to verify tool-name uniqueness, schema dialect, reference closure, size limits, and risk annotations. The JSON Schema dialect and vocabulary must be explicitly pinned; MCP annotations are likewise untrusted hints, and readOnlyHint alone cannot grant unconfirmed execution rights.

There Is Only One Catalog Acceptance Point

The single acceptance point is the catalog registry's CAS on the active pointer for (serverIdentity, audienceClass): the candidate must be SNAPSHOT_PROVEN, all pages and the final recheck must be bound to the same source catalog revision, and the policy epoch, source identity, and trust configuration must be unchanged before generation is repointed from 183 to 184. The source revision, page root, pointer, and catalog receipt are written in the same transaction; caches, the prompt builder, and the planner can only read that pointer. The old generation is retained until all bound runs drain.

Plan nodes store the catalog generation and descriptor digest. At the actual call, the gateway re-checks the active/compatible generation, current principal, policy epoch, whether the tool has been revoked, and the parameter schema; on pass, it issues a short-lived execution capability scoped to specific tools and resources. Agent Short-Lived Capability Leases is responsible for this non-transferable authorization. The tenant context in MCP Multi-Tenant Isolation must also enter the catalog cache key and the invocation receipt. A catalog hit only proves "once admitted" and never replaces at-call authorization.

Attack Integrity with Drift and Reordering

The fault suite must inject list_changed after page 2, A-B-A catalog changes, late notifications after the scan ends, cross-page source-revision changes, duplicate cursors, cursor loops, same-name tools with differing schemas, Server identity certificate rotation, policy revocation, and pointer CAS contention; it must also let a stale planner issue late calls. The expected outcomes are zero autonomous write tools without a source generation entering ELIGIBLE, zero half-page-set admissions, and zero cross-tenant cache hits. Tool Calling Failure Taxonomy can observe STALE_CATALOG, INCOMPLETE_DISCOVERY, and POLICY_REVOKED separately.

The acceptance gate requires that across 100,000 rounds of randomized pagination and notification interleaving, the complete set matches an independent oracle 100% of the time; acceptance of unknown schema dialects is zero; revocation propagation p99 is under 30 seconds; every execution receipt binds catalog, descriptor, and policy epoch with 100% coverage; and on catalog refresh failure the old generation continues serving only while unexpired and still authorized. Rollout first shadow-builds and compares against the existing static manifest, then opens 5% of sessions to read-only tools, and finally expands to write tools. If the missing-tool rate rises or a single privilege escalation occurs, the CAS flips the active pointer back to the previous still-fresh generation and stops issuing new leases; if the old catalog also cannot prove current authorization, the Server is shut down rather than falling back to a stale full set.

Related

Related Content