@farming-labs/docs 0.2.102 → 0.2.104

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 (58) hide show
  1. package/dist/{agent-lWTq6pgr.d.mts → agent-C9IdC__R.d.mts} +9 -3
  2. package/dist/{agent-BLHMoVId.mjs → agent-NerpHcQB.mjs} +3 -3
  3. package/dist/{agent-CFqECAG0.mjs → agent-PSrgiJD8.mjs} +165 -8
  4. package/dist/{agent-evals-GpcaCbRb.mjs → agent-evals-U9d_db2u.mjs} +2 -2
  5. package/dist/{agent-export-oiWxB5aX.mjs → agent-export-DlBUKkTt.mjs} +20 -9
  6. package/dist/agent-feedback-loop-CGrc-gDW.mjs +196 -0
  7. package/dist/agent-skills-bundle.d.mts +1 -1
  8. package/dist/{agent-skills-server-CICMh0zv.d.mts → agent-skills-server-CIoL58X7.d.mts} +2 -2
  9. package/dist/agent-skills-vite.d.mts +3 -3
  10. package/dist/{agents-CtqIUW5I.mjs → agents-D3SLnQet.mjs} +8 -7
  11. package/dist/{analytics-C75BnTEW.d.mts → analytics-B-jz7re8.d.mts} +1 -1
  12. package/dist/authoring-BsWXMH43.mjs +41 -0
  13. package/dist/authoring-mcp-BCM5CJbJ.mjs +380 -0
  14. package/dist/browser.d.mts +3 -3
  15. package/dist/cli/index.mjs +67 -35
  16. package/dist/client/react.d.mts +1 -1
  17. package/dist/{cloud-FNfcbfli.mjs → cloud-BLxt_HYO.mjs} +3 -3
  18. package/dist/{cloud-ask-ai-BmbZVAT5.d.mts → cloud-ask-ai-19G2HJ62.d.mts} +1 -1
  19. package/dist/{code-blocks-Bel6vDqI.mjs → code-blocks-c3nIrWOM.mjs} +1 -1
  20. package/dist/{codeblocks-jbWuvtJQ.mjs → codeblocks-qHdKGVaE.mjs} +2 -2
  21. package/dist/{dev-BvbYmTLz.mjs → dev-B0iHJjTz.mjs} +3 -3
  22. package/dist/docs-cloud-server.d.mts +2 -2
  23. package/dist/docs-cloud-server.mjs +1 -1
  24. package/dist/{doctor-Bmm0uY98.mjs → doctor-x3k6aE2I.mjs} +16 -12
  25. package/dist/{downgrade-Dqm0rJMy.mjs → downgrade-Dd3vvVdD.mjs} +2 -2
  26. package/dist/feedback-DdWc33oO.mjs +72 -0
  27. package/dist/{golden-evaluations-K45fcWGW.mjs → golden-evaluations-7k7Akp3R.mjs} +2 -2
  28. package/dist/index.d.mts +70 -8
  29. package/dist/index.mjs +5 -5
  30. package/dist/{init-jDQSo-Or.mjs → init-DKukQdOQ.mjs} +3 -3
  31. package/dist/{mcp-CwgKlbIz.mjs → mcp-B6ZZdp6F.mjs} +7 -6
  32. package/dist/mcp.d.mts +18 -5
  33. package/dist/mcp.mjs +457 -38
  34. package/dist/{content-change-hydration-BsbBXAUi.mjs → openapi-mcp-B7u7Y0gB.mjs} +116 -2
  35. package/dist/{package-version--oQiHwZj.mjs → package-version-P4TT_9IO.mjs} +1 -1
  36. package/dist/{prompt-references-DW5vG5zs.mjs → prompt-references-Ca2Rcrpw.mjs} +2 -2
  37. package/dist/{retrieval-digest-DYEsIeCE.d.mts → retrieval-digest-DT9Ktc4C.d.mts} +2 -2
  38. package/dist/{review-C_5wB0rR.mjs → review-DgaNTtP0.mjs} +8 -8
  39. package/dist/{robots-NQesPX5L.mjs → robots-DNod0XKB.mjs} +1 -1
  40. package/dist/{robots-BQ42Y84a.mjs → robots-msD1xoH5.mjs} +4 -4
  41. package/dist/{search-B5etKU-v.mjs → search-CNzq9S5S.mjs} +7 -6
  42. package/dist/server.d.mts +92 -7
  43. package/dist/server.mjs +9 -7
  44. package/dist/{sidebar-CwZlgyAz.d.mts → sidebar-DmXRpAAK.d.mts} +1 -1
  45. package/dist/{sitemap-DjhxvQP1.mjs → sitemap-CHvips7o.mjs} +8 -7
  46. package/dist/{sitemap-server-B0SiREN0.mjs → sitemap-server-C2TauhKa.mjs} +11 -1
  47. package/dist/{skills-BFang8vF.mjs → skills-aaE5L1go.mjs} +8 -7
  48. package/dist/{standards-discovery-D1Ys4J3h.d.mts → standards-discovery-Cqq8QmlH.d.mts} +1 -1
  49. package/dist/{types-DLLefoU5.d.mts → types-BthCiAD9.d.mts} +114 -1
  50. package/dist/{upgrade-kj2crJSk.mjs → upgrade-B0OA_7jE.mjs} +2 -2
  51. package/package.json +1 -1
  52. /package/dist/{agent-skills-progressive-disclosure-Da9LKEKv.mjs → agent-skills-progressive-disclosure-CfIsq4Ew.mjs} +0 -0
  53. /package/dist/{audience-exposure-Cw3SA4WE.d.mts → audience-exposure-BW13cI2O.d.mts} +0 -0
  54. /package/dist/{cloud-ask-ai-CkCYjf2N.mjs → cloud-ask-ai-DeojA5lB.mjs} +0 -0
  55. /package/dist/{config-BaTmjDqD.mjs → config-BhF6J2N4.mjs} +0 -0
  56. /package/dist/{errors-DZiNXv5c.mjs → errors-B5KTe1Xo.mjs} +0 -0
  57. /package/dist/{templates-kc7jxspR.mjs → templates-m_j4ARnu.mjs} +0 -0
  58. /package/dist/{utils-DOMeZW9V.mjs → utils-DcCUwxqS.mjs} +0 -0
package/dist/mcp.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { a as emitDocsAnalyticsEvent, i as emitDocsAgentTraceEvent, n as createDocsAgentTraceContext, r as createDocsAgentTraceId } from "./analytics-Bx44lg6d.mjs";
2
- import { $n as resolveDocsMcpProtectedResourceMetadataLocation, An as normalizeAgentLocale, Dn as agentVersionConstraintMatches, E as DOCS_MARKDOWN_SECTION_INDEX_FORMAT, Fn as paginateDocsItems, Jn as isDocsMcpProtectedResourceMetadataPath, Jt as resolveDocsContentChangesConfig, Kt as isDocsContentChangeGeneration, Ln as normalizeDocsRelated, Nn as DocsPaginationCursorError, On as agentVersionConstraintsOverlap, P as buildDocsMarkdownSectionIndex, Qn as normalizeDocsMcpEndpointPath, Rn as renderDocsRelatedMarkdownLines, Tn as resolveDocsAudienceMdxContent, Ut as DocsContentChangesRequestError, Wt as createDocsContentChangeFeed, Xn as isDocsMcpResourcePath, Zn as normalizeDocsMcpAuthorizationServerUrls, dn as performDocsSearch, er as resolveDocsMcpResourceLocation, fn as performDocsSearchWithMetadata, jn as normalizeAgentScopeValues, kn as normalizeAgentFramework, qn as isDocsMcpOAuthScopeToken, tn as buildDocsSearchFacets, un as normalizeDocsSearchFilters, wt as toDocsMarkdownUrl, yn as resolveLocalDocsMcpSearchConfig } from "./agent-CFqECAG0.mjs";
2
+ import { $n as resolveDocsMcpProtectedResourceMetadataLocation, An as normalizeAgentLocale, Dn as agentVersionConstraintMatches, E as DOCS_MARKDOWN_SECTION_INDEX_FORMAT, Fn as paginateDocsItems, Jn as isDocsMcpProtectedResourceMetadataPath, Jt as resolveDocsContentChangesConfig, Kt as isDocsContentChangeGeneration, Ln as normalizeDocsRelated, Nn as DocsPaginationCursorError, On as agentVersionConstraintsOverlap, P as buildDocsMarkdownSectionIndex, Qn as normalizeDocsMcpEndpointPath, Rn as renderDocsRelatedMarkdownLines, Tn as resolveDocsAudienceMdxContent, Tt as validateDocsAgentFeedbackPayload, Ut as DocsContentChangesRequestError, Wt as createDocsContentChangeFeed, Xn as isDocsMcpResourcePath, Zn as normalizeDocsMcpAuthorizationServerUrls, ar as normalizeDocsOkfTrustMetadataInput, dn as performDocsSearch, er as resolveDocsMcpResourceLocation, fn as performDocsSearchWithMetadata, ir as buildDocsOkfBundle, jn as normalizeAgentScopeValues, kn as normalizeAgentFramework, lt as resolveDocsAgentFeedbackConfig, or as resolveDocsOkfConfig, qn as isDocsMcpOAuthScopeToken, tn as buildDocsSearchFacets, un as normalizeDocsSearchFilters, wt as toDocsMarkdownUrl, yn as resolveLocalDocsMcpSearchConfig } from "./agent-PSrgiJD8.mjs";
3
3
  import { S as upsertPageAgentContractMarkdown, _ as hasStructuredPageAgentContract, o as findDocsMarkdownSection, s as parseDocsMarkdownSections, v as normalizePageAgentFrontmatter, y as renderPageAgentContractMarkdown } from "./markdown-sections-7OoA7ylx.mjs";
4
4
  import { q as isDocsRetrievalCanonicalUrl } from "./standards-discovery-Ckx0tN7B.mjs";
5
- import { a as hydrateDocsContentChanges, f as stripGeneratedAgentProvenance, g as emitDocsTelemetryProjectEvent, h as emitDocsTelemetryMcpToolEvent, i as MIN_DOCS_CONTENT_CHANGE_HYDRATION_TOKEN_BUDGET, n as DOCS_CONTENT_CHANGE_HYDRATION_FORMAT, p as emitDocsTelemetryAgentSurfaceEvent, r as MAX_DOCS_CONTENT_CHANGE_HYDRATION_TOKEN_BUDGET, t as DEFAULT_DOCS_CONTENT_CHANGE_HYDRATION_TOKEN_BUDGET } from "./content-change-hydration-BsbBXAUi.mjs";
5
+ import { _ as emitDocsTelemetryMcpToolEvent, a as MAX_DOCS_CONTENT_CHANGE_HYDRATION_TOKEN_BUDGET, h as emitDocsTelemetryAgentSurfaceEvent, i as DOCS_CONTENT_CHANGE_HYDRATION_FORMAT, m as stripGeneratedAgentProvenance, n as resolveDocsOpenApiMcpOperations, o as MIN_DOCS_CONTENT_CHANGE_HYDRATION_TOKEN_BUDGET, r as DEFAULT_DOCS_CONTENT_CHANGE_HYDRATION_TOKEN_BUDGET, s as hydrateDocsContentChanges, t as resolveDocsOpenApiMcpBaseUrl, v as emitDocsTelemetryProjectEvent } from "./openapi-mcp-B7u7Y0gB.mjs";
6
6
  import { n as resolvePageSidebarFolderIndexBehavior } from "./sidebar-IcpElOVH.mjs";
7
7
  import matter from "gray-matter";
8
8
  import fs from "node:fs";
@@ -20,6 +20,8 @@ const DOCS_MCP_RESOURCE_CACHE_TTL_MS = 60 * 1e3;
20
20
  const DEFAULT_MCP_CONTEXT_TOKEN_BUDGET = 4e3;
21
21
  const MIN_MCP_CONTEXT_TOKEN_BUDGET = 256;
22
22
  const MAX_MCP_CONTEXT_TOKEN_BUDGET = 32e3;
23
+ const DEFAULT_MCP_READ_PAGES_TOKEN_BUDGET = 8e3;
24
+ const MAX_MCP_READ_PAGES_COUNT = 20;
23
25
  const DOCS_MCP_PROTOCOL_LIST_PAGE_SIZE = 10;
24
26
  const DOCS_MCP_TOOL_LIST_PAGE_SIZE = 25;
25
27
  const DOCS_MCP_PAGINATION_META_KEY = "dev.farming-labs/pagination";
@@ -233,6 +235,53 @@ const DOCS_CONFIG_SCHEMA_OPTIONS_TEMPLATE = [
233
235
  description: "Agent synchronization, reusable skills, compaction defaults, and offline-by-default usefulness evaluations.",
234
236
  docs: "/docs/getting-started/agent-ready-docs",
235
237
  children: [
238
+ {
239
+ path: "agent.okf",
240
+ name: "okf",
241
+ type: "boolean | DocsOkfConfig",
242
+ default: false,
243
+ description: "Publish Open Knowledge Format v0.2 source, generation, verification, lifecycle, and staleness metadata.",
244
+ children: [
245
+ {
246
+ path: "agent.okf.route",
247
+ name: "route",
248
+ type: "string",
249
+ default: "/.well-known/okf.json",
250
+ description: "Public route used by static Agent Bundle export."
251
+ },
252
+ {
253
+ path: "agent.okf.generatedBy",
254
+ name: "generatedBy",
255
+ type: "string",
256
+ default: "software:@farming-labs/docs",
257
+ description: "Generator actor used when a page omits okf.generated."
258
+ },
259
+ {
260
+ path: "agent.okf.staleAfterDays",
261
+ name: "staleAfterDays",
262
+ type: "number",
263
+ description: "Derive stale_after this many days after the best page timestamp."
264
+ },
265
+ {
266
+ path: "agent.okf.sources",
267
+ name: "sources",
268
+ type: "readonly DocsOkfSource[]",
269
+ description: "Default source provenance inherited by pages without authored sources."
270
+ },
271
+ {
272
+ path: "agent.okf.verified",
273
+ name: "verified",
274
+ type: "readonly DocsOkfActorTimestamp[]",
275
+ description: "Default machine or human verification records."
276
+ },
277
+ {
278
+ path: "agent.okf.status",
279
+ name: "status",
280
+ type: "\"draft\" | \"stable\" | \"deprecated\"",
281
+ description: "Default lifecycle status for knowledge documents."
282
+ }
283
+ ]
284
+ },
236
285
  {
237
286
  path: "agent.contentChanges",
238
287
  name: "contentChanges",
@@ -1380,40 +1429,55 @@ const DOCS_CONFIG_SCHEMA_OPTIONS_TEMPLATE = [
1380
1429
  path: "pageActions",
1381
1430
  name: "pageActions",
1382
1431
  type: "PageActionsConfig",
1383
- description: "Copy Markdown and Open in LLM actions for docs pages.",
1432
+ description: "Copy, Open in LLM, MCP connection, and Agent Skills setup actions.",
1384
1433
  docs: "/docs/customization/page-actions",
1385
- children: [{
1386
- path: "pageActions.copyMarkdown",
1387
- name: "copyMarkdown",
1388
- type: "boolean | PageActionConfig",
1389
- description: "Show a Copy Markdown action for the current page."
1390
- }, {
1391
- path: "pageActions.openDocs",
1392
- name: "openDocs",
1393
- type: "boolean | OpenDocsActionConfig",
1394
- description: "Show provider actions that open the current docs page in an LLM.",
1395
- children: [
1396
- {
1397
- path: "pageActions.openDocs.target",
1398
- name: "target",
1399
- type: "\"page\" | \"markdown\"",
1400
- default: "page",
1401
- description: "Whether provider URLs receive the rendered page URL or the .md markdown route."
1402
- },
1403
- {
1404
- path: "pageActions.openDocs.providers",
1405
- name: "providers",
1406
- type: "Array<string | PromptProviderConfig>",
1407
- description: "Provider IDs or provider objects. Built-ins include chatgpt, claude, cursor, and t3."
1408
- },
1409
- {
1410
- path: "pageActions.openDocs.prompt",
1411
- name: "prompt",
1412
- type: "string",
1413
- description: "Prompt text prepended to the provider URL when opening docs."
1414
- }
1415
- ]
1416
- }]
1434
+ children: [
1435
+ {
1436
+ path: "pageActions.copyMarkdown",
1437
+ name: "copyMarkdown",
1438
+ type: "boolean | PageActionConfig",
1439
+ description: "Show a Copy Markdown action for the current page."
1440
+ },
1441
+ {
1442
+ path: "pageActions.openDocs",
1443
+ name: "openDocs",
1444
+ type: "boolean | OpenDocsActionConfig",
1445
+ description: "Show provider actions that open the current docs page in an LLM.",
1446
+ children: [
1447
+ {
1448
+ path: "pageActions.openDocs.target",
1449
+ name: "target",
1450
+ type: "\"page\" | \"markdown\"",
1451
+ default: "page",
1452
+ description: "Whether provider URLs receive the rendered page URL or the .md markdown route."
1453
+ },
1454
+ {
1455
+ path: "pageActions.openDocs.providers",
1456
+ name: "providers",
1457
+ type: "Array<string | PromptProviderConfig>",
1458
+ description: "Provider IDs or provider objects. Built-ins include chatgpt, claude, cursor, and t3."
1459
+ },
1460
+ {
1461
+ path: "pageActions.openDocs.prompt",
1462
+ name: "prompt",
1463
+ type: "string",
1464
+ description: "Prompt text prepended to the provider URL when opening docs."
1465
+ }
1466
+ ]
1467
+ },
1468
+ {
1469
+ path: "pageActions.connectMcp",
1470
+ name: "connectMcp",
1471
+ type: "boolean | PageActionConnectMcpConfig",
1472
+ description: "Show copyable MCP setup for Claude Code, Cursor, VS Code, Codex, or a raw endpoint."
1473
+ },
1474
+ {
1475
+ path: "pageActions.installSkills",
1476
+ name: "installSkills",
1477
+ type: "boolean | PageActionInstallSkillsConfig",
1478
+ description: "Discover the published Agent Skills index and provide a copyable skills install command."
1479
+ }
1480
+ ]
1417
1481
  },
1418
1482
  {
1419
1483
  path: "ai",
@@ -1778,6 +1842,20 @@ const DOCS_CONFIG_SCHEMA_OPTIONS_TEMPLATE = [
1778
1842
  default: true,
1779
1843
  description: "Expose the read_page tool."
1780
1844
  },
1845
+ {
1846
+ path: "mcp.tools.readPages",
1847
+ name: "readPages",
1848
+ type: "boolean",
1849
+ default: true,
1850
+ description: "Expose the budget-aware read_pages batch tool."
1851
+ },
1852
+ {
1853
+ path: "mcp.tools.submitFeedback",
1854
+ name: "submitFeedback",
1855
+ type: "boolean",
1856
+ default: true,
1857
+ description: "Expose submit_feedback when feedback.agent is enabled and validate payloads with its configured schema."
1858
+ },
1781
1859
  {
1782
1860
  path: "mcp.tools.getCodeExamples",
1783
1861
  name: "getCodeExamples",
@@ -1798,6 +1876,13 @@ const DOCS_CONFIG_SCHEMA_OPTIONS_TEMPLATE = [
1798
1876
  type: "boolean",
1799
1877
  default: true,
1800
1878
  description: "Expose deterministic get_context retrieval with a conservative UTF-8 byte ceiling."
1879
+ },
1880
+ {
1881
+ path: "mcp.tools.getTrustMetadata",
1882
+ name: "getTrustMetadata",
1883
+ type: "boolean",
1884
+ default: true,
1885
+ description: "Expose OKF v0.2 trust metadata when agent.okf is enabled."
1801
1886
  }
1802
1887
  ]
1803
1888
  }
@@ -1828,6 +1913,47 @@ const DOCS_CONFIG_SCHEMA_OPTIONS_TEMPLATE = [
1828
1913
  name: "catalogTargets",
1829
1914
  type: "string[]",
1830
1915
  description: "Product API base URLs that the OpenAPI document describes in the RFC 9727 catalog."
1916
+ },
1917
+ {
1918
+ path: "apiReference.mcp",
1919
+ name: "mcp",
1920
+ type: "boolean | DocsOpenApiMcpConfig",
1921
+ default: false,
1922
+ description: "Project explicitly allowlisted OpenAPI operations into server-executed MCP tools.",
1923
+ children: [
1924
+ {
1925
+ path: "apiReference.mcp.operations",
1926
+ name: "operations",
1927
+ type: "readonly string[]",
1928
+ description: "Allowed operationIds or METHOD /path selectors; empty exposes nothing."
1929
+ },
1930
+ {
1931
+ path: "apiReference.mcp.baseUrl",
1932
+ name: "baseUrl",
1933
+ type: "string",
1934
+ description: "Override the first OpenAPI server URL used for tool requests."
1935
+ },
1936
+ {
1937
+ path: "apiReference.mcp.allowMutations",
1938
+ name: "allowMutations",
1939
+ type: "boolean",
1940
+ default: false,
1941
+ description: "Permit explicitly allowlisted write operations."
1942
+ },
1943
+ {
1944
+ path: "apiReference.mcp.headers",
1945
+ name: "headers",
1946
+ type: "DocsOpenApiMcpHeaders",
1947
+ description: "Server-owned credential headers applied after model-provided input."
1948
+ },
1949
+ {
1950
+ path: "apiReference.mcp.timeoutMs",
1951
+ name: "timeoutMs",
1952
+ type: "number",
1953
+ default: 1e4,
1954
+ description: "Per-operation HTTP timeout."
1955
+ }
1956
+ ]
1831
1957
  }
1832
1958
  ]
1833
1959
  },
@@ -1996,6 +2122,30 @@ const readPageInputSchema = z.object({
1996
2122
  section: z.string().trim().min(1).optional(),
1997
2123
  maxChars: z.number().int().min(256).max(1e6).optional()
1998
2124
  });
2125
+ const trustMetadataInputSchema = z.object({
2126
+ path: z.string().trim().min(1).optional(),
2127
+ locale: z.string().trim().min(1).max(128).optional()
2128
+ });
2129
+ const openApiOperationInputSchema = z.object({
2130
+ parameters: z.record(z.string(), z.unknown()).optional(),
2131
+ body: z.unknown().optional()
2132
+ });
2133
+ const readPagesInputSchema = z.object({
2134
+ paths: z.array(z.string().trim().min(1)).min(1).max(MAX_MCP_READ_PAGES_COUNT).describe("Page slugs or URL paths to read in the requested order."),
2135
+ locale: z.string().trim().min(1).max(128).optional(),
2136
+ tokenBudget: z.number().int().min(MIN_MCP_CONTEXT_TOKEN_BUDGET).max(MAX_MCP_CONTEXT_TOKEN_BUDGET).optional(),
2137
+ maxCharsPerPage: z.number().int().min(256).max(1e6).optional()
2138
+ });
2139
+ const submitFeedbackInputSchema = z.object({
2140
+ context: z.object({
2141
+ page: z.string().optional(),
2142
+ url: z.string().optional(),
2143
+ slug: z.string().optional(),
2144
+ locale: z.string().optional(),
2145
+ source: z.string().optional()
2146
+ }).optional(),
2147
+ payload: z.record(z.string(), z.unknown())
2148
+ });
1999
2149
  const listPageSectionsInputSchema = z.object({
2000
2150
  path: z.string().min(1),
2001
2151
  locale: z.string().trim().min(1).max(128).optional(),
@@ -2304,7 +2454,8 @@ const searchResultOutputSchema = z.object({
2304
2454
  ]),
2305
2455
  score: z.number().optional(),
2306
2456
  section: z.string().optional(),
2307
- source: retrievalSourceOutputSchema.optional()
2457
+ source: retrievalSourceOutputSchema.optional(),
2458
+ trust: z.record(z.string(), z.unknown()).optional()
2308
2459
  });
2309
2460
  const searchFiltersOutputSchema = z.object({
2310
2461
  framework: z.array(z.string()).optional(),
@@ -2522,6 +2673,45 @@ const readPageOutputSchema = z.object({
2522
2673
  totalChars: z.number().int().nonnegative(),
2523
2674
  truncated: z.boolean()
2524
2675
  });
2676
+ const trustMetadataOutputSchema = z.object({
2677
+ format: z.literal("open-knowledge-format.v0.2"),
2678
+ spec_version: z.literal("0.2"),
2679
+ generated: z.object({
2680
+ by: z.string(),
2681
+ at: z.string()
2682
+ }),
2683
+ documents: z.array(z.record(z.string(), z.unknown()))
2684
+ });
2685
+ const openApiOperationOutputSchema = z.object({
2686
+ operationId: z.string(),
2687
+ status: z.number().int(),
2688
+ ok: z.boolean(),
2689
+ contentType: z.string().optional(),
2690
+ body: z.unknown()
2691
+ });
2692
+ const readPagesOutputSchema = z.object({
2693
+ format: z.literal("docs-read-pages.v1"),
2694
+ budget: z.object({
2695
+ requestedTokens: z.number().int().positive(),
2696
+ strategy: z.literal("utf8-bytes"),
2697
+ maxUtf8Bytes: z.number().int().positive(),
2698
+ usedUtf8Bytes: z.number().int().nonnegative(),
2699
+ remainingUtf8Bytes: z.number().int().nonnegative(),
2700
+ truncated: z.boolean()
2701
+ }),
2702
+ resultCount: z.number().int().nonnegative(),
2703
+ requestedCount: z.number().int().positive(),
2704
+ pages: z.array(readPageOutputSchema.extend({ requestedPath: z.string() })),
2705
+ errors: z.array(z.object({
2706
+ path: z.string(),
2707
+ error: z.string()
2708
+ })),
2709
+ remainingPaths: z.array(z.string())
2710
+ });
2711
+ const submitFeedbackOutputSchema = z.object({
2712
+ accepted: z.literal(true),
2713
+ message: z.string()
2714
+ });
2525
2715
  const pageSectionMetadataOutputSchema = z.object({
2526
2716
  id: z.string(),
2527
2717
  heading: z.string(),
@@ -2609,6 +2799,8 @@ function resolveDocsMcpConfig(mcp, defaults = {}) {
2609
2799
  listPages: true,
2610
2800
  listPageSections: true,
2611
2801
  readPage: true,
2802
+ readPages: true,
2803
+ submitFeedback: true,
2612
2804
  listTasks: true,
2613
2805
  readTask: true,
2614
2806
  searchDocs: true,
@@ -2618,7 +2810,8 @@ function resolveDocsMcpConfig(mcp, defaults = {}) {
2618
2810
  getNavigation: true,
2619
2811
  getCodeExamples: true,
2620
2812
  getConfigSchema: true,
2621
- getContext: true
2813
+ getContext: true,
2814
+ getTrustMetadata: true
2622
2815
  },
2623
2816
  prompts: resolveDocsMcpPromptsConfig(),
2624
2817
  security: resolveDocsMcpSecurityConfig()
@@ -2634,6 +2827,8 @@ function resolveDocsMcpConfig(mcp, defaults = {}) {
2634
2827
  listPages: config.tools?.listPages ?? true,
2635
2828
  listPageSections: config.tools?.listPageSections ?? true,
2636
2829
  readPage: config.tools?.readPage ?? true,
2830
+ readPages: config.tools?.readPages ?? true,
2831
+ submitFeedback: config.tools?.submitFeedback ?? true,
2637
2832
  listTasks: config.tools?.listTasks ?? true,
2638
2833
  readTask: config.tools?.readTask ?? true,
2639
2834
  searchDocs: config.tools?.searchDocs ?? true,
@@ -2643,7 +2838,8 @@ function resolveDocsMcpConfig(mcp, defaults = {}) {
2643
2838
  getNavigation: config.tools?.getNavigation ?? true,
2644
2839
  getCodeExamples: config.tools?.getCodeExamples ?? true,
2645
2840
  getConfigSchema: config.tools?.getConfigSchema ?? true,
2646
- getContext: config.tools?.getContext ?? true
2841
+ getContext: config.tools?.getContext ?? true,
2842
+ getTrustMetadata: config.tools?.getTrustMetadata ?? true
2647
2843
  },
2648
2844
  prompts: resolveDocsMcpPromptsConfig(config.prompts),
2649
2845
  security: resolveDocsMcpSecurityConfig(config.security)
@@ -3078,6 +3274,7 @@ async function createDocsMcpServer(options) {
3078
3274
  });
3079
3275
  const contentChangesEnabled = resolveDocsContentChangesConfig(options.contentChanges).enabled && resolved.tools.listContentChanges !== false;
3080
3276
  const contentChangeHydrationEnabled = contentChangesEnabled && resolved.tools.hydrateContentChanges !== false;
3277
+ const agentFeedback = resolveDocsAgentFeedbackConfig(options.feedback);
3081
3278
  const cacheScope = options.requestContext?.auth ? "private" : "public";
3082
3279
  const server = new McpServer({
3083
3280
  name: resolved.name,
@@ -3146,10 +3343,126 @@ async function createDocsMcpServer(options) {
3146
3343
  const defaultPages = dedupePages(await getSourcePages());
3147
3344
  const defaultTree = await getSourceNavigation();
3148
3345
  const defaultSkills = await getSourceSkills();
3346
+ const okfConfig = resolveDocsOkfConfig(options.okf);
3347
+ const openapiDocument = options.openapi ? typeof options.openapi.document === "function" ? await options.openapi.document() : options.openapi.document : void 0;
3348
+ const openapiConfig = options.openapi?.config;
3349
+ const openapiOperations = openapiDocument && openapiConfig ? resolveDocsOpenApiMcpOperations(openapiDocument, openapiConfig) : [];
3149
3350
  const prompts = resolved.prompts ?? resolveDocsMcpPromptsConfig();
3150
3351
  const contractPromptDefinitions = prompts.enabled ? resolveDocsMcpContractPromptDefinitions(defaultPages, prompts.contracts, options.source.entry) : [];
3151
3352
  const goldenPromptDefinitions = prompts.enabled ? resolveDocsMcpGoldenPromptDefinitions(prompts.goldenTasks, options.evaluations) : [];
3152
3353
  if (contractPromptDefinitions.length > 0 || goldenPromptDefinitions.length > 0) server.server.registerCapabilities({ prompts: { listChanged: false } });
3354
+ if (okfConfig.enabled && resolved.tools.getTrustMetadata !== false) registerTool("get_trust_metadata", {
3355
+ title: "Get documentation trust metadata",
3356
+ description: "Return OKF v0.2 sources, generation, verification, lifecycle status, trust tier, and staleness for one page or the docs corpus.",
3357
+ inputSchema: trustMetadataInputSchema,
3358
+ outputSchema: trustMetadataOutputSchema,
3359
+ annotations: { readOnlyHint: true }
3360
+ }, async ({ path: requestedPath, locale }) => {
3361
+ const pages = dedupePages(await getSourcePages(locale));
3362
+ const selected = requestedPath ? pages.filter((page) => Boolean(findDocsPage([page], requestedPath, options.source.entry))) : pages;
3363
+ if (requestedPath && selected.length === 0) return {
3364
+ content: [{
3365
+ type: "text",
3366
+ text: `No docs page matched "${requestedPath}".`
3367
+ }],
3368
+ isError: true
3369
+ };
3370
+ const bundle = buildDocsOkfBundle(selected, okfConfig);
3371
+ trackMcpTool("get_trust_metadata", {
3372
+ locale,
3373
+ resultCount: bundle.documents.length
3374
+ });
3375
+ return createStructuredTextResult(bundle);
3376
+ });
3377
+ for (const operation of openapiOperations) registerTool(operation.toolName, {
3378
+ title: operation.title,
3379
+ description: operation.description ?? `${operation.method} ${operation.path} (${operation.operationId})`,
3380
+ inputSchema: openApiOperationInputSchema,
3381
+ outputSchema: openApiOperationOutputSchema,
3382
+ annotations: {
3383
+ readOnlyHint: operation.readOnly,
3384
+ destructiveHint: operation.destructive,
3385
+ idempotentHint: operation.idempotent,
3386
+ openWorldHint: true
3387
+ },
3388
+ _meta: { "dev.farming-labs/openapi": {
3389
+ operationId: operation.operationId,
3390
+ method: operation.method,
3391
+ path: operation.path,
3392
+ security: operation.security,
3393
+ securitySchemes: operation.securitySchemes
3394
+ } }
3395
+ }, async ({ parameters = {}, body }) => {
3396
+ if (!openapiDocument || !openapiConfig) throw new ProtocolError(ProtocolErrorCode.InternalError, "OpenAPI MCP is unavailable.");
3397
+ const baseUrl = resolveDocsOpenApiMcpBaseUrl(openapiDocument, openapiConfig);
3398
+ if (!baseUrl) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `OpenAPI operation ${operation.operationId} has no server URL. Configure apiReference.mcp.baseUrl.`);
3399
+ let requestPath = operation.path;
3400
+ const query = new URLSearchParams();
3401
+ const requestHeaders = new Headers({ Accept: "application/json, text/plain, */*" });
3402
+ const requestCookies = new URLSearchParams();
3403
+ for (const parameter of operation.parameters) {
3404
+ const value = parameters[parameter.name];
3405
+ if (value === void 0 || value === null || value === "") {
3406
+ if (parameter.required) return {
3407
+ content: [{
3408
+ type: "text",
3409
+ text: `Missing required ${parameter.in} parameter: ${parameter.name}`
3410
+ }],
3411
+ isError: true
3412
+ };
3413
+ continue;
3414
+ }
3415
+ const values = Array.isArray(value) ? value : [value];
3416
+ if (parameter.in === "path") requestPath = requestPath.replaceAll(`{${parameter.name}}`, encodeURIComponent(String(values[0])));
3417
+ else if (parameter.in === "query") for (const item of values) query.append(parameter.name, String(item));
3418
+ else if (parameter.in === "header") requestHeaders.set(parameter.name, String(values[0]));
3419
+ else if (parameter.in === "cookie") requestCookies.set(parameter.name, String(values[0]));
3420
+ }
3421
+ const requestUrl = new URL(requestPath.replace(/^\/+/, ""), `${baseUrl.replace(/\/+$/u, "")}/`);
3422
+ for (const [name, value] of query) requestUrl.searchParams.append(name, value);
3423
+ if (requestUrl.protocol !== "https:" && requestUrl.protocol !== "http:") throw new ProtocolError(ProtocolErrorCode.InvalidParams, "OpenAPI MCP server URLs must use HTTP or HTTPS.");
3424
+ const configuredHeaders = typeof openapiConfig.headers === "function" ? await openapiConfig.headers({
3425
+ operationId: operation.operationId,
3426
+ method: operation.method,
3427
+ path: operation.path,
3428
+ security: operation.security
3429
+ }) : openapiConfig.headers;
3430
+ for (const [name, value] of Object.entries(configuredHeaders ?? {})) requestHeaders.set(name, value);
3431
+ if (requestCookies.size > 0) requestHeaders.set("Cookie", [...requestCookies].map(([name, value]) => `${name}=${value}`).join("; "));
3432
+ if (body !== void 0) requestHeaders.set("Content-Type", "application/json");
3433
+ const controller = new AbortController();
3434
+ const timeout = setTimeout(() => controller.abort(), openapiConfig.timeoutMs ?? 1e4);
3435
+ try {
3436
+ const response = await fetch(requestUrl, {
3437
+ method: operation.method,
3438
+ headers: requestHeaders,
3439
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {},
3440
+ signal: controller.signal
3441
+ });
3442
+ const contentType = response.headers.get("content-type") ?? void 0;
3443
+ const text = (await response.text()).slice(0, 1e6);
3444
+ let responseBody = text;
3445
+ if (contentType?.includes("json") && text) try {
3446
+ responseBody = JSON.parse(text);
3447
+ } catch {
3448
+ responseBody = text;
3449
+ }
3450
+ const result = {
3451
+ operationId: operation.operationId,
3452
+ status: response.status,
3453
+ ok: response.ok,
3454
+ ...contentType ? { contentType } : {},
3455
+ body: responseBody
3456
+ };
3457
+ trackMcpTool(operation.toolName, { resultCount: 1 });
3458
+ return {
3459
+ ...createStructuredTextResult(result),
3460
+ ...response.ok ? {} : { isError: true }
3461
+ };
3462
+ } finally {
3463
+ clearTimeout(timeout);
3464
+ }
3465
+ });
3153
3466
  for (const definition of contractPromptDefinitions) {
3154
3467
  const { page, contract, resourceUri } = definition;
3155
3468
  server.registerPrompt(definition.name, {
@@ -4580,6 +4893,111 @@ async function createDocsMcpServer(options) {
4580
4893
  throw error;
4581
4894
  }
4582
4895
  });
4896
+ if (resolved.tools.readPages !== false) registerTool("read_pages", {
4897
+ title: "Read several docs pages",
4898
+ description: "Read up to 20 documentation pages in one round trip. Results preserve request order and share a conservative UTF-8 token budget.",
4899
+ inputSchema: readPagesInputSchema,
4900
+ outputSchema: readPagesOutputSchema,
4901
+ annotations: { readOnlyHint: true }
4902
+ }, async ({ paths: requestedPaths, locale, tokenBudget = DEFAULT_MCP_READ_PAGES_TOKEN_BUDGET, maxCharsPerPage }) => {
4903
+ const pages = dedupePages(await getSourcePages(locale));
4904
+ const maxUtf8Bytes = tokenBudget * 4;
4905
+ let usedUtf8Bytes = 0;
4906
+ let processedCount = 0;
4907
+ let truncated = false;
4908
+ const results = [];
4909
+ const errors = [];
4910
+ for (const requestedPath of requestedPaths) {
4911
+ if (usedUtf8Bytes >= maxUtf8Bytes) {
4912
+ truncated = true;
4913
+ break;
4914
+ }
4915
+ processedCount += 1;
4916
+ const page = findDocsPage(pages, requestedPath, options.source.entry);
4917
+ if (!page) {
4918
+ errors.push({
4919
+ path: requestedPath,
4920
+ error: `No docs page matched "${requestedPath}".`
4921
+ });
4922
+ continue;
4923
+ }
4924
+ const fullDocument = renderPageDocument(page);
4925
+ const perPage = limitDocsMcpText(fullDocument, maxCharsPerPage);
4926
+ const remainingUtf8Bytes = maxUtf8Bytes - usedUtf8Bytes;
4927
+ const budgeted = limitDocsMcpUtf8Bytes(perPage.text, remainingUtf8Bytes);
4928
+ const document = budgeted.text;
4929
+ const documentBytes = docsMcpUtf8Bytes(document);
4930
+ usedUtf8Bytes += documentBytes;
4931
+ const pageTruncated = perPage.truncated || budgeted.truncated;
4932
+ truncated ||= pageTruncated;
4933
+ results.push({
4934
+ requestedPath,
4935
+ page: toStructuredDocsMcpPage(page),
4936
+ document,
4937
+ chars: document.length,
4938
+ totalChars: fullDocument.length,
4939
+ truncated: pageTruncated
4940
+ });
4941
+ }
4942
+ const result = {
4943
+ format: "docs-read-pages.v1",
4944
+ budget: {
4945
+ requestedTokens: tokenBudget,
4946
+ strategy: "utf8-bytes",
4947
+ maxUtf8Bytes,
4948
+ usedUtf8Bytes,
4949
+ remainingUtf8Bytes: Math.max(0, maxUtf8Bytes - usedUtf8Bytes),
4950
+ truncated: truncated || processedCount < requestedPaths.length
4951
+ },
4952
+ resultCount: results.length,
4953
+ requestedCount: requestedPaths.length,
4954
+ pages: results,
4955
+ errors,
4956
+ remainingPaths: requestedPaths.slice(processedCount)
4957
+ };
4958
+ trackMcpTool("read_pages", {
4959
+ locale,
4960
+ resultCount: results.length
4961
+ });
4962
+ return createStructuredTextResult(result);
4963
+ });
4964
+ if (resolved.tools.submitFeedback !== false && agentFeedback.enabled) registerTool("submit_feedback", {
4965
+ title: "Submit documentation feedback",
4966
+ description: "Submit machine-readable documentation feedback. The payload is validated against the site's configured agent feedback schema before delivery.",
4967
+ inputSchema: submitFeedbackInputSchema,
4968
+ outputSchema: submitFeedbackOutputSchema,
4969
+ annotations: {
4970
+ readOnlyHint: false,
4971
+ destructiveHint: false,
4972
+ idempotentHint: false
4973
+ },
4974
+ _meta: { "dev.farming-labs/feedbackSchema": agentFeedback.schema }
4975
+ }, async ({ context, payload }) => {
4976
+ const validationError = validateDocsAgentFeedbackPayload(payload, agentFeedback.payloadSchema);
4977
+ if (validationError) return {
4978
+ content: [{
4979
+ type: "text",
4980
+ text: validationError
4981
+ }],
4982
+ isError: true
4983
+ };
4984
+ const feedbackContext = {
4985
+ ...context,
4986
+ source: context?.source ?? "mcp"
4987
+ };
4988
+ await agentFeedback.onFeedback?.({
4989
+ ...Object.keys(feedbackContext).length > 0 ? { context: feedbackContext } : {},
4990
+ payload
4991
+ });
4992
+ trackMcpTool("submit_feedback", {
4993
+ locale: feedbackContext.locale,
4994
+ resultCount: 1
4995
+ });
4996
+ return createStructuredTextResult({
4997
+ accepted: true,
4998
+ message: "Feedback accepted."
4999
+ });
5000
+ });
4583
5001
  if (resolved.tools.readPage) registerTool("read_page", {
4584
5002
  title: "Read a docs page",
4585
5003
  description: "Read a documentation page by slug or URL path, optionally selecting one heading and limiting returned characters.",
@@ -5440,6 +5858,7 @@ function scanFilesystemDocsPages(contentDirAbs, entry, rootDir) {
5440
5858
  description: data.description,
5441
5859
  relatedInput: data.related,
5442
5860
  agent: normalizePageAgentFrontmatter(data.agent),
5861
+ okf: normalizeDocsOkfTrustMetadataInput(data.okf),
5443
5862
  icon: data.icon,
5444
5863
  sourcePath: path.relative(rootDir, full).replace(/\\/g, "/"),
5445
5864
  lastmod: normalizeFrontmatterLastmod(data.lastmod),