@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.
- package/dist/{agent-DxTa3PLB.mjs → agent-D4hNGatV.mjs} +2 -2
- package/dist/{agent-cAPjC2jb.d.mts → agent-DRKj42ht.d.mts} +18 -2
- package/dist/{agent-BHqaZx-X.mjs → agent-SRIJ-oF8.mjs} +26 -6
- package/dist/{agent-evals-21LZ_uEl.mjs → agent-evals-DmFoKy_y.mjs} +1 -1
- package/dist/{agent-export-3VheegEK.mjs → agent-export-CM-p9_YD.mjs} +7 -7
- package/dist/agent-skills-bundle.d.mts +1 -1
- package/dist/{agent-skills-server-noFn1l_p.mjs → agent-skills-server-B2EXn2QD.mjs} +1 -1
- package/dist/{agent-skills-server-Cix64aGB.d.mts → agent-skills-server-CJ4D0G9P.d.mts} +2 -2
- package/dist/agent-skills-vite.d.mts +3 -3
- package/dist/agent-skills-vite.mjs +2 -2
- package/dist/{agents-ByTdP0Hl.mjs → agents-BeLDNCnE.mjs} +7 -16
- package/dist/cli/index.mjs +15 -15
- package/dist/client/react.d.mts +1 -1
- package/dist/{cloud-ask-ai-Bt0rzFbe.d.mts → cloud-ask-ai-BCgHPxew.d.mts} +1 -1
- package/dist/{dev-Cc1cFFj-.mjs → dev-kjaxOOla.mjs} +8 -2
- package/dist/docs-cloud-server.d.mts +2 -2
- package/dist/{doctor-C34VVF85.mjs → doctor-DhMVKHjp.mjs} +56 -24
- package/dist/{golden-evaluations-D_oDJwGv.mjs → golden-evaluations-VzE6juQl.mjs} +7 -3
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +6 -6
- package/dist/{mcp-Bnqfu8P5.mjs → mcp-CZkQJXtA.mjs} +6 -6
- package/dist/mcp.d.mts +26 -5
- package/dist/mcp.mjs +366 -61
- package/dist/{prompt-references-CfyYOa_W.mjs → prompt-references-B6L3AD98.mjs} +3 -3
- package/dist/{retrieval-digest-CFOcSKTo.d.mts → retrieval-digest-rponR4jM.d.mts} +24 -6
- package/dist/{review-Dw6oCQX3.mjs → review-nxoV8jIm.mjs} +5 -5
- package/dist/{robots-DaYBoy_A.mjs → robots-CgH6Stqn.mjs} +2 -2
- package/dist/{robots-KWLujSLI.mjs → robots-CiQT8mX2.mjs} +3 -3
- package/dist/{search-DBdmbUkc.mjs → search-BTUIkhnT.mjs} +6 -6
- package/dist/{search-BmC8wVr0.mjs → search-Da2AKaFJ.mjs} +624 -122
- package/dist/server.d.mts +9 -7
- package/dist/server.mjs +7 -7
- package/dist/{sitemap-CrzG5Wpf.mjs → sitemap-e0_GeCWJ.mjs} +6 -6
- package/dist/{sitemap-server-B9LZQ1FE.mjs → sitemap-server-DHaxyPAd.mjs} +12 -3
- package/dist/{standards-discovery-BwL5ffGx.mjs → standards-discovery-BKlEnK_H.mjs} +32 -4
- package/dist/{standards-discovery-DBI1PD7m.d.mts → standards-discovery-BbslY4id.d.mts} +34 -7
- package/dist/{types-BDRwrExu.d.mts → types-B-5Kzmkn.d.mts} +68 -1
- 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-
|
|
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-
|
|
5
|
-
import {
|
|
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
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
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
|
-
|
|
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({
|
|
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({
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
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: {
|
|
2533
|
+
inputPreview: {
|
|
2534
|
+
locale,
|
|
2535
|
+
hasCursor: cursor !== void 0
|
|
2536
|
+
},
|
|
2364
2537
|
metadata: { tool: "list_pages" }
|
|
2365
2538
|
});
|
|
2366
2539
|
try {
|
|
2367
|
-
const
|
|
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:
|
|
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:
|
|
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: {
|
|
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(
|
|
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)
|
|
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
|
|
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)
|
|
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
|
|
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:
|
|
2537
|
-
|
|
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:
|
|
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:
|
|
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: {
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
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
|
|
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.
|
|
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)
|
|
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) {
|