@farming-labs/docs 0.2.78 → 0.2.80

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 (35) hide show
  1. package/dist/{agent-Ba94vwM2.mjs → agent-Cj50nyeV.mjs} +40 -3
  2. package/dist/{agent-CHgWJcK_.d.mts → agent-DGALg9hQ.d.mts} +256 -14
  3. package/dist/{agent-CM-hzhfh.mjs → agent-aWl_O6xn.mjs} +2 -2
  4. package/dist/{agent-evals-C8dkgJMN.mjs → agent-evals-DvQQtN62.mjs} +1 -1
  5. package/dist/{agent-export-DdF-IVfg.mjs → agent-export-BISSb83U.mjs} +5 -5
  6. package/dist/{agent-provenance-D8UBpnMc.mjs → agent-provenance-C_jgqNTW.mjs} +1 -1
  7. package/dist/agent-skills-bundle.d.mts +1 -1
  8. package/dist/{agent-skills-server-zvs0Yknt.d.mts → agent-skills-server-Daq-FOay.d.mts} +2 -2
  9. package/dist/agent-skills-vite.d.mts +3 -3
  10. package/dist/{agents-v0Em2KZo.mjs → agents-DLNUgcgU.mjs} +4 -4
  11. package/dist/cli/index.mjs +14 -14
  12. package/dist/client/react.d.mts +1 -1
  13. package/dist/{cloud-ask-ai-BwsdF7AQ.d.mts → cloud-ask-ai-o41WfOTk.d.mts} +1 -1
  14. package/dist/docs-cloud-server.d.mts +2 -2
  15. package/dist/{doctor-BbrN0rdC.mjs → doctor-BxP-UHaE.mjs} +9 -9
  16. package/dist/{golden-evaluations-Bk9t-HNq.mjs → golden-evaluations-Bk8MBi3Z.mjs} +1 -1
  17. package/dist/index.d.mts +5 -5
  18. package/dist/index.mjs +5 -5
  19. package/dist/{mcp-DT-WNsA-.mjs → mcp-BU63wzaM.mjs} +5 -4
  20. package/dist/mcp.d.mts +18 -6
  21. package/dist/mcp.mjs +292 -56
  22. package/dist/{prompt-references-CoDorsvS.mjs → prompt-references-CMMoF6AY.mjs} +2 -2
  23. package/dist/retrieval-digest-DIbiu0TW.d.mts +190 -0
  24. package/dist/{review-DrCaaT7G.mjs → review-D14mUw5D.mjs} +4 -4
  25. package/dist/{robots-C_v2HK43.mjs → robots-BukukxNm.mjs} +1 -1
  26. package/dist/{robots-izJbM9cv.mjs → robots-DmczzidR.mjs} +2 -2
  27. package/dist/{search-YiZMZDfb.mjs → search-DP96dV3e.mjs} +4 -4
  28. package/dist/server.d.mts +7 -7
  29. package/dist/server.mjs +6 -6
  30. package/dist/{sitemap-NO8UMO6Y.mjs → sitemap-DuosRvmn.mjs} +4 -4
  31. package/dist/{sitemap-server-Dgxs9V6S.mjs → sitemap-server-DzlV1NOB.mjs} +1 -1
  32. package/dist/{standards-discovery-BWyNgZSs.d.mts → standards-discovery-C2cUIQzW.d.mts} +1 -1
  33. package/dist/{types-CpLnwHak.d.mts → types-o-ULHPK6.d.mts} +2 -0
  34. package/package.json +6 -2
  35. package/dist/retrieval-digest-CO3wqeMk.d.mts +0 -413
package/dist/mcp.mjs CHANGED
@@ -1,15 +1,13 @@
1
1
  import { a as emitDocsAnalyticsEvent, i as emitDocsAgentTraceEvent, n as createDocsAgentTraceContext, r as createDocsAgentTraceId } from "./analytics-Bx44lg6d.mjs";
2
- import { $n as resolveDocsMcpResourceLocation, An as normalizeAgentScopeValues, Dn as agentVersionConstraintsOverlap, E as DOCS_MARKDOWN_SECTION_INDEX_FORMAT, En as agentVersionConstraintMatches, Fn as isDocsRetrievalCanonicalUrl, In as normalizeDocsRelated, Kn as isDocsMcpOAuthScopeToken, Ln as renderDocsRelatedMarkdownLines, Mn as DocsPaginationCursorError, Nn as paginateDocsItems, On as normalizeAgentFramework, P as buildDocsMarkdownSectionIndex, Qn as resolveDocsMcpProtectedResourceMetadataLocation, Xn as normalizeDocsMcpAuthorizationServerUrls, Yn as isDocsMcpResourcePath, Zn as normalizeDocsMcpEndpointPath, dn as performDocsSearchWithMetadata, en as buildDocsSearchFacets, kn as normalizeAgentLocale, ln as normalizeDocsSearchFilters, qn as isDocsMcpProtectedResourceMetadataPath, un as performDocsSearch, wn as resolveDocsAudienceMdxContent, wt as toDocsMarkdownUrl } from "./agent-Ba94vwM2.mjs";
2
+ import { $n as normalizeDocsMcpEndpointPath, An as normalizeAgentFramework, E as DOCS_MARKDOWN_SECTION_INDEX_FORMAT, En as resolveDocsAudienceMdxContent, Fn as paginateDocsItems, Jn as isDocsMcpOAuthScopeToken, Jt as resolveDocsContentChangesConfig, Kt as isDocsContentChangeGeneration, Ln as isDocsRetrievalCanonicalUrl, Mn as normalizeAgentScopeValues, On as agentVersionConstraintMatches, P as buildDocsMarkdownSectionIndex, Pn as DocsPaginationCursorError, Qn as normalizeDocsMcpAuthorizationServerUrls, Rn as normalizeDocsRelated, Ut as DocsContentChangesRequestError, Wt as createDocsContentChangeFeed, Yn as isDocsMcpProtectedResourceMetadataPath, Zn as isDocsMcpResourcePath, dn as performDocsSearch, er as resolveDocsMcpProtectedResourceMetadataLocation, fn as performDocsSearchWithMetadata, jn as normalizeAgentLocale, kn as agentVersionConstraintsOverlap, tn as buildDocsSearchFacets, tr as resolveDocsMcpResourceLocation, un as normalizeDocsSearchFilters, wt as toDocsMarkdownUrl, yn as resolveLocalDocsMcpSearchConfig, zn as renderDocsRelatedMarkdownLines } from "./agent-Cj50nyeV.mjs";
3
3
  import { S as upsertPageAgentContractMarkdown, _ as hasStructuredPageAgentContract, o as findDocsMarkdownSection, s as parseDocsMarkdownSections, v as normalizePageAgentFrontmatter } from "./markdown-sections-7OoA7ylx.mjs";
4
4
  import "./standards-discovery-BKlEnK_H.mjs";
5
- import { f as emitDocsTelemetryAgentSurfaceEvent, h as emitDocsTelemetryProjectEvent, l as resolvePageSidebarFolderIndexBehavior, m as emitDocsTelemetryMcpToolEvent, s as stripGeneratedAgentProvenance } from "./agent-provenance-D8UBpnMc.mjs";
5
+ import { f as emitDocsTelemetryAgentSurfaceEvent, h as emitDocsTelemetryProjectEvent, l as resolvePageSidebarFolderIndexBehavior, m as emitDocsTelemetryMcpToolEvent, s as stripGeneratedAgentProvenance } from "./agent-provenance-C_jgqNTW.mjs";
6
6
  import matter from "gray-matter";
7
7
  import fs from "node:fs";
8
8
  import path from "node:path";
9
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11
- import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
12
- import { ErrorCode, ListPromptsRequestSchema, ListResourceTemplatesRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, McpError, isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
9
+ import { serveStdio } from "@modelcontextprotocol/server/stdio";
10
+ import { InMemoryServerEventBus, McpServer, ProtocolError, ProtocolErrorCode, ResourceTemplate, createMcpHandler, isInitializeRequest } from "@modelcontextprotocol/server";
13
11
  import * as z from "zod/v4";
14
12
 
15
13
  //#region src/mcp.ts
@@ -21,6 +19,9 @@ const MAX_MCP_CONTEXT_TOKEN_BUDGET = 32e3;
21
19
  const DOCS_MCP_PROTOCOL_LIST_PAGE_SIZE = 10;
22
20
  const DOCS_MCP_TOOL_LIST_PAGE_SIZE = 25;
23
21
  const DOCS_MCP_PAGINATION_META_KEY = "dev.farming-labs/pagination";
22
+ const DOCS_MCP_CONTENT_CHANGES_CURRENT_URI = "docs://changes/current";
23
+ const DOCS_MCP_CONTENT_CHANGES_URI_TEMPLATE = "docs://changes/{generation}";
24
+ const DEFAULT_DOCS_MCP_CONTENT_CHANGE_POLL_INTERVAL_MS = 3e4;
24
25
  const UTF8_ENCODER = new TextEncoder();
25
26
  const DEFAULT_DOCS_MCP_MAX_BODY_BYTES = 1024 * 1024;
26
27
  const DEFAULT_DOCS_MCP_CORS_MAX_AGE_SECONDS = 600;
@@ -29,6 +30,8 @@ const DEFAULT_DOCS_MCP_CORS_ALLOWED_HEADERS = Object.freeze([
29
30
  "Authorization",
30
31
  "Content-Type",
31
32
  "Last-Event-ID",
33
+ "MCP-Method",
34
+ "MCP-Name",
32
35
  "MCP-Protocol-Version",
33
36
  "MCP-Session-Id"
34
37
  ]);
@@ -1568,6 +1571,13 @@ const DOCS_CONFIG_SCHEMA_OPTIONS_TEMPLATE = [
1568
1571
  default: true,
1569
1572
  description: "Expose the list_search_facets tool for body-free framework, version, package, and tag discovery."
1570
1573
  },
1574
+ {
1575
+ path: "mcp.tools.listContentChanges",
1576
+ name: "listContentChanges",
1577
+ type: "boolean",
1578
+ default: true,
1579
+ description: "Expose list_content_changes polling and docs://changes resources when agent.contentChanges is enabled."
1580
+ },
1571
1581
  {
1572
1582
  path: "mcp.tools.readPage",
1573
1583
  name: "readPage",
@@ -1880,6 +1890,10 @@ const listDocsInputSchema = z.object({
1880
1890
  cursor: paginationCursorInputSchema
1881
1891
  });
1882
1892
  const getNavigationInputSchema = z.object({ locale: z.string().trim().min(1).max(128).optional() });
1893
+ const listContentChangesInputSchema = z.object({
1894
+ since: z.string().refine(isDocsContentChangeGeneration, "Expected a SHA-256 index generation").optional(),
1895
+ locale: z.string().trim().min(1).max(128).optional()
1896
+ });
1883
1897
  const getConfigSchemaInputSchema = z.object({
1884
1898
  option: z.string().trim().min(1).optional(),
1885
1899
  query: z.string().trim().min(1).optional()
@@ -1987,6 +2001,37 @@ const retrievalSourceScopeOutputSchema = z.object({
1987
2001
  ])).max(4).optional()
1988
2002
  });
1989
2003
  const retrievalSourceDigestOutputSchema = z.string().regex(/^sha256:[a-f\d]{64}$/iu);
2004
+ const contentChangeDocumentOutputSchema = z.object({
2005
+ url: z.string(),
2006
+ canonicalUrl: z.string().max(4096).refine(isDocsRetrievalCanonicalUrl, "Expected a safe HTTP(S) URL or root-relative path"),
2007
+ digest: retrievalSourceDigestOutputSchema,
2008
+ lastModified: z.string().optional()
2009
+ });
2010
+ const contentChangesOutputSchema = z.object({
2011
+ format: z.literal("docs-content-changes.v1"),
2012
+ audience: z.enum(["human", "agent"]),
2013
+ locale: z.string().optional(),
2014
+ since: retrievalSourceDigestOutputSchema.nullable(),
2015
+ indexGeneration: retrievalSourceDigestOutputSchema,
2016
+ mode: z.enum([
2017
+ "snapshot",
2018
+ "delta",
2019
+ "reset"
2020
+ ]),
2021
+ resetRequired: z.boolean(),
2022
+ documentCount: z.number().int().nonnegative(),
2023
+ counts: z.object({
2024
+ added: z.number().int().nonnegative(),
2025
+ changed: z.number().int().nonnegative(),
2026
+ deleted: z.number().int().nonnegative()
2027
+ }),
2028
+ added: z.array(contentChangeDocumentOutputSchema),
2029
+ changed: z.array(contentChangeDocumentOutputSchema.extend({
2030
+ previousDigest: retrievalSourceDigestOutputSchema,
2031
+ previousLastModified: z.string().optional()
2032
+ })),
2033
+ deleted: z.array(contentChangeDocumentOutputSchema)
2034
+ });
1990
2035
  const retrievalSourceOutputSchema = z.object({
1991
2036
  canonicalUrl: z.string().max(4096).refine(isDocsRetrievalCanonicalUrl, "Expected a safe HTTP(S) URL or root-relative path"),
1992
2037
  scope: retrievalSourceScopeOutputSchema,
@@ -2228,6 +2273,7 @@ function resolveDocsMcpConfig(mcp, defaults = {}) {
2228
2273
  readTask: true,
2229
2274
  searchDocs: true,
2230
2275
  searchFacets: true,
2276
+ listContentChanges: true,
2231
2277
  getNavigation: true,
2232
2278
  getCodeExamples: true,
2233
2279
  getConfigSchema: true,
@@ -2250,6 +2296,7 @@ function resolveDocsMcpConfig(mcp, defaults = {}) {
2250
2296
  readTask: config.tools?.readTask ?? true,
2251
2297
  searchDocs: config.tools?.searchDocs ?? true,
2252
2298
  searchFacets: config.tools?.searchFacets ?? true,
2299
+ listContentChanges: config.tools?.listContentChanges ?? true,
2253
2300
  getNavigation: config.tools?.getNavigation ?? true,
2254
2301
  getCodeExamples: config.tools?.getCodeExamples ?? true,
2255
2302
  getConfigSchema: config.tools?.getConfigSchema ?? true,
@@ -2368,6 +2415,54 @@ function nowMs() {
2368
2415
  function durationMs(startedAt) {
2369
2416
  return Math.max(0, Date.now() - startedAt);
2370
2417
  }
2418
+ function createDocsMcpContentChangeMonitor(options) {
2419
+ let context;
2420
+ let generation;
2421
+ let timer;
2422
+ let running = false;
2423
+ let closed = false;
2424
+ const schedule = () => {
2425
+ if (closed || timer || running || options.isActive?.() === false) return;
2426
+ timer = setTimeout(async () => {
2427
+ timer = void 0;
2428
+ if (closed || context === void 0 || options.isActive?.() === false) return;
2429
+ running = true;
2430
+ try {
2431
+ const nextGeneration = await options.readGeneration(context);
2432
+ if (generation && nextGeneration !== generation) {
2433
+ generation = nextGeneration;
2434
+ await options.notify();
2435
+ } else generation = nextGeneration;
2436
+ } catch {} finally {
2437
+ running = false;
2438
+ schedule();
2439
+ }
2440
+ }, options.pollIntervalMs);
2441
+ if (options.unrefTimer) timer.unref?.();
2442
+ };
2443
+ return {
2444
+ async start(nextContext) {
2445
+ if (closed) return;
2446
+ context = nextContext;
2447
+ if (timer) {
2448
+ clearTimeout(timer);
2449
+ timer = void 0;
2450
+ }
2451
+ try {
2452
+ generation = await options.readGeneration(nextContext);
2453
+ } catch {
2454
+ generation = void 0;
2455
+ }
2456
+ schedule();
2457
+ },
2458
+ close() {
2459
+ closed = true;
2460
+ context = void 0;
2461
+ if (timer) clearTimeout(timer);
2462
+ timer = void 0;
2463
+ }
2464
+ };
2465
+ }
2371
2466
  function createStructuredTextResult(structuredContent, text) {
2372
2467
  return {
2373
2468
  content: [{
@@ -2399,7 +2494,7 @@ function paginateDocsMcpProtocolItems(items, options) {
2399
2494
  pageSize: DOCS_MCP_PROTOCOL_LIST_PAGE_SIZE
2400
2495
  });
2401
2496
  } catch (error) {
2402
- if (error instanceof DocsPaginationCursorError) throw new McpError(ErrorCode.InvalidParams, error.message);
2497
+ if (error instanceof DocsPaginationCursorError) throw new ProtocolError(ProtocolErrorCode.InvalidParams, error.message);
2403
2498
  throw error;
2404
2499
  }
2405
2500
  }
@@ -2430,10 +2525,10 @@ function paginateDocsMcpSdkListResult(result, field, options) {
2430
2525
  function installDocsMcpSdkListPaginationInterceptor(server, scope) {
2431
2526
  const originalSetRequestHandler = server.server.setRequestHandler;
2432
2527
  const setRequestHandler = originalSetRequestHandler.bind(server.server);
2433
- const interceptedSetRequestHandler = ((requestSchema, handler) => {
2528
+ const interceptedSetRequestHandler = ((method, handler) => {
2434
2529
  const sdkHandler = handler;
2435
- if (Object.is(requestSchema, ListResourcesRequestSchema)) {
2436
- setRequestHandler(ListResourcesRequestSchema, async (request, extra) => {
2530
+ if (method === "resources/list") {
2531
+ setRequestHandler("resources/list", async (request, extra) => {
2437
2532
  return paginateDocsMcpSdkListResult(await sdkHandler(request, extra), "resources", {
2438
2533
  kind: "mcp.protocol/resources-list",
2439
2534
  scope,
@@ -2446,8 +2541,8 @@ function installDocsMcpSdkListPaginationInterceptor(server, scope) {
2446
2541
  });
2447
2542
  return;
2448
2543
  }
2449
- if (Object.is(requestSchema, ListResourceTemplatesRequestSchema)) {
2450
- setRequestHandler(ListResourceTemplatesRequestSchema, async (request, extra) => {
2544
+ if (method === "resources/templates/list") {
2545
+ setRequestHandler("resources/templates/list", async (request, extra) => {
2451
2546
  return paginateDocsMcpSdkListResult(await sdkHandler(request, extra), "resourceTemplates", {
2452
2547
  kind: "mcp.protocol/resource-templates-list",
2453
2548
  scope,
@@ -2460,8 +2555,8 @@ function installDocsMcpSdkListPaginationInterceptor(server, scope) {
2460
2555
  });
2461
2556
  return;
2462
2557
  }
2463
- if (Object.is(requestSchema, ListToolsRequestSchema)) {
2464
- setRequestHandler(ListToolsRequestSchema, async (request, extra) => {
2558
+ if (method === "tools/list") {
2559
+ setRequestHandler("tools/list", async (request, extra) => {
2465
2560
  return paginateDocsMcpSdkListResult(await sdkHandler(request, extra), "tools", {
2466
2561
  kind: "mcp.protocol/tools-list",
2467
2562
  scope,
@@ -2471,8 +2566,8 @@ function installDocsMcpSdkListPaginationInterceptor(server, scope) {
2471
2566
  });
2472
2567
  return;
2473
2568
  }
2474
- if (Object.is(requestSchema, ListPromptsRequestSchema)) {
2475
- setRequestHandler(ListPromptsRequestSchema, async (request, extra) => {
2569
+ if (method === "prompts/list") {
2570
+ setRequestHandler("prompts/list", async (request, extra) => {
2476
2571
  return paginateDocsMcpSdkListResult(await sdkHandler(request, extra), "prompts", {
2477
2572
  kind: "mcp.protocol/prompts-list",
2478
2573
  scope,
@@ -2482,7 +2577,7 @@ function installDocsMcpSdkListPaginationInterceptor(server, scope) {
2482
2577
  });
2483
2578
  return;
2484
2579
  }
2485
- setRequestHandler(requestSchema, handler);
2580
+ setRequestHandler(method, handler);
2486
2581
  });
2487
2582
  server.server.setRequestHandler = interceptedSetRequestHandler;
2488
2583
  return () => {
@@ -2499,27 +2594,26 @@ function installDocsMcpSdkRegistrationPagination(server, scope) {
2499
2594
  }
2500
2595
  } });
2501
2596
  server.registerTool = wrapRegistration(server.registerTool);
2502
- server.tool = wrapRegistration(server.tool);
2503
2597
  server.registerResource = wrapRegistration(server.registerResource);
2504
- server.resource = wrapRegistration(server.resource);
2505
2598
  server.registerPrompt = wrapRegistration(server.registerPrompt);
2506
- server.prompt = wrapRegistration(server.prompt);
2507
2599
  }
2508
2600
  async function createDocsMcpServer(options) {
2509
2601
  const resolved = resolveDocsMcpConfig(options.mcp, {
2510
2602
  defaultName: options.defaultName ?? options.source.siteTitle ?? DEFAULT_MCP_NAME,
2511
2603
  defaultVersion: options.defaultVersion
2512
2604
  });
2513
- const toolSearchConfig = resolveMcpToolSearchConfig(options.search, resolved.route);
2605
+ const toolSearchConfig = resolveLocalDocsMcpSearchConfig(options.search, options.mcp, options.requestContext?.request?.url);
2514
2606
  const protocolScope = stableDocsMcpPaginationScope({
2515
2607
  name: resolved.name,
2516
2608
  version: resolved.version
2517
2609
  });
2610
+ const contentChangesEnabled = resolveDocsContentChangesConfig(options.contentChanges).enabled && resolved.tools.listContentChanges !== false;
2518
2611
  const server = new McpServer({
2519
2612
  name: resolved.name,
2520
2613
  version: resolved.version
2521
2614
  });
2522
2615
  installDocsMcpSdkRegistrationPagination(server, protocolScope);
2616
+ if (contentChangesEnabled) server.server.registerCapabilities({ resources: { subscribe: true } });
2523
2617
  const registerResource = (name, uri, metadata, callback) => {
2524
2618
  return server.registerResource(name, uri, metadata, callback);
2525
2619
  };
@@ -2556,6 +2650,26 @@ async function createDocsMcpServer(options) {
2556
2650
  const defaultPages = dedupePages(await getSourcePages());
2557
2651
  const defaultTree = await getSourceNavigation();
2558
2652
  const defaultSkills = await getSourceSkills();
2653
+ const contentChangeFeed = options.contentChangeFeed ?? createDocsContentChangeFeed(options.contentChanges);
2654
+ async function resolveContentChanges(since, locale) {
2655
+ if (!contentChangesEnabled) throw new ProtocolError(ProtocolErrorCode.MethodNotFound, "Content changes are disabled.");
2656
+ if (since && !isDocsContentChangeGeneration(since)) throw new ProtocolError(ProtocolErrorCode.InvalidParams, "Content-change `since` must be a SHA-256 index generation.");
2657
+ const resolvedLocale = resolveSourceLocale(locale);
2658
+ try {
2659
+ return await contentChangeFeed.resolve({
2660
+ pages: toSearchSourcePages(dedupePages(await options.source.getPages(resolvedLocale, options.requestContext))),
2661
+ search: options.search,
2662
+ audience: "agent",
2663
+ locale: resolvedLocale,
2664
+ baseUrl: options.source.baseUrl ?? (options.requestContext?.request ? new URL(options.requestContext.request.url).origin : void 0),
2665
+ ...since ? { since } : {},
2666
+ ...options.requestContext?.request ? { request: options.requestContext.request.clone() } : {}
2667
+ });
2668
+ } catch (error) {
2669
+ if (error instanceof DocsContentChangesRequestError) throw new ProtocolError(ProtocolErrorCode.InvalidParams, error.message);
2670
+ throw error;
2671
+ }
2672
+ }
2559
2673
  registerResource("docs-navigation", "docs://navigation", {
2560
2674
  title: "Docs Navigation",
2561
2675
  description: "Structured navigation tree for the documentation site.",
@@ -2565,6 +2679,65 @@ async function createDocsMcpServer(options) {
2565
2679
  mimeType: "text/plain",
2566
2680
  text: renderNavigationTree(defaultTree)
2567
2681
  }] }));
2682
+ if (contentChangesEnabled) {
2683
+ const readChanges = async (generation) => {
2684
+ const result = await resolveContentChanges(generation && generation !== "current" ? generation : void 0);
2685
+ return { contents: [{
2686
+ uri: generation && generation !== "current" ? `docs://changes/${generation}` : DOCS_MCP_CONTENT_CHANGES_CURRENT_URI,
2687
+ mimeType: "application/json",
2688
+ text: JSON.stringify(result, null, 2)
2689
+ }] };
2690
+ };
2691
+ registerResource("docs-content-changes-current", DOCS_MCP_CONTENT_CHANGES_CURRENT_URI, {
2692
+ title: "Current docs content changes",
2693
+ description: "Stable subscription target for body-free documentation change notifications.",
2694
+ mimeType: "application/json"
2695
+ }, async () => readChanges());
2696
+ server.registerResource("docs-content-changes-by-generation", new ResourceTemplate(DOCS_MCP_CONTENT_CHANGES_URI_TEMPLATE, { list: async () => {
2697
+ return { resources: [{
2698
+ uri: `docs://changes/${(await resolveContentChanges()).indexGeneration}`,
2699
+ name: "Docs changes from current generation",
2700
+ title: "Docs changes from current generation",
2701
+ description: "Read this URI later to retrieve changes since the listed generation.",
2702
+ mimeType: "application/json"
2703
+ }] };
2704
+ } }), {
2705
+ title: "Docs content changes by generation",
2706
+ description: "Body-free documentation changes from a SHA-256 index generation to the current generation.",
2707
+ mimeType: "application/json"
2708
+ }, async (_uri, variables) => {
2709
+ const generation = variables.generation;
2710
+ if (typeof generation !== "string" || !isDocsContentChangeGeneration(generation)) throw new ProtocolError(ProtocolErrorCode.InvalidParams, "Change resource generation must be a SHA-256 index generation.");
2711
+ return readChanges(generation);
2712
+ });
2713
+ registerTool("list_content_changes", {
2714
+ title: "List docs content changes",
2715
+ description: "Synchronize body-free document metadata. Save indexGeneration and pass it as since on the next poll. A reset response means the named snapshot is no longer available.",
2716
+ inputSchema: listContentChangesInputSchema,
2717
+ outputSchema: contentChangesOutputSchema,
2718
+ annotations: { readOnlyHint: true }
2719
+ }, async ({ since, locale }) => {
2720
+ const startedAt = nowMs();
2721
+ const result = await resolveContentChanges(since, locale);
2722
+ trackMcpTool("list_content_changes", {
2723
+ locale,
2724
+ resultCount: result.counts.added + result.counts.changed + result.counts.deleted
2725
+ });
2726
+ await emitDocsAnalyticsEvent(options.analytics, {
2727
+ type: "mcp_tool",
2728
+ source: "mcp",
2729
+ locale,
2730
+ properties: {
2731
+ tool: "list_content_changes",
2732
+ mode: result.mode,
2733
+ resetRequired: result.resetRequired,
2734
+ resultCount: result.counts.added + result.counts.changed + result.counts.deleted,
2735
+ durationMs: durationMs(startedAt)
2736
+ }
2737
+ });
2738
+ return createStructuredTextResult(result);
2739
+ });
2740
+ }
2568
2741
  for (const page of defaultPages) {
2569
2742
  const resourceUri = toPageResourceUri(page.url);
2570
2743
  registerResource(`page-${slugToKey(page.slug)}`, resourceUri, {
@@ -4051,20 +4224,48 @@ function createDocsMcpHttpHandler(options) {
4051
4224
  data: { reason: "mcp_disabled" }
4052
4225
  }),
4053
4226
  DELETE: async () => createJsonErrorResponse(404, disabledMessage),
4054
- OPTIONS: async () => createJsonErrorResponse(404, disabledMessage)
4227
+ OPTIONS: async () => createJsonErrorResponse(404, disabledMessage),
4228
+ close: async () => {}
4055
4229
  };
4056
- async function createStatelessTransport(requestContext) {
4057
- const server = await createDocsMcpServer({
4230
+ const contentChangesEnabled = resolveDocsContentChangesConfig(options.contentChanges).enabled && resolved.tools.listContentChanges !== false;
4231
+ const contentChangeFeed = options.contentChangeFeed ?? createDocsContentChangeFeed(options.contentChanges);
4232
+ const eventBus = new InMemoryServerEventBus();
4233
+ const mcpHandler = createMcpHandler(async (context) => {
4234
+ const internalAuth = context.authInfo;
4235
+ return createDocsMcpServer({
4058
4236
  ...options,
4059
- requestContext
4237
+ contentChangeFeed,
4238
+ requestContext: {
4239
+ transport: "http",
4240
+ ...context.requestInfo ? { request: context.requestInfo.clone() } : {},
4241
+ ...internalAuth?.principal ? { auth: internalAuth.principal } : {}
4242
+ }
4060
4243
  });
4061
- const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
4062
- await server.connect(transport);
4063
- return {
4064
- server,
4065
- transport
4066
- };
4244
+ }, {
4245
+ legacy: "stateless",
4246
+ bus: eventBus
4247
+ });
4248
+ const configuredPollInterval = options.contentChangePollIntervalMs;
4249
+ const contentChangePollIntervalMs = typeof configuredPollInterval === "number" && Number.isFinite(configuredPollInterval) && configuredPollInterval >= 10 ? Math.floor(configuredPollInterval) : DEFAULT_DOCS_MCP_CONTENT_CHANGE_POLL_INTERVAL_MS;
4250
+ async function readMonitoredGeneration(context) {
4251
+ const locale = options.source.resolveLocale?.(void 0, context);
4252
+ const pages = dedupePages(await options.source.getPages(locale, context));
4253
+ return (await contentChangeFeed.resolve({
4254
+ pages: toSearchSourcePages(pages),
4255
+ search: options.search,
4256
+ audience: "agent",
4257
+ locale,
4258
+ baseUrl: options.source.baseUrl ?? (context.request ? new URL(context.request.url).origin : void 0),
4259
+ ...context.request ? { request: context.request.clone() } : {}
4260
+ })).indexGeneration;
4067
4261
  }
4262
+ const contentChangeMonitor = createDocsMcpContentChangeMonitor({
4263
+ pollIntervalMs: contentChangePollIntervalMs,
4264
+ readGeneration: readMonitoredGeneration,
4265
+ notify: () => mcpHandler.notify.resourceUpdated(DOCS_MCP_CONTENT_CHANGES_CURRENT_URI),
4266
+ isActive: () => eventBus.listenerCount > 0,
4267
+ unrefTimer: true
4268
+ });
4068
4269
  async function handle(request) {
4069
4270
  const originalUrl = new URL(request.url);
4070
4271
  const method = request.method.toUpperCase();
@@ -4147,26 +4348,77 @@ function createDocsMcpHttpHandler(options) {
4147
4348
  code: -32700,
4148
4349
  message: "Parse error: Invalid JSON"
4149
4350
  }));
4150
- return withCors(await (await createStatelessTransport({
4351
+ const requestContext = {
4151
4352
  transport: "http",
4152
4353
  request: request.clone(),
4153
4354
  auth
4154
- })).transport.handleRequest(request, parsedBody === void 0 ? void 0 : { parsedBody }));
4355
+ };
4356
+ const isContentChangeSubscription = contentChangesEnabled && method === "POST" && typeof parsedBody === "object" && parsedBody !== null && !Array.isArray(parsedBody) && parsedBody.method === "subscriptions/listen";
4357
+ const authInfo = auth ? {
4358
+ token: "",
4359
+ clientId: auth.id,
4360
+ scopes: auth.scopes ?? [],
4361
+ principal: auth
4362
+ } : void 0;
4363
+ const response = await mcpHandler.fetch(request, {
4364
+ ...parsedBody === void 0 ? {} : { parsedBody },
4365
+ ...authInfo ? { authInfo } : {}
4366
+ });
4367
+ if (isContentChangeSubscription) await contentChangeMonitor.start(requestContext);
4368
+ return withCors(response);
4155
4369
  }
4156
4370
  return {
4157
4371
  GET: async ({ request }) => handle(request),
4158
4372
  POST: async ({ request }) => handle(request),
4159
4373
  DELETE: async ({ request }) => handle(request),
4160
- OPTIONS: async ({ request }) => handle(request)
4374
+ OPTIONS: async ({ request }) => handle(request),
4375
+ close: async () => {
4376
+ contentChangeMonitor.close();
4377
+ await mcpHandler.close();
4378
+ }
4161
4379
  };
4162
4380
  }
4163
4381
  async function runDocsMcpStdio(options) {
4164
- const server = await createDocsMcpServer({
4165
- ...options,
4166
- requestContext: { transport: "stdio" }
4382
+ const resolved = resolveDocsMcpConfig(options.mcp, {
4383
+ defaultName: options.defaultName ?? options.source.siteTitle ?? DEFAULT_MCP_NAME,
4384
+ defaultVersion: options.defaultVersion
4385
+ });
4386
+ const contentChangesEnabled = resolveDocsContentChangesConfig(options.contentChanges).enabled && resolved.tools.listContentChanges !== false;
4387
+ const contentChangeFeed = options.contentChangeFeed ?? createDocsContentChangeFeed(options.contentChanges);
4388
+ const configuredPollInterval = options.contentChangePollIntervalMs;
4389
+ const pollIntervalMs = typeof configuredPollInterval === "number" && Number.isFinite(configuredPollInterval) && configuredPollInterval >= 10 ? Math.floor(configuredPollInterval) : DEFAULT_DOCS_MCP_CONTENT_CHANGE_POLL_INTERVAL_MS;
4390
+ serveStdio(async () => {
4391
+ const requestContext = { transport: "stdio" };
4392
+ const server = await createDocsMcpServer({
4393
+ ...options,
4394
+ contentChangeFeed,
4395
+ requestContext
4396
+ });
4397
+ if (!contentChangesEnabled) return server;
4398
+ const readGeneration = async () => {
4399
+ const locale = options.source.resolveLocale?.(void 0, requestContext);
4400
+ const pages = dedupePages(await options.source.getPages(locale, requestContext));
4401
+ return (await contentChangeFeed.resolve({
4402
+ pages: toSearchSourcePages(pages),
4403
+ search: options.search,
4404
+ audience: "agent",
4405
+ locale,
4406
+ baseUrl: options.source.baseUrl
4407
+ })).indexGeneration;
4408
+ };
4409
+ const monitor = createDocsMcpContentChangeMonitor({
4410
+ pollIntervalMs,
4411
+ readGeneration: () => readGeneration(),
4412
+ notify: () => server.server.sendResourceUpdated({ uri: DOCS_MCP_CONTENT_CHANGES_CURRENT_URI })
4413
+ });
4414
+ await monitor.start(requestContext);
4415
+ const closeServer = server.close.bind(server);
4416
+ server.close = async () => {
4417
+ monitor.close();
4418
+ await closeServer();
4419
+ };
4420
+ return server;
4167
4421
  });
4168
- const transport = new StdioServerTransport();
4169
- await server.connect(transport);
4170
4422
  }
4171
4423
  async function isDocsMcpOriginAllowed(request, allowedOrigins) {
4172
4424
  const origin = request.headers.get("origin");
@@ -4766,22 +5018,6 @@ function countConfigSchemaOptions(options) {
4766
5018
  function normalizeConfigSchemaToken(value) {
4767
5019
  return value.trim().toLowerCase().replace(/^docs\.config\.?/, "").replace(/[`'"]/g, "").replace(/[_\-\s]+/g, "");
4768
5020
  }
4769
- function isSelfMcpSearchEndpoint(search, route) {
4770
- if (!search || search === true || typeof search !== "object" || search.provider !== "mcp") return false;
4771
- const endpoint = search.endpoint.trim();
4772
- if (!endpoint.startsWith("/")) return false;
4773
- return normalizeDocsMcpRoute(endpoint) === normalizeDocsMcpRoute(route);
4774
- }
4775
- function resolveMcpToolSearchConfig(search, route) {
4776
- if (!isSelfMcpSearchEndpoint(search, route)) return search;
4777
- const config = search;
4778
- return {
4779
- provider: "simple",
4780
- enabled: config.enabled,
4781
- maxResults: config.maxResults,
4782
- chunking: config.chunking
4783
- };
4784
- }
4785
5021
  function toAgentContractSummary(value) {
4786
5022
  const agent = normalizePageAgentFrontmatter(value);
4787
5023
  const hasContract = hasStructuredPageAgentContract(agent);
@@ -5607,4 +5843,4 @@ function toPageResourceUri(url) {
5607
5843
  }
5608
5844
 
5609
5845
  //#endregion
5610
- export { DEFAULT_DOCS_MCP_CORS_ALLOWED_HEADERS, DEFAULT_DOCS_MCP_CORS_EXPOSED_HEADERS, DEFAULT_DOCS_MCP_CORS_MAX_AGE_SECONDS, DEFAULT_DOCS_MCP_MAX_BODY_BYTES, DOCS_CONFIG_SCHEMA_OPTIONS, buildDocsMcpContext, createDocsMcpHttpHandler, createDocsMcpServer, createFilesystemDocsMcpSource, getDocsConfigSchema, normalizeDocsMcpRoute, resolveDocsMcpConfig, runDocsMcpStdio };
5846
+ export { DEFAULT_DOCS_MCP_CONTENT_CHANGE_POLL_INTERVAL_MS, DEFAULT_DOCS_MCP_CORS_ALLOWED_HEADERS, DEFAULT_DOCS_MCP_CORS_EXPOSED_HEADERS, DEFAULT_DOCS_MCP_CORS_MAX_AGE_SECONDS, DEFAULT_DOCS_MCP_MAX_BODY_BYTES, DOCS_CONFIG_SCHEMA_OPTIONS, DOCS_MCP_CONTENT_CHANGES_CURRENT_URI, DOCS_MCP_CONTENT_CHANGES_URI_TEMPLATE, buildDocsMcpContext, createDocsMcpHttpHandler, createDocsMcpServer, createFilesystemDocsMcpSource, getDocsConfigSchema, normalizeDocsMcpRoute, resolveDocsMcpConfig, runDocsMcpStdio };
@@ -1,7 +1,7 @@
1
- import { At as DEFAULT_SITEMAP_XML_ROUTE, Ot as DEFAULT_SITEMAP_MD_ROUTE, Vn as DEFAULT_MCP_WELL_KNOWN_ROUTE, c as DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE, f as DEFAULT_DOCS_CONFIG_ROUTE, h as DEFAULT_LLMS_FULL_TXT_ROUTE, i as DEFAULT_AGENT_FEEDBACK_ROUTE, m as DEFAULT_DOCS_DIAGNOSTICS_ROUTE, t as DEFAULT_AGENTS_MD_ROUTE, v as DEFAULT_LLMS_TXT_ROUTE, x as DEFAULT_SKILL_MD_ROUTE } from "./agent-Ba94vwM2.mjs";
1
+ import { At as DEFAULT_SITEMAP_XML_ROUTE, Ot as DEFAULT_SITEMAP_MD_ROUTE, Un as DEFAULT_MCP_WELL_KNOWN_ROUTE, c as DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE, f as DEFAULT_DOCS_CONFIG_ROUTE, h as DEFAULT_LLMS_FULL_TXT_ROUTE, i as DEFAULT_AGENT_FEEDBACK_ROUTE, m as DEFAULT_DOCS_DIAGNOSTICS_ROUTE, t as DEFAULT_AGENTS_MD_ROUTE, v as DEFAULT_LLMS_TXT_ROUTE, x as DEFAULT_SKILL_MD_ROUTE } from "./agent-Cj50nyeV.mjs";
2
2
  import { isDocsAgentSkillName, validateDocsAgentSkillFrontmatter } from "./agent-skills-spec.mjs";
3
3
  import { C as DOCS_AGENT_MANIFEST_SCHEMA_MEDIA_TYPE, S as DOCS_AGENT_MANIFEST_FORMAT, T as DOCS_AGENT_MANIFEST_VERSION, f as DEFAULT_AGENT_SKILLS_ROUTE_PREFIX, n as API_CATALOG_MEDIA_TYPE, r as API_CATALOG_PROFILE_URI, t as AGENT_SKILLS_DISCOVERY_SCHEMA_URI, u as DEFAULT_AGENT_SKILLS_INDEX_ROUTE, v as DEFAULT_API_CATALOG_ROUTE, w as DOCS_AGENT_MANIFEST_SCHEMA_URI } from "./standards-discovery-BKlEnK_H.mjs";
4
- import { n as DEFAULT_ROBOTS_TXT_ROUTE } from "./robots-C_v2HK43.mjs";
4
+ import { n as DEFAULT_ROBOTS_TXT_ROUTE } from "./robots-BukukxNm.mjs";
5
5
  import matter from "gray-matter";
6
6
 
7
7
  //#region src/define-docs.ts