When a tool exposed to agents promises a specific behavior for a third-party API parameter, the guarantee must match the remote API’s contract. A docstring that lists sort=”stars” for GitHub’s /users/{username}/repos endpoint is misleading: that endpoint accepts created|updated|pushed|full_name, not stars or forks. The mismatch can silently return plausible but incorrect results. This guide explains the failure mode, shows a robust approach to implement the missing sort on the client side, and gives checks and trade-offs to avoid quiet bugs.
- An endpoint may silently ignore unsupported query values; docstrings must match the upstream API contract.
- If an upstream endpoint doesn't support a desired sort, implement client-side sorting but fetch a large enough set before ranking.
- Add CI checks and unit tests that verify docstring claims against the authoritative API reference and validate ranking logic with stubs.
The underlying problem
Tool surfaces (docstrings, minimal documentation served to agents) often assert a small set of allowed parameter values for convenience. If those values refer to behavior of an external API, two things can go wrong:
- The tool’s description lists values that the remote endpoint actually does not accept for that specific endpoint (for example, “stars” on GET /users/{username}/repos).
- The remote API silently ignores unknown query values rather than returning an error, so callers see a successful response in a harmless shape but with an order that doesn’t match their intent.
Those two facts combine into a failure mode that is worse than a crash: the tool appears to work, returns valid data, but the semantic contract is broken without an obvious signal.
Recommended approach
When a documented or advertised parameter maps to a capability the upstream API doesn’t provide for that endpoint, implement the capability in the client code and ensure the exposed docstring and behavior match. Key points:
- Map server-side sorts to the endpoint-allowed values; fall back to a safe default for the query string.
- For sorts the API doesn’t support server-side (e.g., stars, forks in this context), fetch a sufficiently large set of items, sort them locally using the appropriate object keys, then apply the requested limit.
- Carefully choose how many items to fetch so the ranking operates on a representative population rather than a tiny page that could miss top items.
Implementation workflow (Python, MCP tool pattern)
The following pattern is a concise, evidence-based implementation that preserves the public function signature while performing client-side sorting when necessary.
<code>_REPO_API_SORTS = {"created", "updated", "pushed", "full_name"}
_REPO_CLIENT_SORT_KEYS = {"stars": "stargazers_count", "forks": "forks_count"}
@mcp.tool()
def list_repos(sort: str = "updated", limit: int = 10) -> list:
"""List public repos. sort: updated|created|pushed|full_name|stars|forks. limit: 1-100."""
api_sort = sort if sort in _REPO_API_SORTS else "updated"
fetch_limit = 100 if sort in _REPO_CLIENT_SORT_KEYS else min(limit, 100)
repos = _gh(f"/users/{GITHUB_USERNAME}/repos?sort={api_sort}&per_page={fetch_limit}")
if sort in _REPO_CLIENT_SORT_KEYS:
repos = sorted(repos, key=lambda r: r[_REPO_CLIENT_SORT_KEYS[sort]], reverse=True)[:limit]
else:
repos = repos[:limit]
return repos
</code>
Explanation of steps:
- Define the set of sorts the GitHub endpoint accepts. Use those for the query string to avoid requesting unsupported values.
- Define mapping from client-visible sorts (stars, forks) to the repository object keys returned by the API (stargazers_count, forks_count).
- If client-side sorts are requested, request a larger page size (here, GitHub’s documented per_page ceiling of 100) so the client can rank from a useful sample rather than an arbitrarily small slice.
- Sort locally using the mapped key and then slice to the requested limit.
Edge cases and why fetch_limit matters
Common-but-subtle bug: if the code asks GitHub for only limit items then ranks them locally, the result is the top N from the returned page, not the top N overall. Example: limit=5 and sort=stars — requesting page_size=5 and then sorting those five may miss higher-starred repos that were on subsequent pages. The correct behavior is to fetch a larger set and rank across it before slicing to the user-visible limit.
Choosing the fetch size trades off bandwidth and latency against correctness. Options:
- Conservative: fetch up to the API per_page maximum (100) when client-side sorting is required. This is simple and avoids many mistaken omissions for typical accounts.
- Progressive: fetch pages iteratively until either all repos are retrieved or some heuristic threshold is met (e.g., 500 items or a maximum elapsed time). This is more complete for large accounts but more complex.
- Search API alternative: where available, use the endpoint that natively supports the desired sort (e.g., GET /search/repositories supports sort=stars). This may require different query semantics and result shapes and could differ in authentication and rate limits.
Validation strategies
Because this failure mode produces no exception, add checks that validate that the tool’s docstring matches the remote contract and that the implementation honors the advertised behavior:
- Automated contract verification: add a CI check that cross-references documented parameter enums in tool docstrings against the authoritative API reference (scraped or vetted copy). This catches mismatches where the docstring confidently states remote capabilities that don’t exist.
- Behavioral tests with stubs: unit tests that simulate the remote API response and verify client-side sorting logic. For example, return an unsorted slice and assert that the function returns the correctly ranked top-N items after sorting.
- Integration tests against the live API where feasible: run a subset of calls against the real endpoint in a test environment not hampered by limited egress. If sandbox constraints block live requests, rely on the API reference in CI to validate parameter enums.
- Telemetry and divergence alerts: in production, log when a caller requested a server-side-only sort but the implemented logic performed client-side sorting, or when the remote API ignored a requested value (if the API provides any echo). Because some APIs silently ignore unknown values, record and surface such requests for periodic review.
Common failures and mitigations
Failure: silent mismatch where caller asks for “stars” but server ignored it. Mitigation: implement client-side sort and ensure the docstring is honest about which sorts are server-side versus client-side.
Failure: insufficient fetch window (fetch_limit too small) leading to wrong top-N. Mitigation: fetch more items before sorting; for small accounts this is cheap, for large accounts consider progressive paging with a reasonable cap.
Failure: trusting only code-docstring agreement. Mitigation: add an explicit check that the tool’s claims about an upstream API are verified against that API’s published reference as part of PR or CI checks.
Trade-offs to consider
- Performance vs. correctness: fetching more items increases latency and bandwidth. For interactive agents prioritize low latency; for correctness-critical tasks (top starred repos, analytics) prioritize completeness or use the search endpoint.
- Rate limits and authentication: additional requests or larger pages might hit different rate limits. The client-side approach reduces extra API calls compared with iterative paging, but fetching large pages still counts toward quota.
- Maintenance cost: mapping client-visible semantics to different upstream endpoints increases surface area for future drift. Keep the mapping small, well-tested, and documented in one place.
Checklist for applying this pattern
- Inspect the upstream endpoint’s documented parameter enums before declaring accepted values in a docstring.
- If the desired sort is not supported server-side for that endpoint, choose between using another endpoint that supports it or implementing client-side sorting.
- If implementing client-side sorting: map keys, choose fetch size, sort locally, then apply limit.
- Add unit tests with stubs that validate ranking across the full fetched population, not just a single page slice.
- Add CI validation that docstrings claiming third-party behavior are reconciled with the authoritative API reference.
Practical implication for MCP-style tool servers
Agent-visible docstrings are the sole contract for callers. When those strings assert third-party behavior, they must be treated as claims requiring verification. Silence from a dependency (an API that ignores bad values) makes these bugs especially dangerous. Implement missing upstream capabilities in the client where reasonable, and add automated checks so future docstring claims stay honest.
“A docstring that matches the code isn’t the same claim as a docstring that matches reality.”
Keeping docstrings aligned with both code and upstream API contracts prevents quiet failures that are hard to detect in automated checks and in day-to-day use. Use the patterns above to restore correctness while preserving the API callers expect.
