@farming-labs/docs 0.2.75 → 0.2.77

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/{agent-DxTa3PLB.mjs → agent-D4hNGatV.mjs} +2 -2
  2. package/dist/{agent-cAPjC2jb.d.mts → agent-DRKj42ht.d.mts} +18 -2
  3. package/dist/{agent-BHqaZx-X.mjs → agent-SRIJ-oF8.mjs} +26 -6
  4. package/dist/{agent-evals-21LZ_uEl.mjs → agent-evals-DmFoKy_y.mjs} +1 -1
  5. package/dist/{agent-export-3VheegEK.mjs → agent-export-CM-p9_YD.mjs} +7 -7
  6. package/dist/agent-skills-bundle.d.mts +1 -1
  7. package/dist/{agent-skills-server-noFn1l_p.mjs → agent-skills-server-B2EXn2QD.mjs} +1 -1
  8. package/dist/{agent-skills-server-Cix64aGB.d.mts → agent-skills-server-CJ4D0G9P.d.mts} +2 -2
  9. package/dist/agent-skills-vite.d.mts +3 -3
  10. package/dist/agent-skills-vite.mjs +2 -2
  11. package/dist/{agents-ByTdP0Hl.mjs → agents-BeLDNCnE.mjs} +7 -16
  12. package/dist/cli/index.mjs +15 -15
  13. package/dist/client/react.d.mts +1 -1
  14. package/dist/{cloud-ask-ai-Bt0rzFbe.d.mts → cloud-ask-ai-BCgHPxew.d.mts} +1 -1
  15. package/dist/{dev-Cc1cFFj-.mjs → dev-kjaxOOla.mjs} +8 -2
  16. package/dist/docs-cloud-server.d.mts +2 -2
  17. package/dist/{doctor-C34VVF85.mjs → doctor-DhMVKHjp.mjs} +56 -24
  18. package/dist/{golden-evaluations-D_oDJwGv.mjs → golden-evaluations-VzE6juQl.mjs} +7 -3
  19. package/dist/index.d.mts +5 -5
  20. package/dist/index.mjs +6 -6
  21. package/dist/{mcp-Bnqfu8P5.mjs → mcp-CZkQJXtA.mjs} +6 -6
  22. package/dist/mcp.d.mts +26 -5
  23. package/dist/mcp.mjs +366 -61
  24. package/dist/{prompt-references-CfyYOa_W.mjs → prompt-references-B6L3AD98.mjs} +3 -3
  25. package/dist/{retrieval-digest-CFOcSKTo.d.mts → retrieval-digest-rponR4jM.d.mts} +24 -6
  26. package/dist/{review-Dw6oCQX3.mjs → review-nxoV8jIm.mjs} +5 -5
  27. package/dist/{robots-DaYBoy_A.mjs → robots-CgH6Stqn.mjs} +2 -2
  28. package/dist/{robots-KWLujSLI.mjs → robots-CiQT8mX2.mjs} +3 -3
  29. package/dist/{search-DBdmbUkc.mjs → search-BTUIkhnT.mjs} +6 -6
  30. package/dist/{search-BmC8wVr0.mjs → search-Da2AKaFJ.mjs} +624 -122
  31. package/dist/server.d.mts +9 -7
  32. package/dist/server.mjs +7 -7
  33. package/dist/{sitemap-CrzG5Wpf.mjs → sitemap-e0_GeCWJ.mjs} +6 -6
  34. package/dist/{sitemap-server-B9LZQ1FE.mjs → sitemap-server-DHaxyPAd.mjs} +12 -3
  35. package/dist/{standards-discovery-BwL5ffGx.mjs → standards-discovery-BKlEnK_H.mjs} +32 -4
  36. package/dist/{standards-discovery-DBI1PD7m.d.mts → standards-discovery-BbslY4id.d.mts} +34 -7
  37. package/dist/{types-BDRwrExu.d.mts → types-B-5Kzmkn.d.mts} +68 -1
  38. package/package.json +1 -1
package/dist/mcp.mjs CHANGED
@@ -1,15 +1,15 @@
1
1
  import { a as emitDocsAnalyticsEvent, i as emitDocsAgentTraceEvent, n as createDocsAgentTraceContext, r as createDocsAgentTraceId } from "./analytics-Bx44lg6d.mjs";
2
- import { E as DOCS_MARKDOWN_SECTION_INDEX_FORMAT, Kt as normalizeDocsRelated, P as buildDocsMarkdownSectionIndex, an as isDocsMcpResourcePath, cn as resolveDocsMcpProtectedResourceMetadataLocation, jt as resolveDocsAudienceMdxContent, ln as resolveDocsMcpResourceLocation, nn as isDocsMcpOAuthScopeToken, on as normalizeDocsMcpAuthorizationServerUrls, qt as renderDocsRelatedMarkdownLines, rn as isDocsMcpProtectedResourceMetadataPath, sn as normalizeDocsMcpEndpointPath, wt as toDocsMarkdownUrl } from "./agent-BHqaZx-X.mjs";
2
+ import { E as DOCS_MARKDOWN_SECTION_INDEX_FORMAT, Kt as normalizeDocsRelated, P as buildDocsMarkdownSectionIndex, an as isDocsMcpResourcePath, cn as resolveDocsMcpProtectedResourceMetadataLocation, jt as resolveDocsAudienceMdxContent, ln as resolveDocsMcpResourceLocation, nn as isDocsMcpOAuthScopeToken, on as normalizeDocsMcpAuthorizationServerUrls, qt as renderDocsRelatedMarkdownLines, rn as isDocsMcpProtectedResourceMetadataPath, sn as normalizeDocsMcpEndpointPath, wt as toDocsMarkdownUrl } from "./agent-SRIJ-oF8.mjs";
3
3
  import { S as upsertPageAgentContractMarkdown, _ as hasStructuredPageAgentContract, o as findDocsMarkdownSection, s as parseDocsMarkdownSections, v as normalizePageAgentFrontmatter } from "./markdown-sections-7OoA7ylx.mjs";
4
- import "./standards-discovery-BwL5ffGx.mjs";
5
- import { C as agentVersionConstraintsOverlap, E as normalizeAgentScopeValues, I as stripGeneratedAgentProvenance, R as resolvePageSidebarFolderIndexBehavior, S as agentVersionConstraintMatches, T as normalizeAgentLocale, U as emitDocsTelemetryMcpToolEvent, V as emitDocsTelemetryAgentSurfaceEvent, W as emitDocsTelemetryProjectEvent, f as normalizeDocsSearchFilters, k as isDocsRetrievalCanonicalUrl, m as performDocsSearchWithMetadata, p as performDocsSearch, w as normalizeAgentFramework } from "./search-BmC8wVr0.mjs";
4
+ import "./standards-discovery-BKlEnK_H.mjs";
5
+ import { A as DocsPaginationCursorError, B as stripGeneratedAgentProvenance, D as normalizeAgentLocale, E as normalizeAgentFramework, G as emitDocsTelemetryAgentSurfaceEvent, H as resolvePageSidebarFolderIndexBehavior, J as emitDocsTelemetryProjectEvent, N as isDocsRetrievalCanonicalUrl, O as normalizeAgentScopeValues, T as agentVersionConstraintsOverlap, h as performDocsSearchWithMetadata, j as paginateDocsItems, m as performDocsSearch, p as normalizeDocsSearchFilters, q as emitDocsTelemetryMcpToolEvent, w as agentVersionConstraintMatches } from "./search-Da2AKaFJ.mjs";
6
6
  import matter from "gray-matter";
7
7
  import fs from "node:fs";
8
8
  import path from "node:path";
9
9
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
10
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11
11
  import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
12
- import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
12
+ import { ErrorCode, ListPromptsRequestSchema, ListResourceTemplatesRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, McpError, isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
13
13
  import * as z from "zod/v4";
14
14
 
15
15
  //#region src/mcp.ts
@@ -18,6 +18,9 @@ const DEFAULT_MCP_NAME = "@farming-labs/docs";
18
18
  const DEFAULT_MCP_CONTEXT_TOKEN_BUDGET = 4e3;
19
19
  const MIN_MCP_CONTEXT_TOKEN_BUDGET = 256;
20
20
  const MAX_MCP_CONTEXT_TOKEN_BUDGET = 32e3;
21
+ const DOCS_MCP_PROTOCOL_LIST_PAGE_SIZE = 10;
22
+ const DOCS_MCP_TOOL_LIST_PAGE_SIZE = 25;
23
+ const DOCS_MCP_PAGINATION_META_KEY = "dev.farming-labs/pagination";
21
24
  const UTF8_ENCODER = new TextEncoder();
22
25
  const DEFAULT_DOCS_MCP_MAX_BODY_BYTES = 1024 * 1024;
23
26
  const DEFAULT_DOCS_MCP_CORS_MAX_AGE_SECONDS = 600;
@@ -1562,17 +1565,26 @@ const DOCS_CONFIG_SCHEMA_OPTIONS_TEMPLATE = [
1562
1565
  default: false,
1563
1566
  description: "Generated API reference pages from framework route conventions or a hosted OpenAPI document.",
1564
1567
  docs: "/docs/customization/api-reference",
1565
- children: [{
1566
- path: "apiReference.specUrl",
1567
- name: "specUrl",
1568
- type: "string",
1569
- description: "Remote OpenAPI JSON URL when the backend owns the schema."
1570
- }, {
1571
- path: "apiReference.path",
1572
- name: "path",
1573
- type: "string",
1574
- description: "Docs route where the API reference is rendered."
1575
- }]
1568
+ children: [
1569
+ {
1570
+ path: "apiReference.specUrl",
1571
+ name: "specUrl",
1572
+ type: "string",
1573
+ description: "Remote OpenAPI JSON URL when the backend owns the schema."
1574
+ },
1575
+ {
1576
+ path: "apiReference.path",
1577
+ name: "path",
1578
+ type: "string",
1579
+ description: "Docs route where the API reference is rendered."
1580
+ },
1581
+ {
1582
+ path: "apiReference.catalogTargets",
1583
+ name: "catalogTargets",
1584
+ type: "string[]",
1585
+ description: "Product API base URLs that the OpenAPI document describes in the RFC 9727 catalog."
1586
+ }
1587
+ ]
1576
1588
  },
1577
1589
  {
1578
1590
  path: "codeBlocks",
@@ -1704,9 +1716,11 @@ export default defineDocs({
1704
1716
  }
1705
1717
  ]);
1706
1718
  const searchFilterValueSchema = z.union([z.string().trim().min(1).max(128), z.array(z.string().trim().min(1).max(128)).min(1).max(16)]);
1719
+ const paginationCursorInputSchema = z.string().min(1).max(4096).describe("Opaque nextCursor from the preceding response page.").optional();
1707
1720
  const searchDocsInputSchema = z.object({
1708
1721
  query: z.string().trim().min(1),
1709
1722
  limit: z.number().int().min(1).max(25).optional(),
1723
+ cursor: paginationCursorInputSchema,
1710
1724
  locale: z.string().trim().min(1).max(128).optional(),
1711
1725
  audience: z.enum(["human", "agent"]).optional(),
1712
1726
  framework: searchFilterValueSchema.optional(),
@@ -1724,14 +1738,16 @@ const listPageSectionsInputSchema = z.object({
1724
1738
  path: z.string().min(1),
1725
1739
  locale: z.string().trim().min(1).max(128).optional(),
1726
1740
  tokenBudget: z.number().int().min(1).max(1e6).optional(),
1727
- byteBudget: z.number().int().min(1).max(1e6).optional()
1741
+ byteBudget: z.number().int().min(1).max(1e6).optional(),
1742
+ cursor: paginationCursorInputSchema
1728
1743
  });
1729
1744
  const listTasksInputSchema = z.object({
1730
1745
  query: z.string().trim().min(1).optional(),
1731
1746
  framework: z.string().trim().min(1).optional(),
1732
1747
  version: z.string().trim().min(1).optional(),
1733
1748
  package: z.string().trim().min(1).optional(),
1734
- locale: z.string().trim().min(1).max(128).optional()
1749
+ locale: z.string().trim().min(1).max(128).optional(),
1750
+ cursor: paginationCursorInputSchema
1735
1751
  });
1736
1752
  const readTaskInputSchema = readPageInputSchema;
1737
1753
  const pageAgentAppliesToOutputSchema = z.object({
@@ -1775,8 +1791,14 @@ const taskSummaryOutputSchema = z.object({
1775
1791
  outcome: z.string().optional(),
1776
1792
  appliesTo: pageAgentAppliesToOutputSchema.optional()
1777
1793
  });
1794
+ const paginationOutputShape = {
1795
+ resultCount: z.number().int().nonnegative().describe("Items returned in this response page."),
1796
+ total: z.number().int().nonnegative().describe("Items in the complete filtered result set."),
1797
+ hasMore: z.boolean().describe("Whether another response page is available."),
1798
+ nextCursor: z.string().describe("Opaque cursor to pass as cursor on the next request.").optional()
1799
+ };
1778
1800
  const listTasksOutputSchema = z.object({
1779
- resultCount: z.number().int().nonnegative(),
1801
+ ...paginationOutputShape,
1780
1802
  tasks: z.array(taskSummaryOutputSchema)
1781
1803
  });
1782
1804
  const readTaskOutputSchema = z.object({
@@ -1790,10 +1812,14 @@ const readTaskOutputSchema = z.object({
1790
1812
  }),
1791
1813
  contract: pageAgentContractOutputSchema
1792
1814
  });
1793
- const listPagesInputSchema = z.object({ locale: z.string().trim().min(1).max(128).optional() });
1815
+ const listPagesInputSchema = z.object({
1816
+ locale: z.string().trim().min(1).max(128).optional(),
1817
+ cursor: paginationCursorInputSchema
1818
+ });
1794
1819
  const listDocsInputSchema = z.object({
1795
1820
  section: z.string().trim().min(1).optional(),
1796
- locale: z.string().trim().min(1).max(128).optional()
1821
+ locale: z.string().trim().min(1).max(128).optional(),
1822
+ cursor: paginationCursorInputSchema
1797
1823
  });
1798
1824
  const getNavigationInputSchema = z.object({ locale: z.string().trim().min(1).max(128).optional() });
1799
1825
  const getConfigSchemaInputSchema = z.object({
@@ -1846,10 +1872,13 @@ const docsSectionOutputSchema = z.lazy(() => z.object({
1846
1872
  pages: z.array(pageSummaryOutputSchema),
1847
1873
  sections: z.array(docsSectionOutputSchema)
1848
1874
  }));
1849
- const listPagesOutputSchema = z.object({ pages: z.array(pageSummaryOutputSchema) });
1875
+ const listPagesOutputSchema = z.object({
1876
+ ...paginationOutputShape,
1877
+ pages: z.array(pageSummaryOutputSchema)
1878
+ });
1850
1879
  const listDocsOutputSchema = z.object({
1880
+ ...paginationOutputShape,
1851
1881
  section: z.string().optional(),
1852
- resultCount: z.number().int().nonnegative(),
1853
1882
  sectionCount: z.number().int().nonnegative(),
1854
1883
  pages: z.array(pageSummaryOutputSchema),
1855
1884
  rootPages: z.array(pageSummaryOutputSchema),
@@ -1951,7 +1980,7 @@ const searchDocsOutputSchema = z.object({
1951
1980
  audience: z.enum(["human", "agent"]),
1952
1981
  filters: searchFiltersOutputSchema,
1953
1982
  indexGeneration: retrievalSourceDigestOutputSchema,
1954
- resultCount: z.number().int().nonnegative(),
1983
+ ...paginationOutputShape,
1955
1984
  results: z.array(searchResultOutputSchema),
1956
1985
  warnings: z.array(searchWarningOutputSchema)
1957
1986
  });
@@ -2046,6 +2075,7 @@ const pageSectionIndexOutputSchema = z.object({
2046
2075
  sectionIndexUrl: z.string(),
2047
2076
  lineNumbering: z.literal("body"),
2048
2077
  sectionCount: z.number().int().nonnegative(),
2078
+ ...paginationOutputShape,
2049
2079
  estimatedTokens: z.number().int().nonnegative(),
2050
2080
  utf8Bytes: z.number().int().nonnegative(),
2051
2081
  fetchBudget: z.object({
@@ -2263,16 +2293,155 @@ function createStructuredTextResult(structuredContent, text) {
2263
2293
  structuredContent
2264
2294
  };
2265
2295
  }
2296
+ function stableDocsMcpPaginationScope(values) {
2297
+ return JSON.stringify(values);
2298
+ }
2299
+ function docsMcpPaginationSnapshot(values) {
2300
+ return JSON.stringify(values);
2301
+ }
2302
+ function paginateDocsMcpItems(items, options) {
2303
+ return paginateDocsItems(items, {
2304
+ kind: options.kind,
2305
+ scope: options.scope,
2306
+ snapshot: docsMcpPaginationSnapshot(items),
2307
+ cursor: options.cursor,
2308
+ pageSize: options.pageSize ?? DOCS_MCP_TOOL_LIST_PAGE_SIZE
2309
+ });
2310
+ }
2311
+ function paginateDocsMcpProtocolItems(items, options) {
2312
+ try {
2313
+ return paginateDocsMcpItems(items, {
2314
+ ...options,
2315
+ pageSize: DOCS_MCP_PROTOCOL_LIST_PAGE_SIZE
2316
+ });
2317
+ } catch (error) {
2318
+ if (error instanceof DocsPaginationCursorError) throw new McpError(ErrorCode.InvalidParams, error.message);
2319
+ throw error;
2320
+ }
2321
+ }
2322
+ function toDocsMcpProtocolPaginationMeta(result) {
2323
+ return { [DOCS_MCP_PAGINATION_META_KEY]: {
2324
+ hasMore: result.hasMore,
2325
+ total: result.total
2326
+ } };
2327
+ }
2328
+ function paginateDocsMcpSdkListResult(result, field, options) {
2329
+ const page = paginateDocsMcpProtocolItems([...result[field]].sort(options.compare), options);
2330
+ return {
2331
+ ...result,
2332
+ [field]: page.items,
2333
+ nextCursor: page.nextCursor,
2334
+ _meta: {
2335
+ ...result._meta,
2336
+ ...toDocsMcpProtocolPaginationMeta(page)
2337
+ }
2338
+ };
2339
+ }
2340
+ /**
2341
+ * The MCP SDK owns the live registries and descriptor serialization. Intercepting the
2342
+ * handlers it installs lets pagination operate on that live output without copying private
2343
+ * registry state or drifting from SDK behavior when callers register, update, or disable
2344
+ * tools/resources after this factory returns.
2345
+ */
2346
+ function installDocsMcpSdkListPaginationInterceptor(server, scope) {
2347
+ const originalSetRequestHandler = server.server.setRequestHandler;
2348
+ const setRequestHandler = originalSetRequestHandler.bind(server.server);
2349
+ const interceptedSetRequestHandler = ((requestSchema, handler) => {
2350
+ const sdkHandler = handler;
2351
+ if (Object.is(requestSchema, ListResourcesRequestSchema)) {
2352
+ setRequestHandler(ListResourcesRequestSchema, async (request, extra) => {
2353
+ return paginateDocsMcpSdkListResult(await sdkHandler(request, extra), "resources", {
2354
+ kind: "mcp.protocol/resources-list",
2355
+ scope,
2356
+ cursor: request.params?.cursor,
2357
+ compare: (left, right) => {
2358
+ const uriOrder = left.uri.localeCompare(right.uri);
2359
+ return uriOrder !== 0 ? uriOrder : left.name.localeCompare(right.name);
2360
+ }
2361
+ });
2362
+ });
2363
+ return;
2364
+ }
2365
+ if (Object.is(requestSchema, ListResourceTemplatesRequestSchema)) {
2366
+ setRequestHandler(ListResourceTemplatesRequestSchema, async (request, extra) => {
2367
+ return paginateDocsMcpSdkListResult(await sdkHandler(request, extra), "resourceTemplates", {
2368
+ kind: "mcp.protocol/resource-templates-list",
2369
+ scope,
2370
+ cursor: request.params?.cursor,
2371
+ compare: (left, right) => {
2372
+ const uriOrder = left.uriTemplate.localeCompare(right.uriTemplate);
2373
+ return uriOrder !== 0 ? uriOrder : left.name.localeCompare(right.name);
2374
+ }
2375
+ });
2376
+ });
2377
+ return;
2378
+ }
2379
+ if (Object.is(requestSchema, ListToolsRequestSchema)) {
2380
+ setRequestHandler(ListToolsRequestSchema, async (request, extra) => {
2381
+ return paginateDocsMcpSdkListResult(await sdkHandler(request, extra), "tools", {
2382
+ kind: "mcp.protocol/tools-list",
2383
+ scope,
2384
+ cursor: request.params?.cursor,
2385
+ compare: (left, right) => left.name.localeCompare(right.name)
2386
+ });
2387
+ });
2388
+ return;
2389
+ }
2390
+ if (Object.is(requestSchema, ListPromptsRequestSchema)) {
2391
+ setRequestHandler(ListPromptsRequestSchema, async (request, extra) => {
2392
+ return paginateDocsMcpSdkListResult(await sdkHandler(request, extra), "prompts", {
2393
+ kind: "mcp.protocol/prompts-list",
2394
+ scope,
2395
+ cursor: request.params?.cursor,
2396
+ compare: (left, right) => left.name.localeCompare(right.name)
2397
+ });
2398
+ });
2399
+ return;
2400
+ }
2401
+ setRequestHandler(requestSchema, handler);
2402
+ });
2403
+ server.server.setRequestHandler = interceptedSetRequestHandler;
2404
+ return () => {
2405
+ if (server.server.setRequestHandler === interceptedSetRequestHandler) server.server.setRequestHandler = originalSetRequestHandler;
2406
+ };
2407
+ }
2408
+ function installDocsMcpSdkRegistrationPagination(server, scope) {
2409
+ const wrapRegistration = (registration) => new Proxy(registration, { apply(target, thisArg, argArray) {
2410
+ const restore = installDocsMcpSdkListPaginationInterceptor(server, scope);
2411
+ try {
2412
+ return Reflect.apply(target, thisArg, argArray);
2413
+ } finally {
2414
+ restore();
2415
+ }
2416
+ } });
2417
+ server.registerTool = wrapRegistration(server.registerTool);
2418
+ server.tool = wrapRegistration(server.tool);
2419
+ server.registerResource = wrapRegistration(server.registerResource);
2420
+ server.resource = wrapRegistration(server.resource);
2421
+ server.registerPrompt = wrapRegistration(server.registerPrompt);
2422
+ server.prompt = wrapRegistration(server.prompt);
2423
+ }
2266
2424
  async function createDocsMcpServer(options) {
2267
2425
  const resolved = resolveDocsMcpConfig(options.mcp, {
2268
2426
  defaultName: options.defaultName ?? options.source.siteTitle ?? DEFAULT_MCP_NAME,
2269
2427
  defaultVersion: options.defaultVersion
2270
2428
  });
2271
2429
  const toolSearchConfig = resolveMcpToolSearchConfig(options.search, resolved.route);
2430
+ const protocolScope = stableDocsMcpPaginationScope({
2431
+ name: resolved.name,
2432
+ version: resolved.version
2433
+ });
2272
2434
  const server = new McpServer({
2273
2435
  name: resolved.name,
2274
2436
  version: resolved.version
2275
2437
  });
2438
+ installDocsMcpSdkRegistrationPagination(server, protocolScope);
2439
+ const registerResource = (name, uri, metadata, callback) => {
2440
+ return server.registerResource(name, uri, metadata, callback);
2441
+ };
2442
+ const registerTool = (name, config, callback) => {
2443
+ return server.registerTool(name, config, callback);
2444
+ };
2276
2445
  const telemetryConfig = {
2277
2446
  telemetry: options.telemetry,
2278
2447
  mcp: options.mcp,
@@ -2303,7 +2472,7 @@ async function createDocsMcpServer(options) {
2303
2472
  const defaultPages = dedupePages(await getSourcePages());
2304
2473
  const defaultTree = await getSourceNavigation();
2305
2474
  const defaultSkills = await getSourceSkills();
2306
- server.registerResource("docs-navigation", "docs://navigation", {
2475
+ registerResource("docs-navigation", "docs://navigation", {
2307
2476
  title: "Docs Navigation",
2308
2477
  description: "Structured navigation tree for the documentation site.",
2309
2478
  mimeType: "text/plain"
@@ -2314,7 +2483,7 @@ async function createDocsMcpServer(options) {
2314
2483
  }] }));
2315
2484
  for (const page of defaultPages) {
2316
2485
  const resourceUri = toPageResourceUri(page.url);
2317
- server.registerResource(`page-${slugToKey(page.slug)}`, resourceUri, {
2486
+ registerResource(`page-${slugToKey(page.slug)}`, resourceUri, {
2318
2487
  title: page.title,
2319
2488
  description: page.description,
2320
2489
  mimeType: "text/markdown"
@@ -2327,7 +2496,7 @@ async function createDocsMcpServer(options) {
2327
2496
  for (const skill of defaultSkills) for (const file of skill.files) {
2328
2497
  const encodedPath = file.path.split("/").map((segment) => encodeURIComponent(segment)).join("/");
2329
2498
  const resourceUri = `docs://skills/${encodeURIComponent(skill.name)}/${encodedPath}`;
2330
- server.registerResource(`skill-${encodeURIComponent(skill.name)}-${Buffer.from(file.path).toString("base64url")}`, resourceUri, {
2499
+ registerResource(`skill-${encodeURIComponent(skill.name)}-${Buffer.from(file.path).toString("base64url")}`, resourceUri, {
2331
2500
  title: `${skill.name}: ${file.path}`,
2332
2501
  description: `${skill.description} (${file.digest})`,
2333
2502
  mimeType: file.mediaType
@@ -2341,14 +2510,15 @@ async function createDocsMcpServer(options) {
2341
2510
  blob: Buffer.from(file.content).toString("base64")
2342
2511
  }] }));
2343
2512
  }
2344
- if (resolved.tools.listPages) server.registerTool("list_pages", {
2513
+ if (resolved.tools.listPages) registerTool("list_pages", {
2345
2514
  title: "List docs pages",
2346
2515
  description: "List the known documentation pages with titles, slugs, and URLs.",
2347
2516
  inputSchema: listPagesInputSchema,
2348
2517
  outputSchema: listPagesOutputSchema,
2349
2518
  annotations: { readOnlyHint: true }
2350
- }, async ({ locale }) => {
2519
+ }, async ({ locale, cursor }) => {
2351
2520
  const startedAt = nowMs();
2521
+ const resolvedLocale = resolveSourceLocale(locale);
2352
2522
  const trace = createDocsAgentTraceContext("mcp.tool.list_pages");
2353
2523
  const callSpanId = createDocsAgentTraceId("span");
2354
2524
  await emitDocsAgentTraceEvent(options.observability, {
@@ -2360,11 +2530,25 @@ async function createDocsMcpServer(options) {
2360
2530
  startedAt: trace.startedAt,
2361
2531
  status: "started",
2362
2532
  locale,
2363
- inputPreview: { locale },
2533
+ inputPreview: {
2534
+ locale,
2535
+ hasCursor: cursor !== void 0
2536
+ },
2364
2537
  metadata: { tool: "list_pages" }
2365
2538
  });
2366
2539
  try {
2367
- const pages = toPageSummaries(dedupePages(await getSourcePages(locale)));
2540
+ const page = paginateDocsMcpItems(toPageSummaries(dedupePages(await options.source.getPages(resolvedLocale, options.requestContext))).sort(compareDocsMcpPageSummaries), {
2541
+ kind: "mcp.tool/list_pages",
2542
+ scope: stableDocsMcpPaginationScope({ locale: resolvedLocale ?? null }),
2543
+ cursor
2544
+ });
2545
+ const result = {
2546
+ resultCount: page.resultCount,
2547
+ total: page.total,
2548
+ hasMore: page.hasMore,
2549
+ ...page.nextCursor ? { nextCursor: page.nextCursor } : {},
2550
+ pages: page.items
2551
+ };
2368
2552
  const elapsed = durationMs(startedAt);
2369
2553
  await emitDocsAnalyticsEvent(options.analytics, {
2370
2554
  type: "mcp_tool",
@@ -2372,13 +2556,15 @@ async function createDocsMcpServer(options) {
2372
2556
  locale,
2373
2557
  properties: {
2374
2558
  tool: "list_pages",
2375
- resultCount: pages.length,
2559
+ resultCount: page.resultCount,
2560
+ total: page.total,
2561
+ hasMore: page.hasMore,
2376
2562
  durationMs: elapsed
2377
2563
  }
2378
2564
  });
2379
2565
  trackMcpTool("list_pages", {
2380
2566
  locale,
2381
- resultCount: pages.length
2567
+ resultCount: page.resultCount
2382
2568
  });
2383
2569
  await emitDocsAgentTraceEvent(options.observability, {
2384
2570
  type: "tool.result",
@@ -2391,10 +2577,14 @@ async function createDocsMcpServer(options) {
2391
2577
  durationMs: elapsed,
2392
2578
  status: "success",
2393
2579
  locale,
2394
- outputPreview: { resultCount: pages.length },
2580
+ outputPreview: {
2581
+ resultCount: page.resultCount,
2582
+ total: page.total,
2583
+ hasMore: page.hasMore
2584
+ },
2395
2585
  metadata: { tool: "list_pages" }
2396
2586
  });
2397
- return createStructuredTextResult({ pages });
2587
+ return createStructuredTextResult(result);
2398
2588
  } catch (error) {
2399
2589
  const elapsed = durationMs(startedAt);
2400
2590
  await emitDocsAgentTraceEvent(options.observability, {
@@ -2414,14 +2604,15 @@ async function createDocsMcpServer(options) {
2414
2604
  throw error;
2415
2605
  }
2416
2606
  });
2417
- if (resolved.tools.listDocs) server.registerTool("list_docs", {
2607
+ if (resolved.tools.listDocs) registerTool("list_docs", {
2418
2608
  title: "List docs by section",
2419
2609
  description: "List documentation pages grouped by section, optionally narrowed to one section.",
2420
2610
  inputSchema: listDocsInputSchema,
2421
2611
  outputSchema: listDocsOutputSchema,
2422
2612
  annotations: { readOnlyHint: true }
2423
- }, async ({ section, locale }) => {
2613
+ }, async ({ section, locale, cursor }) => {
2424
2614
  const startedAt = nowMs();
2615
+ const resolvedLocale = resolveSourceLocale(locale);
2425
2616
  const trace = createDocsAgentTraceContext("mcp.tool.list_docs");
2426
2617
  const callSpanId = createDocsAgentTraceId("span");
2427
2618
  await emitDocsAgentTraceEvent(options.observability, {
@@ -2435,14 +2626,21 @@ async function createDocsMcpServer(options) {
2435
2626
  locale,
2436
2627
  inputPreview: {
2437
2628
  section,
2438
- locale
2629
+ locale,
2630
+ hasCursor: cursor !== void 0
2439
2631
  },
2440
2632
  metadata: { tool: "list_docs" }
2441
2633
  });
2442
2634
  try {
2443
- const docs = listDocsBySection(dedupePages(await getSourcePages(locale)), {
2635
+ const docs = paginateDocsMcpDocsList(listDocsBySection(dedupePages(await options.source.getPages(resolvedLocale, options.requestContext)), {
2444
2636
  section,
2445
2637
  entry: options.source.entry
2638
+ }), {
2639
+ cursor,
2640
+ scope: stableDocsMcpPaginationScope({
2641
+ locale: resolvedLocale ?? null,
2642
+ section: normalizeDocsListMatchValue(section ?? "")
2643
+ })
2446
2644
  });
2447
2645
  const elapsed = durationMs(startedAt);
2448
2646
  await emitDocsAnalyticsEvent(options.analytics, {
@@ -2453,6 +2651,8 @@ async function createDocsMcpServer(options) {
2453
2651
  tool: "list_docs",
2454
2652
  section,
2455
2653
  resultCount: docs.resultCount,
2654
+ total: docs.total,
2655
+ hasMore: docs.hasMore,
2456
2656
  sectionCount: docs.sectionCount,
2457
2657
  durationMs: elapsed
2458
2658
  }
@@ -2474,6 +2674,8 @@ async function createDocsMcpServer(options) {
2474
2674
  locale,
2475
2675
  outputPreview: {
2476
2676
  resultCount: docs.resultCount,
2677
+ total: docs.total,
2678
+ hasMore: docs.hasMore,
2477
2679
  sectionCount: docs.sectionCount
2478
2680
  },
2479
2681
  metadata: { tool: "list_docs" }
@@ -2498,14 +2700,15 @@ async function createDocsMcpServer(options) {
2498
2700
  throw error;
2499
2701
  }
2500
2702
  });
2501
- if (resolved.tools.listTasks) server.registerTool("list_tasks", {
2703
+ if (resolved.tools.listTasks) registerTool("list_tasks", {
2502
2704
  title: "List documented tasks",
2503
2705
  description: "List pages with actionable agent contracts, optionally filtered by text or applicability.",
2504
2706
  inputSchema: listTasksInputSchema,
2505
2707
  outputSchema: listTasksOutputSchema,
2506
2708
  annotations: { readOnlyHint: true }
2507
- }, async ({ query, framework, version, package: packageName, locale }) => {
2709
+ }, async ({ query, framework, version, package: packageName, locale, cursor }) => {
2508
2710
  const startedAt = nowMs();
2711
+ const resolvedLocale = resolveSourceLocale(locale);
2509
2712
  const trace = createDocsAgentTraceContext("mcp.tool.list_tasks");
2510
2713
  const callSpanId = createDocsAgentTraceId("span");
2511
2714
  await emitDocsAgentTraceEvent(options.observability, {
@@ -2521,20 +2724,34 @@ async function createDocsMcpServer(options) {
2521
2724
  queryLength: query?.length,
2522
2725
  framework,
2523
2726
  version,
2524
- package: packageName
2727
+ package: packageName,
2728
+ hasCursor: cursor !== void 0
2525
2729
  },
2526
2730
  metadata: { tool: "list_tasks" }
2527
2731
  });
2528
2732
  try {
2529
- const tasks = listDocsTasks(dedupePages(await getSourcePages(locale)), {
2733
+ const page = paginateDocsMcpItems(listDocsTasks(dedupePages(await options.source.getPages(resolvedLocale, options.requestContext)), {
2530
2734
  query,
2531
2735
  framework,
2532
2736
  version,
2533
2737
  package: packageName
2738
+ }).sort(compareDocsMcpTaskSummaries), {
2739
+ kind: "mcp.tool/list_tasks",
2740
+ scope: stableDocsMcpPaginationScope({
2741
+ locale: resolvedLocale ?? null,
2742
+ query: query?.trim().toLowerCase() ?? null,
2743
+ framework: framework?.trim().toLowerCase() ?? null,
2744
+ version: version?.trim().toLowerCase() ?? null,
2745
+ package: packageName?.trim().toLowerCase() ?? null
2746
+ }),
2747
+ cursor
2534
2748
  });
2535
2749
  const result = {
2536
- resultCount: tasks.length,
2537
- tasks
2750
+ resultCount: page.resultCount,
2751
+ total: page.total,
2752
+ hasMore: page.hasMore,
2753
+ ...page.nextCursor ? { nextCursor: page.nextCursor } : {},
2754
+ tasks: page.items
2538
2755
  };
2539
2756
  const elapsed = durationMs(startedAt);
2540
2757
  await emitDocsAnalyticsEvent(options.analytics, {
@@ -2547,13 +2764,15 @@ async function createDocsMcpServer(options) {
2547
2764
  framework,
2548
2765
  version,
2549
2766
  package: packageName,
2550
- resultCount: tasks.length,
2767
+ resultCount: page.resultCount,
2768
+ total: page.total,
2769
+ hasMore: page.hasMore,
2551
2770
  durationMs: elapsed
2552
2771
  }
2553
2772
  });
2554
2773
  trackMcpTool("list_tasks", {
2555
2774
  locale,
2556
- resultCount: tasks.length
2775
+ resultCount: page.resultCount
2557
2776
  });
2558
2777
  await emitDocsAgentTraceEvent(options.observability, {
2559
2778
  type: "tool.result",
@@ -2566,7 +2785,11 @@ async function createDocsMcpServer(options) {
2566
2785
  durationMs: elapsed,
2567
2786
  status: "success",
2568
2787
  locale,
2569
- outputPreview: { resultCount: tasks.length },
2788
+ outputPreview: {
2789
+ resultCount: page.resultCount,
2790
+ total: page.total,
2791
+ hasMore: page.hasMore
2792
+ },
2570
2793
  metadata: { tool: "list_tasks" }
2571
2794
  });
2572
2795
  return {
@@ -2595,7 +2818,7 @@ async function createDocsMcpServer(options) {
2595
2818
  throw error;
2596
2819
  }
2597
2820
  });
2598
- if (resolved.tools.readTask) server.registerTool("read_task", {
2821
+ if (resolved.tools.readTask) registerTool("read_task", {
2599
2822
  title: "Read a documented task",
2600
2823
  description: "Read the full structured agent contract for a page by slug or URL path.",
2601
2824
  inputSchema: readTaskInputSchema,
@@ -2744,7 +2967,7 @@ async function createDocsMcpServer(options) {
2744
2967
  throw error;
2745
2968
  }
2746
2969
  });
2747
- if (resolved.tools.getNavigation) server.registerTool("get_navigation", {
2970
+ if (resolved.tools.getNavigation) registerTool("get_navigation", {
2748
2971
  title: "Get docs navigation",
2749
2972
  description: "Return the documentation navigation tree for the current docs site.",
2750
2973
  inputSchema: getNavigationInputSchema,
@@ -2817,7 +3040,7 @@ async function createDocsMcpServer(options) {
2817
3040
  throw error;
2818
3041
  }
2819
3042
  });
2820
- if (resolved.tools.getConfigSchema) server.registerTool("get_config_schema", {
3043
+ if (resolved.tools.getConfigSchema) registerTool("get_config_schema", {
2821
3044
  title: "Get docs config schema",
2822
3045
  description: "Return structured docs.config.ts option metadata, optionally filtered by option path or query.",
2823
3046
  inputSchema: getConfigSchemaInputSchema,
@@ -2892,13 +3115,13 @@ async function createDocsMcpServer(options) {
2892
3115
  throw error;
2893
3116
  }
2894
3117
  });
2895
- if (resolved.tools.searchDocs) server.registerTool("search_docs", {
3118
+ if (resolved.tools.searchDocs) registerTool("search_docs", {
2896
3119
  title: "Search documentation",
2897
3120
  description: "Search the docs by keyword with optional framework, version, package, and tag filters. Returns structured ambiguity and metadata warnings.",
2898
3121
  inputSchema: searchDocsInputSchema,
2899
3122
  outputSchema: searchDocsOutputSchema,
2900
3123
  annotations: { readOnlyHint: true }
2901
- }, async ({ query, limit, locale, audience, framework, version, package: packageName, tags }) => {
3124
+ }, async ({ query, limit, cursor, locale, audience, framework, version, package: packageName, tags }) => {
2902
3125
  const startedAt = nowMs();
2903
3126
  const resolvedLimit = limit ?? 10;
2904
3127
  const resolvedAudience = audience ?? "agent";
@@ -2923,6 +3146,7 @@ async function createDocsMcpServer(options) {
2923
3146
  inputPreview: {
2924
3147
  queryLength: query.length,
2925
3148
  limit: resolvedLimit,
3149
+ hasCursor: cursor !== void 0,
2926
3150
  locale,
2927
3151
  audience: resolvedAudience,
2928
3152
  filterFields: Object.entries(filters).filter(([, value]) => value !== void 0).map(([field]) => field)
@@ -2940,7 +3164,8 @@ async function createDocsMcpServer(options) {
2940
3164
  siteTitle: options.source.siteTitle,
2941
3165
  baseUrl: options.source.baseUrl ?? (options.requestContext?.request ? new URL(options.requestContext.request.url).origin : void 0),
2942
3166
  syncBaseUrl: options.source.baseUrl ?? null,
2943
- limit: resolvedLimit
3167
+ limit: resolvedLimit,
3168
+ cursor
2944
3169
  });
2945
3170
  const elapsed = durationMs(startedAt);
2946
3171
  await emitDocsAnalyticsEvent(options.analytics, {
@@ -2954,6 +3179,8 @@ async function createDocsMcpServer(options) {
2954
3179
  limit: resolvedLimit,
2955
3180
  audience: resolvedAudience,
2956
3181
  resultCount: searchResponse.resultCount,
3182
+ total: searchResponse.total,
3183
+ hasMore: searchResponse.hasMore,
2957
3184
  warningCount: searchResponse.warnings.length,
2958
3185
  durationMs: elapsed
2959
3186
  }
@@ -2975,6 +3202,8 @@ async function createDocsMcpServer(options) {
2975
3202
  locale,
2976
3203
  outputPreview: {
2977
3204
  resultCount: searchResponse.resultCount,
3205
+ total: searchResponse.total,
3206
+ hasMore: searchResponse.hasMore,
2978
3207
  warningCount: searchResponse.warnings.length
2979
3208
  },
2980
3209
  metadata: { tool: "search_docs" }
@@ -2999,7 +3228,7 @@ async function createDocsMcpServer(options) {
2999
3228
  throw error;
3000
3229
  }
3001
3230
  });
3002
- if (resolved.tools.getCodeExamples) server.registerTool("get_code_examples", {
3231
+ if (resolved.tools.getCodeExamples) registerTool("get_code_examples", {
3003
3232
  title: "Get docs code examples",
3004
3233
  description: "Return fenced code examples from the docs with parsed metadata such as title, framework, packageManager, and runnable.",
3005
3234
  inputSchema: getCodeExamplesInputSchema,
@@ -3098,7 +3327,7 @@ async function createDocsMcpServer(options) {
3098
3327
  throw error;
3099
3328
  }
3100
3329
  });
3101
- if (resolved.tools.getContext) server.registerTool("get_context", {
3330
+ if (resolved.tools.getContext) registerTool("get_context", {
3102
3331
  title: "Get budgeted docs context",
3103
3332
  description: "Build deterministic, section-level documentation context for a query within a conservative UTF-8 byte ceiling derived from the requested token budget, with source URLs and accounting metadata.",
3104
3333
  inputSchema: getContextInputSchema,
@@ -3208,13 +3437,13 @@ async function createDocsMcpServer(options) {
3208
3437
  throw error;
3209
3438
  }
3210
3439
  });
3211
- if (resolved.tools.listPageSections !== false) server.registerTool("list_page_sections", {
3440
+ if (resolved.tools.listPageSections !== false) registerTool("list_page_sections", {
3212
3441
  title: "List sections in a docs page",
3213
3442
  description: "Discover a page's canonical headings, anchors, hierarchy, line ranges, size estimates, and budget-aware fetch URLs without returning the page body.",
3214
3443
  inputSchema: listPageSectionsInputSchema,
3215
3444
  outputSchema: pageSectionIndexOutputSchema,
3216
3445
  annotations: { readOnlyHint: true }
3217
- }, async ({ path: requestedPath, locale, tokenBudget, byteBudget }) => {
3446
+ }, async ({ path: requestedPath, locale, tokenBudget, byteBudget, cursor }) => {
3218
3447
  const startedAt = nowMs();
3219
3448
  const resolvedLocale = resolveSourceLocale(locale);
3220
3449
  const trace = createDocsAgentTraceContext("mcp.tool.list_page_sections");
@@ -3233,7 +3462,8 @@ async function createDocsMcpServer(options) {
3233
3462
  locale,
3234
3463
  resolvedLocale,
3235
3464
  tokenBudget,
3236
- byteBudget
3465
+ byteBudget,
3466
+ hasCursor: cursor !== void 0
3237
3467
  },
3238
3468
  metadata: { tool: "list_page_sections" }
3239
3469
  });
@@ -3286,12 +3516,31 @@ async function createDocsMcpServer(options) {
3286
3516
  }
3287
3517
  const document = upsertPageAgentContractMarkdown(getDocsMcpSourceMarkdown(page), page.agent);
3288
3518
  const publicBaseUrl = options.source.baseUrl ?? (options.requestContext?.request ? new URL(options.requestContext.request.url).origin : void 0);
3289
- const sectionIndex = buildDocsMarkdownSectionIndex(document, {
3519
+ const completeSectionIndex = buildDocsMarkdownSectionIndex(document, {
3290
3520
  canonicalUrl: resolveDocsMcpPublicUrl(withDocsMcpUrlLocale(page.url, resolvedLocale), publicBaseUrl),
3291
3521
  markdownUrl: resolveDocsMcpPublicUrl(toDocsMcpPageMarkdownUrl(page.url, resolvedLocale), publicBaseUrl),
3292
3522
  tokenBudget,
3293
3523
  byteBudget
3294
3524
  });
3525
+ const sectionPage = paginateDocsMcpItems(completeSectionIndex.sections, {
3526
+ kind: "mcp.tool/list_page_sections",
3527
+ scope: stableDocsMcpPaginationScope({
3528
+ path: page.url,
3529
+ locale: resolvedLocale ?? null,
3530
+ tokenBudget: tokenBudget ?? null,
3531
+ byteBudget: byteBudget ?? null
3532
+ }),
3533
+ cursor
3534
+ });
3535
+ const sectionIndex = {
3536
+ ...completeSectionIndex,
3537
+ sectionCount: completeSectionIndex.sectionCount,
3538
+ resultCount: sectionPage.resultCount,
3539
+ total: sectionPage.total,
3540
+ hasMore: sectionPage.hasMore,
3541
+ ...sectionPage.nextCursor ? { nextCursor: sectionPage.nextCursor } : {},
3542
+ sections: sectionPage.items
3543
+ };
3295
3544
  const elapsed = durationMs(startedAt);
3296
3545
  await emitDocsAnalyticsEvent(options.analytics, {
3297
3546
  type: "mcp_tool",
@@ -3304,6 +3553,8 @@ async function createDocsMcpServer(options) {
3304
3553
  slug: page.slug,
3305
3554
  found: true,
3306
3555
  sectionCount: sectionIndex.sectionCount,
3556
+ total: sectionIndex.total,
3557
+ hasMore: sectionIndex.hasMore,
3307
3558
  estimatedTokens: sectionIndex.estimatedTokens,
3308
3559
  utf8Bytes: sectionIndex.utf8Bytes,
3309
3560
  tokenBudget,
@@ -3313,7 +3564,7 @@ async function createDocsMcpServer(options) {
3313
3564
  });
3314
3565
  trackMcpTool("list_page_sections", {
3315
3566
  locale: resolvedLocale,
3316
- resultCount: sectionIndex.sectionCount
3567
+ resultCount: sectionIndex.resultCount
3317
3568
  });
3318
3569
  await emitDocsAgentTraceEvent(options.observability, {
3319
3570
  type: "tool.result",
@@ -3331,6 +3582,8 @@ async function createDocsMcpServer(options) {
3331
3582
  found: true,
3332
3583
  slug: page.slug,
3333
3584
  sectionCount: sectionIndex.sectionCount,
3585
+ total: sectionIndex.total,
3586
+ hasMore: sectionIndex.hasMore,
3334
3587
  estimatedTokens: sectionIndex.estimatedTokens,
3335
3588
  utf8Bytes: sectionIndex.utf8Bytes
3336
3589
  },
@@ -3356,7 +3609,7 @@ async function createDocsMcpServer(options) {
3356
3609
  throw error;
3357
3610
  }
3358
3611
  });
3359
- if (resolved.tools.readPage) server.registerTool("read_page", {
3612
+ if (resolved.tools.readPage) registerTool("read_page", {
3360
3613
  title: "Read a docs page",
3361
3614
  description: "Read a documentation page by slug or URL path, optionally selecting one heading and limiting returned characters.",
3362
3615
  inputSchema: readPageInputSchema,
@@ -4371,6 +4624,18 @@ function toDocsListPageSummary(page) {
4371
4624
  lastModified: page.lastModified
4372
4625
  };
4373
4626
  }
4627
+ function compareDocsMcpPageSummaries(left, right) {
4628
+ const urlOrder = left.url.localeCompare(right.url);
4629
+ if (urlOrder !== 0) return urlOrder;
4630
+ const slugOrder = left.slug.localeCompare(right.slug);
4631
+ return slugOrder !== 0 ? slugOrder : left.title.localeCompare(right.title);
4632
+ }
4633
+ function compareDocsMcpTaskSummaries(left, right) {
4634
+ const urlOrder = left.url.localeCompare(right.url);
4635
+ if (urlOrder !== 0) return urlOrder;
4636
+ const slugOrder = left.slug.localeCompare(right.slug);
4637
+ return slugOrder !== 0 ? slugOrder : left.title.localeCompare(right.title);
4638
+ }
4374
4639
  function listDocsTasks(pages, filters) {
4375
4640
  const query = filters.query?.toLowerCase();
4376
4641
  const applicabilityFilters = [
@@ -4412,6 +4677,8 @@ function listDocsBySection(pages, filters) {
4412
4677
  const requestedSection = filters.section?.trim();
4413
4678
  if (!requestedSection) return {
4414
4679
  resultCount: allPages.length,
4680
+ total: allPages.length,
4681
+ hasMore: false,
4415
4682
  sectionCount: countDocsSections(tree.sections),
4416
4683
  pages: allPages,
4417
4684
  rootPages: tree.rootPages,
@@ -4424,6 +4691,8 @@ function listDocsBySection(pages, filters) {
4424
4691
  return {
4425
4692
  section: requestedSection,
4426
4693
  resultCount: matchedPages.length,
4694
+ total: matchedPages.length,
4695
+ hasMore: false,
4427
4696
  sectionCount: countDocsSections(sections),
4428
4697
  pages: matchedPages,
4429
4698
  rootPages: [],
@@ -4434,6 +4703,8 @@ function listDocsBySection(pages, filters) {
4434
4703
  if (page) return {
4435
4704
  section: requestedSection,
4436
4705
  resultCount: 1,
4706
+ total: 1,
4707
+ hasMore: false,
4437
4708
  sectionCount: 0,
4438
4709
  pages: [page],
4439
4710
  rootPages: [page],
@@ -4442,12 +4713,46 @@ function listDocsBySection(pages, filters) {
4442
4713
  return {
4443
4714
  section: requestedSection,
4444
4715
  resultCount: 0,
4716
+ total: 0,
4717
+ hasMore: false,
4445
4718
  sectionCount: 0,
4446
4719
  pages: [],
4447
4720
  rootPages: [],
4448
4721
  sections: []
4449
4722
  };
4450
4723
  }
4724
+ function paginateDocsMcpDocsList(docs, options) {
4725
+ const page = paginateDocsMcpItems([...docs.pages].sort(compareDocsMcpPageSummaries), {
4726
+ kind: "mcp.tool/list_docs",
4727
+ scope: options.scope,
4728
+ cursor: options.cursor
4729
+ });
4730
+ const pageUrls = new Set(page.items.map((item) => item.url));
4731
+ const sections = pruneDocsMcpSections(docs.sections, pageUrls);
4732
+ return {
4733
+ ...docs,
4734
+ resultCount: page.resultCount,
4735
+ total: page.total,
4736
+ hasMore: page.hasMore,
4737
+ ...page.nextCursor ? { nextCursor: page.nextCursor } : {},
4738
+ pages: page.items,
4739
+ rootPages: docs.rootPages.filter((item) => pageUrls.has(item.url)),
4740
+ sections,
4741
+ sectionCount: docs.sectionCount
4742
+ };
4743
+ }
4744
+ function pruneDocsMcpSections(sections, pageUrls) {
4745
+ return sections.flatMap((section) => {
4746
+ const pages = section.pages.filter((page) => pageUrls.has(page.url));
4747
+ const childSections = pruneDocsMcpSections(section.sections, pageUrls);
4748
+ if (pages.length === 0 && childSections.length === 0) return [];
4749
+ return [{
4750
+ ...section,
4751
+ pages,
4752
+ sections: childSections
4753
+ }];
4754
+ });
4755
+ }
4451
4756
  function buildDocsSectionTree(pages) {
4452
4757
  const sectionSlugs = /* @__PURE__ */ new Set();
4453
4758
  for (const page of pages) {