@farming-labs/theme 0.2.103 → 0.2.105

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.
@@ -99,6 +99,8 @@ interface DocsMCPAPIOptions {
99
99
  telemetry?: boolean | DocsTelemetryConfig;
100
100
  observability?: boolean | DocsObservabilityConfig;
101
101
  agent?: DocsConfig["agent"];
102
+ apiReference?: DocsConfig["apiReference"];
103
+ feedback?: DocsConfig["feedback"];
102
104
  /** @internal Build-time Agent Skills snapshot supplied by framework adapters. */
103
105
  _preloadedAgentSkills?: readonly DocsPublishedAgentSkill[];
104
106
  }
package/dist/docs-api.mjs CHANGED
@@ -4,8 +4,8 @@ import { BoundedRouteCache } from "./bounded-route-cache.mjs";
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import matter from "gray-matter";
7
- import { DEFAULT_A2A_AGENT_CARD_ROUTE, DEFAULT_AGENT_SKILLS_ARCHIVE_ROUTE_PATTERN, DEFAULT_AGENT_SKILLS_INDEX_ROUTE, DEFAULT_AGENT_SKILLS_ROUTE_PREFIX, DEFAULT_API_CATALOG_ROUTE, DEFAULT_LEGACY_SKILLS_INDEX_ROUTE, DEFAULT_SITEMAP_MD_DOCS_ROUTE, DOCS_AGENT_MANIFEST_FORMAT, DOCS_AGENT_MANIFEST_SCHEMA_URI, DOCS_AGENT_MANIFEST_VERSION, DOCS_CONTENT_CHANGES_FORMAT, DOCS_CONTENT_CHANGES_RESPONSE_VALUE, DOCS_MARKDOWN_SECTION_INDEX_FORMAT, acceptsDocsMarkdown, buildDocsAskAIContext, buildDocsConfigMap, buildDocsDiagnostics, buildDocsSearchFacets, createDocsAgentTraceContext, createDocsAgentTraceId, createDocsCacheableResponse, createDocsContentChangeFeed, createDocsContentChangesHttpResponse, createDocsMarkdownResponse, createDocsRobotsResponse, createDocsSitemapResponse, createDocsStandardsDiscoveryResponse, detectDocsMarkdownAgentRequest, emitDocsAgentTraceEvent, emitDocsAnalyticsEvent, emitDocsTelemetryAgentSurfaceEvent, emitDocsTelemetryProjectEvent, formatDocsAskAIPackageHints, getDocsAgentManifestLinkHeader, getDocsDiscoveryLinkHeader, getDocsLlmsTxtMaxCharsIssue, getDocsMcpProtectedResourceMetadataRoutes, hasDocsMarkdownSignatureAgent, inferDocsTelemetryAgentSurface, isDocsConfigRequest, isDocsContentChangesRequest, isDocsDiagnosticsRequest, normalizeDocsRelated, normalizePageAgentFrontmatter, performDocsSearch, performDocsSearchWithMetadata, renderDocsLlmsTxt, renderDocsMarkdownDocument, resolveAskAISearchRequestConfig, resolveChangelogConfig, resolveDocsAgentContractMcpTools, resolveDocsAudienceMdxContent, resolveDocsContentChangesConfig, resolveDocsI18n, resolveDocsLlmsTxtRequest, resolveDocsLlmsTxtSections, resolveDocsLocale, resolveDocsMetadataBaseUrl, resolveDocsPublishedAgentSkill, resolveDocsRequestApiRoute, resolveDocsRetrievalLastModified, resolveDocsSearchAudience, resolveDocsSearchError, resolveDocsSearchRequest, resolveDocsSitemapConfig, resolveDocsStandardsDiscoveryRequest, resolvePageSidebarFolderIndexBehavior, resolveSearchRequestConfig, selectDocsLlmsTxtContent, stripGeneratedAgentProvenance } from "@farming-labs/docs";
8
- import { buildApiReferenceOpenApiDocumentAsync, createDocsMcpHttpHandler, createFilesystemDocsMcpSource, readDocsSitemapManifest, resolveApiReferenceOpenApiDiscovery, resolveConfiguredAgentSkills, resolveDocsMcpConfig } from "@farming-labs/docs/server";
7
+ import { DEFAULT_A2A_AGENT_CARD_ROUTE, DEFAULT_AGENT_SKILLS_ARCHIVE_ROUTE_PATTERN, DEFAULT_AGENT_SKILLS_INDEX_ROUTE, DEFAULT_AGENT_SKILLS_ROUTE_PREFIX, DEFAULT_API_CATALOG_ROUTE, DEFAULT_LEGACY_SKILLS_INDEX_ROUTE, DEFAULT_SITEMAP_MD_DOCS_ROUTE, DOCS_AGENT_MANIFEST_FORMAT, DOCS_AGENT_MANIFEST_SCHEMA_URI, DOCS_AGENT_MANIFEST_VERSION, DOCS_CONTENT_CHANGES_FORMAT, DOCS_CONTENT_CHANGES_RESPONSE_VALUE, DOCS_MARKDOWN_SECTION_INDEX_FORMAT, acceptsDocsMarkdown, buildDocsAskAIContext, buildDocsConfigMap, buildDocsDiagnostics, buildDocsSearchFacets, compactDocsAgentDiscoverySpec, createDocsAgentTraceContext, createDocsAgentTraceId, createDocsCacheableResponse, createDocsContentChangeFeed, createDocsContentChangesHttpResponse, createDocsMarkdownResponse, createDocsRobotsResponse, createDocsSitemapResponse, createDocsStandardsDiscoveryResponse, detectDocsMarkdownAgentRequest, emitDocsAgentTraceEvent, emitDocsAnalyticsEvent, emitDocsTelemetryAgentSurfaceEvent, emitDocsTelemetryProjectEvent, formatDocsAskAIPackageHints, getDocsAgentManifestLinkHeader, getDocsDiscoveryLinkHeader, getDocsLlmsTxtMaxCharsIssue, getDocsMcpProtectedResourceMetadataRoutes, hasDocsMarkdownSignatureAgent, inferDocsTelemetryAgentSurface, isDocsConfigRequest, isDocsContentChangesRequest, isDocsDiagnosticsRequest, normalizeDocsOkfTrustMetadataInput, normalizeDocsRelated, normalizePageAgentFrontmatter, performDocsSearch, performDocsSearchWithMetadata, renderDocsLlmsTxt, renderDocsMarkdownDocument, resolveAskAISearchRequestConfig, resolveChangelogConfig, resolveDocsAgentContractMcpTools, resolveDocsAudienceMdxContent, resolveDocsContentChangesConfig, resolveDocsI18n, resolveDocsLlmsTxtRequest, resolveDocsLlmsTxtSections, resolveDocsLocale, resolveDocsMetadataBaseUrl, resolveDocsPublishedAgentSkill, resolveDocsRequestApiRoute, resolveDocsRetrievalLastModified, resolveDocsSearchAudience, resolveDocsSearchError, resolveDocsSearchRequest, resolveDocsSitemapConfig, resolveDocsStandardsDiscoveryRequest, resolvePageSidebarFolderIndexBehavior, resolveSearchRequestConfig, selectDocsLlmsTxtContent, stripGeneratedAgentProvenance } from "@farming-labs/docs";
8
+ import { buildApiReferenceOpenApiDocumentAsync, createDocsMcpHttpHandler, createFilesystemDocsMcpSource, readDocsSitemapManifest, resolveApiReferenceConfig, resolveApiReferenceOpenApiDiscovery, resolveConfiguredAgentSkills, resolveDocsMcpConfig } from "@farming-labs/docs/server";
9
9
 
10
10
  //#region src/docs-api.ts
11
11
  /**
@@ -214,6 +214,12 @@ function buildAgentSpec({ origin, entry, apiRoute, apiCatalog, i18n, search, con
214
214
  format: DOCS_AGENT_MANIFEST_FORMAT,
215
215
  version: DOCS_AGENT_MANIFEST_VERSION,
216
216
  name: "@farming-labs/docs",
217
+ profile: "full",
218
+ profiles: {
219
+ default: "full",
220
+ full: DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE,
221
+ compact: `${DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE}?profile=compact`
222
+ },
217
223
  baseUrl: origin,
218
224
  site: {
219
225
  title: llms.siteTitle ?? "Documentation",
@@ -1037,6 +1043,7 @@ function scanDocsDir(docsDir, entry, locale, excludedDirs = [], publicPath = `/$
1037
1043
  description,
1038
1044
  relatedInput: data.related,
1039
1045
  agent: normalizePageAgentFrontmatter(data.agent),
1046
+ okf: normalizeDocsOkfTrustMetadataInput(data.okf),
1040
1047
  content,
1041
1048
  rawContent,
1042
1049
  agentFallbackRawContent: agentRawContent !== rawContent ? agentRawContent : void 0,
@@ -1105,6 +1112,7 @@ function scanChangelogDir(changelogDir, entryPath, changelogPath, locale, public
1105
1112
  description,
1106
1113
  relatedInput: data.related,
1107
1114
  agent: normalizePageAgentFrontmatter(data.agent),
1115
+ okf: normalizeDocsOkfTrustMetadataInput(data.okf),
1108
1116
  content,
1109
1117
  rawContent,
1110
1118
  agentFallbackRawContent: agentRawContent !== rawContent ? agentRawContent : void 0,
@@ -1321,7 +1329,8 @@ function renderMarkdownDocument(page, options = {}) {
1321
1329
  return renderDocsMarkdownDocument(page, {
1322
1330
  llms: options.llmsEnabled !== false,
1323
1331
  origin: options.origin,
1324
- sitemap: options.sitemap
1332
+ sitemap: options.sitemap,
1333
+ okf: options.okf
1325
1334
  });
1326
1335
  }
1327
1336
  function renderSkillDocument({ origin, entry, apiRoute, apiCatalog, search, contentChanges, mcp, feedback, llms, sitemap, robots, openapi }) {
@@ -2771,9 +2780,11 @@ function createDocsAPI(options) {
2771
2780
  document: renderMarkdownDocument(withPublicDocsUrl(page, ctx), {
2772
2781
  llmsEnabled: llmsConfig.enabled,
2773
2782
  origin,
2774
- sitemap: sitemapConfig
2783
+ sitemap: sitemapConfig,
2784
+ okf: options?.agent?.okf
2775
2785
  }),
2776
- lastModified: resolveDocsRetrievalLastModified(page, "agent")
2786
+ lastModified: resolveDocsRetrievalLastModified(page, "agent"),
2787
+ access: page.agent?.access
2777
2788
  };
2778
2789
  }
2779
2790
  const fallbackPage = getIndexes(ctx).find((page) => {
@@ -2784,18 +2795,22 @@ function createDocsAPI(options) {
2784
2795
  document: renderMarkdownDocument(withPublicDocsUrl(fallbackPage, ctx), {
2785
2796
  llmsEnabled: llmsConfig.enabled,
2786
2797
  origin,
2787
- sitemap: sitemapConfig
2798
+ sitemap: sitemapConfig,
2799
+ okf: options?.agent?.okf
2788
2800
  }),
2789
- lastModified: resolveDocsRetrievalLastModified(fallbackPage, "agent")
2801
+ lastModified: resolveDocsRetrievalLastModified(fallbackPage, "agent"),
2802
+ access: fallbackPage.agent?.access
2790
2803
  };
2791
2804
  const requestedSlug = normalizePublicDocsSlug(ctx, normalizedPublicRequest);
2792
2805
  for (const page of getIndexes(ctx)) if (normalizePublicDocsSlug(ctx, page.url) === requestedSlug) return {
2793
2806
  document: renderMarkdownDocument(withPublicDocsUrl(page, ctx), {
2794
2807
  llmsEnabled: llmsConfig.enabled,
2795
2808
  origin,
2796
- sitemap: sitemapConfig
2809
+ sitemap: sitemapConfig,
2810
+ okf: options?.agent?.okf
2797
2811
  }),
2798
- lastModified: resolveDocsRetrievalLastModified(page, "agent")
2812
+ lastModified: resolveDocsRetrievalLastModified(page, "agent"),
2813
+ access: page.agent?.access
2799
2814
  };
2800
2815
  return null;
2801
2816
  }
@@ -2931,31 +2946,34 @@ function createDocsAPI(options) {
2931
2946
  method: requestMethod
2932
2947
  }
2933
2948
  });
2949
+ const fullSpec = buildAgentSpec({
2950
+ origin: url.origin,
2951
+ entry,
2952
+ apiRoute: requestApiRoute,
2953
+ apiCatalog: apiCatalogEnabled,
2954
+ i18n,
2955
+ search: searchConfig,
2956
+ contentChanges: contentChangesConfig.enabled,
2957
+ mcp: mcpConfig,
2958
+ feedback: agentFeedbackConfig,
2959
+ llms: llmsConfig,
2960
+ sitemap: sitemapConfig,
2961
+ robots: robotsConfig,
2962
+ openapi: openapiDiscovery,
2963
+ publishedSkills: [await resolveDocsPublishedAgentSkill({
2964
+ preferredDocument: readRootSkillDocument(),
2965
+ fallbackDocument: getGeneratedSkillDocument(url.origin, requestApiRoute)
2966
+ }), ...await getPublishedAgentSkills()],
2967
+ agentCard: options?.agent?.a2a
2968
+ });
2969
+ const compact = url.searchParams.get("profile") === "compact";
2970
+ const spec = compact ? compactDocsAgentDiscoverySpec(fullSpec) : fullSpec;
2934
2971
  return createDocsCacheableResponse({
2935
2972
  request,
2936
- content: `${JSON.stringify(buildAgentSpec({
2937
- origin: url.origin,
2938
- entry,
2939
- apiRoute: requestApiRoute,
2940
- apiCatalog: apiCatalogEnabled,
2941
- i18n,
2942
- search: searchConfig,
2943
- contentChanges: contentChangesConfig.enabled,
2944
- mcp: mcpConfig,
2945
- feedback: agentFeedbackConfig,
2946
- llms: llmsConfig,
2947
- sitemap: sitemapConfig,
2948
- robots: robotsConfig,
2949
- openapi: openapiDiscovery,
2950
- publishedSkills: [await resolveDocsPublishedAgentSkill({
2951
- preferredDocument: readRootSkillDocument(),
2952
- fallbackDocument: getGeneratedSkillDocument(url.origin, requestApiRoute)
2953
- }), ...await getPublishedAgentSkills()],
2954
- agentCard: options?.agent?.a2a
2955
- }), null, 2)}\n`,
2973
+ content: `${JSON.stringify(spec, null, compact ? void 0 : 2)}\n`,
2956
2974
  headers: {
2957
2975
  "Content-Type": "application/json; charset=utf-8",
2958
- "Cache-Control": "public, max-age=0, s-maxage=3600",
2976
+ "Cache-Control": "public, max-age=0, s-maxage=3600, stale-while-revalidate=86400",
2959
2977
  Link: agentManifestLinkHeader,
2960
2978
  "X-Robots-Tag": "noindex"
2961
2979
  }
@@ -3181,6 +3199,7 @@ function createDocsAPI(options) {
3181
3199
  canonicalUrl,
3182
3200
  locale: ctx.locale,
3183
3201
  lastModified: representation?.lastModified,
3202
+ access: representation?.access,
3184
3203
  sitemap: sitemapConfig
3185
3204
  });
3186
3205
  }
@@ -3429,27 +3448,39 @@ function createDocsMCPAPI(options = {}) {
3429
3448
  if (!configuredAgentSkills) configuredAgentSkills = Array.isArray(options._preloadedAgentSkills) ? Promise.resolve([...options._preloadedAgentSkills]) : resolveConfiguredAgentSkills(options.agent?.skills, { rootDir });
3430
3449
  return configuredAgentSkills;
3431
3450
  }
3451
+ const source = {
3452
+ ...createFilesystemDocsMcpSource({
3453
+ rootDir,
3454
+ entry,
3455
+ contentDir,
3456
+ siteTitle: navTitle,
3457
+ baseUrl: options.baseUrl?.trim() || resolveDocsMetadataBaseUrl(options) || void 0,
3458
+ ordering: options.ordering
3459
+ }),
3460
+ async getSkills() {
3461
+ const configured = await getConfiguredAgentSkills();
3462
+ return [await resolveDocsPublishedAgentSkill({
3463
+ preferredDocument: readRootSkillDocument(),
3464
+ fallbackDocument: `---\nname: docs\ndescription: Use the project documentation through MCP resources and tools.\n---\n\n# Documentation\n`
3465
+ }), ...configured];
3466
+ }
3467
+ };
3468
+ const mcpApiReference = resolveApiReferenceConfig(options.apiReference).mcp;
3432
3469
  const handlers = createDocsMcpHttpHandler({
3433
- source: {
3434
- ...createFilesystemDocsMcpSource({
3435
- rootDir,
3436
- entry,
3437
- contentDir,
3438
- siteTitle: navTitle,
3439
- baseUrl: options.baseUrl?.trim() || resolveDocsMetadataBaseUrl(options) || void 0,
3440
- ordering: options.ordering
3441
- }),
3442
- async getSkills() {
3443
- const configured = await getConfiguredAgentSkills();
3444
- return [await resolveDocsPublishedAgentSkill({
3445
- preferredDocument: readRootSkillDocument(),
3446
- fallbackDocument: `---\nname: docs\ndescription: Use the project documentation through MCP resources and tools.\n---\n\n# Documentation\n`
3447
- }), ...configured];
3448
- }
3449
- },
3470
+ source,
3450
3471
  mcp: mcpConfig,
3472
+ okf: options.agent?.okf,
3473
+ openapi: mcpApiReference ? {
3474
+ config: mcpApiReference,
3475
+ document: () => buildApiReferenceOpenApiDocumentAsync(options, {
3476
+ framework: "next",
3477
+ rootDir,
3478
+ baseUrl: options.baseUrl?.trim() || resolveDocsMetadataBaseUrl(options) || void 0
3479
+ })
3480
+ } : void 0,
3451
3481
  contentChanges: options.agent?.contentChanges,
3452
3482
  evaluations: options.agent?.evaluations,
3483
+ feedback: options.feedback,
3453
3484
  contentChangeFeed,
3454
3485
  search: options.search,
3455
3486
  analytics: options.analytics,
@@ -708,6 +708,8 @@ function createDocsLayout(config, options) {
708
708
  const copyMarkdownEnabled = resolveBool(pageActions?.copyMarkdown);
709
709
  const copyMarkdownConfig = pageActions?.copyMarkdown && typeof pageActions.copyMarkdown === "object" ? pageActions.copyMarkdown : void 0;
710
710
  const openDocsEnabled = resolveBool(pageActions?.openDocs);
711
+ const connectMcp = resolveBool(pageActions?.connectMcp) ? pageActions?.connectMcp && typeof pageActions.connectMcp === "object" ? pageActions.connectMcp : {} : void 0;
712
+ const installSkills = resolveBool(pageActions?.installSkills) ? pageActions?.installSkills && typeof pageActions.installSkills === "object" ? pageActions.installSkills : {} : void 0;
711
713
  const pageActionsPosition = pageActions?.position ?? "below-title";
712
714
  const pageActionsAlignment = pageActions?.alignment ?? "left";
713
715
  const lastUpdatedRaw = config.lastUpdated;
@@ -857,6 +859,8 @@ function createDocsLayout(config, options) {
857
859
  openDocsProviders,
858
860
  openDocsTarget: openDocsConfig?.target,
859
861
  openDocsPrompt: openDocsConfig?.prompt,
862
+ connectMcp,
863
+ installSkills,
860
864
  pageActionsPosition,
861
865
  pageActionsAlignment,
862
866
  githubUrl,
@@ -1,5 +1,5 @@
1
1
  import { ReactNode } from "react";
2
- import { CopyMarkdownFormat, DocsFeedbackData, ReadingTimeFormat } from "@farming-labs/docs";
2
+ import { CopyMarkdownFormat, DocsFeedbackData, PageActionConnectMcpConfig, PageActionInstallSkillsConfig, ReadingTimeFormat } from "@farming-labs/docs";
3
3
  import * as react_jsx_runtime0 from "react/jsx-runtime";
4
4
 
5
5
  //#region src/docs-page-client.d.ts
@@ -37,6 +37,8 @@ interface DocsPageClientProps {
37
37
  openDocsProviders?: SerializedProvider[];
38
38
  openDocsTarget?: "markdown" | "page" | "source" | "github";
39
39
  openDocsPrompt?: string;
40
+ connectMcp?: PageActionConnectMcpConfig;
41
+ installSkills?: PageActionInstallSkillsConfig;
40
42
  /** Where to render page actions relative to the title */
41
43
  pageActionsPosition?: "above-title" | "below-title" | "toc";
42
44
  /** Horizontal alignment of page action buttons */
@@ -120,6 +122,8 @@ declare function DocsPageClient({
120
122
  openDocsProviders,
121
123
  openDocsTarget,
122
124
  openDocsPrompt,
125
+ connectMcp,
126
+ installSkills,
123
127
  pageActionsPosition,
124
128
  pageActionsAlignment,
125
129
  githubUrl,
@@ -255,7 +255,7 @@ function findThreadlineTocActionsContainer() {
255
255
  }
256
256
  return toc.parentElement ?? toc;
257
257
  }
258
- function DocsPageClient({ tocEnabled, showLlmsInHeader = false, tocStyle = "default", breadcrumbEnabled = true, changelogBasePath, entry = "docs", publicPath, locale, copyMarkdown = false, copyMarkdownFormat, copyMarkdownIncludeTitle, copyMarkdownLabel, copyMarkdownCopiedLabel, openDocs = false, openDocsProviders, openDocsTarget, openDocsPrompt, pageActionsPosition = "below-title", pageActionsAlignment = "left", githubUrl, contentDir, githubBranch = "main", githubDirectory, editOnGithubUrl, lastModifiedMap, lastModified: lastModifiedProp, readingTimeMap, readingTime: readingTimeProp, readingTimeFormat = "long", previousPage, nextPage, structuredDataMap, structuredData: structuredDataProp, readingTimeEnabled = false, lastUpdatedEnabled = true, lastUpdatedLabel = "Last updated", lastUpdatedPosition = "footer", llmsTxtEnabled = false, generatedTitleMap, descriptionMap, description, descriptionInBody = false, feedbackEnabled = false, feedbackQuestion, feedbackPlaceholder, feedbackRequireComment, feedbackPositiveLabel, feedbackNegativeLabel, feedbackSubmitLabel, feedbackSuccessMessage, feedbackErrorMessage, feedbackOnFeedback, analytics = false, children }) {
258
+ function DocsPageClient({ tocEnabled, showLlmsInHeader = false, tocStyle = "default", breadcrumbEnabled = true, changelogBasePath, entry = "docs", publicPath, locale, copyMarkdown = false, copyMarkdownFormat, copyMarkdownIncludeTitle, copyMarkdownLabel, copyMarkdownCopiedLabel, openDocs = false, openDocsProviders, openDocsTarget, openDocsPrompt, connectMcp, installSkills, pageActionsPosition = "below-title", pageActionsAlignment = "left", githubUrl, contentDir, githubBranch = "main", githubDirectory, editOnGithubUrl, lastModifiedMap, lastModified: lastModifiedProp, readingTimeMap, readingTime: readingTimeProp, readingTimeFormat = "long", previousPage, nextPage, structuredDataMap, structuredData: structuredDataProp, readingTimeEnabled = false, lastUpdatedEnabled = true, lastUpdatedLabel = "Last updated", lastUpdatedPosition = "footer", llmsTxtEnabled = false, generatedTitleMap, descriptionMap, description, descriptionInBody = false, feedbackEnabled = false, feedbackQuestion, feedbackPlaceholder, feedbackRequireComment, feedbackPositiveLabel, feedbackNegativeLabel, feedbackSubmitLabel, feedbackSuccessMessage, feedbackErrorMessage, feedbackOnFeedback, analytics = false, children }) {
259
259
  const fdTocStyle = tocStyle === "directional" ? "clerk" : void 0;
260
260
  const [toc, setToc] = useState([]);
261
261
  const [titlePortalHost, setTitlePortalHost] = useState(null);
@@ -487,6 +487,8 @@ function DocsPageClient({ tocEnabled, showLlmsInHeader = false, tocStyle = "defa
487
487
  providers: openDocsProviders,
488
488
  openDocsTarget,
489
489
  openDocsPrompt,
490
+ connectMcp,
491
+ installSkills,
490
492
  alignment: pageActionsAlignment,
491
493
  variant: "default",
492
494
  githubFileUrl,
@@ -571,6 +573,8 @@ function DocsPageClient({ tocEnabled, showLlmsInHeader = false, tocStyle = "defa
571
573
  providers: openDocsProviders,
572
574
  openDocsTarget,
573
575
  openDocsPrompt,
576
+ connectMcp,
577
+ installSkills,
574
578
  alignment: "left",
575
579
  variant: "rail",
576
580
  githubFileUrl,
@@ -628,6 +632,8 @@ function DocsPageClient({ tocEnabled, showLlmsInHeader = false, tocStyle = "defa
628
632
  providers: openDocsProviders,
629
633
  openDocsTarget,
630
634
  openDocsPrompt,
635
+ connectMcp,
636
+ installSkills,
631
637
  alignment: pageActionsAlignment,
632
638
  variant: "default",
633
639
  githubFileUrl,
@@ -1,4 +1,4 @@
1
- import { CopyMarkdownFormat } from "@farming-labs/docs";
1
+ import { CopyMarkdownFormat, PageActionMcpProvider } from "@farming-labs/docs";
2
2
  import * as react_jsx_runtime0 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/page-actions.d.ts
@@ -25,7 +25,18 @@ interface PageActionsProps {
25
25
  /** GitHub file URL (edit view) for the current page. Used when urlTemplate contains {githubUrl}. */
26
26
  githubFileUrl?: string | null;
27
27
  analytics?: boolean;
28
+ connectMcp?: {
29
+ endpoint?: string;
30
+ providers?: readonly PageActionMcpProvider[];
31
+ label?: string;
32
+ };
33
+ installSkills?: {
34
+ index?: string;
35
+ command?: string;
36
+ label?: string;
37
+ };
28
38
  }
39
+ declare function buildMcpSetupInstruction(provider: PageActionMcpProvider, endpoint: string): string;
29
40
  declare function formatCopyMarkdownContent(params: {
30
41
  content: string;
31
42
  format: CopyMarkdownFormat;
@@ -45,7 +56,9 @@ declare function PageActions({
45
56
  alignment,
46
57
  variant,
47
58
  githubFileUrl,
48
- analytics
59
+ analytics,
60
+ connectMcp,
61
+ installSkills
49
62
  }: PageActionsProps): react_jsx_runtime0.JSX.Element | null;
50
63
  //#endregion
51
- export { PageActions, formatCopyMarkdownContent };
64
+ export { PageActions, buildMcpSetupInstruction, formatCopyMarkdownContent };
@@ -8,6 +8,30 @@ import { usePathname } from "fumadocs-core/framework";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
9
9
 
10
10
  //#region src/page-actions.tsx
11
+ const DEFAULT_MCP_PROVIDERS = [
12
+ "copy",
13
+ "claude-code",
14
+ "cursor",
15
+ "vscode",
16
+ "codex"
17
+ ];
18
+ const MCP_PROVIDER_LABELS = {
19
+ copy: "Copy endpoint",
20
+ "claude-code": "Claude Code",
21
+ cursor: "Cursor",
22
+ vscode: "VS Code",
23
+ codex: "Codex"
24
+ };
25
+ function buildMcpSetupInstruction(provider, endpoint) {
26
+ if (provider === "copy") return endpoint;
27
+ if (provider === "claude-code") return `claude mcp add --transport http docs ${endpoint}`;
28
+ if (provider === "codex") return `codex mcp add docs --url ${endpoint}`;
29
+ if (provider === "vscode") return JSON.stringify({ servers: { docs: {
30
+ type: "http",
31
+ url: endpoint
32
+ } } }, null, 2);
33
+ return JSON.stringify({ mcpServers: { docs: { url: endpoint } } }, null, 2);
34
+ }
11
35
  const CopyIcon = () => /* @__PURE__ */ jsxs("svg", {
12
36
  width: "14",
13
37
  height: "14",
@@ -146,14 +170,46 @@ function currentPageTitle() {
146
170
  const title = document.querySelector("#nd-page h1") ?? document.querySelector("article h1") ?? document.querySelector("h1");
147
171
  return title?.innerText?.trim() || title?.textContent?.trim() || "";
148
172
  }
149
- function PageActions({ copyMarkdown, copyMarkdownFormat = "markdown", copyMarkdownIncludeTitle = false, copyMarkdownLabel = "Copy page", copyMarkdownCopiedLabel = "Copied!", openDocs, providers, openDocsTarget = DEFAULT_OPEN_DOCS_TARGET, openDocsPrompt = DEFAULT_OPEN_DOCS_PROMPT, alignment = "left", variant = "default", githubFileUrl, analytics = false }) {
173
+ function PageActions({ copyMarkdown, copyMarkdownFormat = "markdown", copyMarkdownIncludeTitle = false, copyMarkdownLabel = "Copy page", copyMarkdownCopiedLabel = "Copied!", openDocs, providers, openDocsTarget = DEFAULT_OPEN_DOCS_TARGET, openDocsPrompt = DEFAULT_OPEN_DOCS_PROMPT, alignment = "left", variant = "default", githubFileUrl, analytics = false, connectMcp, installSkills }) {
150
174
  const [copied, setCopied] = useState(false);
151
175
  const [dropdownOpen, setDropdownOpen] = useState(false);
176
+ const [mcpOpen, setMcpOpen] = useState(false);
177
+ const [skillsOpen, setSkillsOpen] = useState(false);
178
+ const [copiedSetup, setCopiedSetup] = useState();
179
+ const [publishedSkills, setPublishedSkills] = useState([]);
152
180
  const dropdownRef = useRef(null);
153
181
  const pathname = usePathname();
154
182
  const resolvedProviders = providers ?? DEFAULT_PROVIDERS;
155
183
  const cleanedPathname = pathname.replace(/\/+$/, "") || "/";
156
184
  const markdownHref = cleanedPathname === "/" ? "/index.md" : `${cleanedPathname.replace(/\.md$/, "")}.md`;
185
+ const resolveAbsoluteUrl = useCallback((value) => {
186
+ try {
187
+ return new URL(value, window.location.origin).toString();
188
+ } catch {
189
+ return value;
190
+ }
191
+ }, []);
192
+ const copySetup = useCallback(async (key, value) => {
193
+ await navigator.clipboard.writeText(value);
194
+ setCopiedSetup(key);
195
+ setTimeout(() => setCopiedSetup(void 0), 2e3);
196
+ }, []);
197
+ const openSkills = useCallback(async () => {
198
+ setSkillsOpen(true);
199
+ if (publishedSkills.length > 0) return;
200
+ try {
201
+ const response = await fetch(resolveAbsoluteUrl(installSkills?.index ?? "/.well-known/agent-skills/index.json"), { headers: { Accept: "application/json" } });
202
+ if (!response.ok) return;
203
+ setPublishedSkills(((await response.json()).skills ?? []).flatMap((skill) => typeof skill.name === "string" ? [{
204
+ name: skill.name,
205
+ ...typeof skill.description === "string" ? { description: skill.description } : {}
206
+ }] : []));
207
+ } catch {}
208
+ }, [
209
+ installSkills?.index,
210
+ publishedSkills.length,
211
+ resolveAbsoluteUrl
212
+ ]);
157
213
  const handleCopyMarkdown = useCallback(async () => {
158
214
  try {
159
215
  let content = "";
@@ -249,9 +305,11 @@ function PageActions({ copyMarkdown, copyMarkdownFormat = "markdown", copyMarkdo
249
305
  }, [dropdownOpen]);
250
306
  useEffect(() => {
251
307
  setDropdownOpen(false);
308
+ setMcpOpen(false);
309
+ setSkillsOpen(false);
252
310
  setCopied(false);
253
311
  }, [pathname]);
254
- if (variant !== "rail" && !copyMarkdown && !openDocs) return null;
312
+ if (variant !== "rail" && !copyMarkdown && !openDocs && !connectMcp && !installSkills) return null;
255
313
  if (variant === "rail") return /* @__PURE__ */ jsxs("div", {
256
314
  className: "fd-page-actions fd-page-actions-rail",
257
315
  "data-page-actions": true,
@@ -285,6 +343,18 @@ function PageActions({ copyMarkdown, copyMarkdownFormat = "markdown", copyMarkdo
285
343
  onClick: handleAskAI,
286
344
  className: "fd-page-action-btn",
287
345
  children: [/* @__PURE__ */ jsx(SparklesIcon, {}), /* @__PURE__ */ jsx("span", { children: "Ask AI" })]
346
+ }),
347
+ connectMcp && /* @__PURE__ */ jsx("button", {
348
+ type: "button",
349
+ onClick: () => copySetup("mcp:rail", resolveAbsoluteUrl(connectMcp.endpoint ?? "/mcp")),
350
+ className: "fd-page-action-btn",
351
+ children: /* @__PURE__ */ jsx("span", { children: copiedSetup === "mcp:rail" ? "Copied endpoint" : connectMcp.label ?? "Connect MCP" })
352
+ }),
353
+ installSkills && /* @__PURE__ */ jsx("button", {
354
+ type: "button",
355
+ onClick: () => copySetup("skills:rail", installSkills.command ?? `npx skills add ${window.location.origin}`),
356
+ className: "fd-page-action-btn",
357
+ children: /* @__PURE__ */ jsx("span", { children: copiedSetup === "skills:rail" ? "Copied command" : installSkills.label ?? "Install skills" })
288
358
  })
289
359
  ]
290
360
  });
@@ -292,60 +362,124 @@ function PageActions({ copyMarkdown, copyMarkdownFormat = "markdown", copyMarkdo
292
362
  className: "fd-page-actions",
293
363
  "data-page-actions": true,
294
364
  "data-actions-alignment": alignment,
295
- children: [copyMarkdown && /* @__PURE__ */ jsxs("button", {
296
- type: "button",
297
- onClick: handleCopyMarkdown,
298
- className: "fd-page-action-btn",
299
- "data-copied": copied,
300
- "data-copy-markdown-format": copyMarkdownFormat,
301
- "data-copy-markdown-include-title": copyMarkdownIncludeTitle || void 0,
302
- children: [copied ? /* @__PURE__ */ jsx(CheckIcon, {}) : /* @__PURE__ */ jsx(CopyIcon, {}), /* @__PURE__ */ jsx("span", { children: copied ? copyMarkdownCopiedLabel : copyMarkdownLabel })]
303
- }), openDocs && resolvedProviders.length > 0 && /* @__PURE__ */ jsxs("div", {
304
- ref: dropdownRef,
305
- className: "fd-page-action-dropdown",
306
- children: [/* @__PURE__ */ jsxs("button", {
365
+ children: [
366
+ copyMarkdown && /* @__PURE__ */ jsxs("button", {
307
367
  type: "button",
308
- onClick: () => {
309
- const next = !dropdownOpen;
310
- setDropdownOpen(next);
311
- if (analytics && next) emitClientAnalyticsEvent({
312
- type: "page_action_open_docs_menu",
313
- properties: {
314
- providerCount: resolvedProviders.length,
315
- pathname
316
- }
317
- });
318
- },
368
+ onClick: handleCopyMarkdown,
319
369
  className: "fd-page-action-btn",
320
- "aria-expanded": dropdownOpen,
321
- children: [/* @__PURE__ */ jsx("span", { children: "Open in" }), /* @__PURE__ */ jsx(ChevronDownIcon, {})]
322
- }), dropdownOpen && /* @__PURE__ */ jsx("div", {
323
- className: "fd-page-action-menu",
324
- role: "menu",
325
- children: resolvedProviders.map((provider) => {
326
- const iconHtml = sanitizeIconHtml(provider.iconHtml ?? resolveOpenDocsProviderIcon(provider.name));
327
- return /* @__PURE__ */ jsxs("button", {
370
+ "data-copied": copied,
371
+ "data-copy-markdown-format": copyMarkdownFormat,
372
+ "data-copy-markdown-include-title": copyMarkdownIncludeTitle || void 0,
373
+ children: [copied ? /* @__PURE__ */ jsx(CheckIcon, {}) : /* @__PURE__ */ jsx(CopyIcon, {}), /* @__PURE__ */ jsx("span", { children: copied ? copyMarkdownCopiedLabel : copyMarkdownLabel })]
374
+ }),
375
+ openDocs && resolvedProviders.length > 0 && /* @__PURE__ */ jsxs("div", {
376
+ ref: dropdownRef,
377
+ className: "fd-page-action-dropdown",
378
+ children: [/* @__PURE__ */ jsxs("button", {
379
+ type: "button",
380
+ onClick: () => {
381
+ const next = !dropdownOpen;
382
+ setDropdownOpen(next);
383
+ if (analytics && next) emitClientAnalyticsEvent({
384
+ type: "page_action_open_docs_menu",
385
+ properties: {
386
+ providerCount: resolvedProviders.length,
387
+ pathname
388
+ }
389
+ });
390
+ },
391
+ className: "fd-page-action-btn",
392
+ "aria-expanded": dropdownOpen,
393
+ children: [/* @__PURE__ */ jsx("span", { children: "Open in" }), /* @__PURE__ */ jsx(ChevronDownIcon, {})]
394
+ }), dropdownOpen && /* @__PURE__ */ jsx("div", {
395
+ className: "fd-page-action-menu",
396
+ role: "menu",
397
+ children: resolvedProviders.map((provider) => {
398
+ const iconHtml = sanitizeIconHtml(provider.iconHtml ?? resolveOpenDocsProviderIcon(provider.name));
399
+ return /* @__PURE__ */ jsxs("button", {
400
+ type: "button",
401
+ role: "menuitem",
402
+ className: "fd-page-action-menu-item",
403
+ onClick: () => handleOpen(provider),
404
+ children: [
405
+ iconHtml && /* @__PURE__ */ jsx("span", {
406
+ className: "fd-page-action-menu-icon",
407
+ dangerouslySetInnerHTML: { __html: iconHtml }
408
+ }),
409
+ /* @__PURE__ */ jsxs("span", {
410
+ className: "fd-page-action-menu-label",
411
+ children: ["Open in ", provider.name]
412
+ }),
413
+ /* @__PURE__ */ jsx(ExternalLinkIcon, {})
414
+ ]
415
+ }, provider.name);
416
+ })
417
+ })]
418
+ }),
419
+ connectMcp && /* @__PURE__ */ jsxs("div", {
420
+ className: "fd-page-action-dropdown",
421
+ children: [/* @__PURE__ */ jsxs("button", {
422
+ type: "button",
423
+ className: "fd-page-action-btn",
424
+ "aria-expanded": mcpOpen,
425
+ onClick: () => setMcpOpen((open) => !open),
426
+ children: [/* @__PURE__ */ jsx("span", { children: connectMcp.label ?? "Connect MCP" }), /* @__PURE__ */ jsx(ChevronDownIcon, {})]
427
+ }), mcpOpen && /* @__PURE__ */ jsx("div", {
428
+ className: "fd-page-action-menu",
429
+ role: "menu",
430
+ children: (connectMcp.providers ?? DEFAULT_MCP_PROVIDERS).map((provider) => {
431
+ const endpoint = resolveAbsoluteUrl(connectMcp.endpoint ?? "/mcp");
432
+ const key = `mcp:${provider}`;
433
+ return /* @__PURE__ */ jsxs("button", {
434
+ type: "button",
435
+ role: "menuitem",
436
+ className: "fd-page-action-menu-item",
437
+ onClick: async () => {
438
+ await copySetup(key, buildMcpSetupInstruction(provider, endpoint));
439
+ setMcpOpen(false);
440
+ },
441
+ children: [/* @__PURE__ */ jsx("span", {
442
+ className: "fd-page-action-menu-label",
443
+ children: copiedSetup === key ? "Copied" : MCP_PROVIDER_LABELS[provider]
444
+ }), copiedSetup === key ? /* @__PURE__ */ jsx(CheckIcon, {}) : /* @__PURE__ */ jsx(CopyIcon, {})]
445
+ }, provider);
446
+ })
447
+ })]
448
+ }),
449
+ installSkills && /* @__PURE__ */ jsxs("div", {
450
+ className: "fd-page-action-dropdown",
451
+ children: [/* @__PURE__ */ jsx("button", {
452
+ type: "button",
453
+ className: "fd-page-action-btn",
454
+ onClick: openSkills,
455
+ children: /* @__PURE__ */ jsx("span", { children: installSkills.label ?? "Install skills" })
456
+ }), skillsOpen && /* @__PURE__ */ jsxs("div", {
457
+ className: "fd-page-action-menu",
458
+ role: "dialog",
459
+ "aria-label": "Install Agent Skills",
460
+ children: [publishedSkills.map((skill) => /* @__PURE__ */ jsx("div", {
461
+ className: "fd-page-action-menu-item",
462
+ children: /* @__PURE__ */ jsx("span", {
463
+ className: "fd-page-action-menu-label",
464
+ children: skill.name
465
+ })
466
+ }, skill.name)), /* @__PURE__ */ jsxs("button", {
328
467
  type: "button",
329
- role: "menuitem",
330
468
  className: "fd-page-action-menu-item",
331
- onClick: () => handleOpen(provider),
332
- children: [
333
- iconHtml && /* @__PURE__ */ jsx("span", {
334
- className: "fd-page-action-menu-icon",
335
- dangerouslySetInnerHTML: { __html: iconHtml }
336
- }),
337
- /* @__PURE__ */ jsxs("span", {
338
- className: "fd-page-action-menu-label",
339
- children: ["Open in ", provider.name]
340
- }),
341
- /* @__PURE__ */ jsx(ExternalLinkIcon, {})
342
- ]
343
- }, provider.name);
344
- })
345
- })]
346
- })]
469
+ onClick: async () => {
470
+ await copySetup("skills", installSkills.command ?? `npx skills add ${window.location.origin}`);
471
+ setSkillsOpen(false);
472
+ },
473
+ children: [/* @__PURE__ */ jsx("span", {
474
+ className: "fd-page-action-menu-label",
475
+ children: copiedSetup === "skills" ? "Copied install command" : "Copy install command"
476
+ }), copiedSetup === "skills" ? /* @__PURE__ */ jsx(CheckIcon, {}) : /* @__PURE__ */ jsx(CopyIcon, {})]
477
+ })]
478
+ })]
479
+ })
480
+ ]
347
481
  });
348
482
  }
349
483
 
350
484
  //#endregion
351
- export { PageActions, formatCopyMarkdownContent };
485
+ export { PageActions, buildMcpSetupInstruction, formatCopyMarkdownContent };
@@ -249,6 +249,8 @@ function TanstackDocsLayout({ config, tree, browserRuntime = false, locale, desc
249
249
  const copyMarkdownEnabled = resolveBool(pageActions?.copyMarkdown);
250
250
  const copyMarkdownConfig = pageActions?.copyMarkdown && typeof pageActions.copyMarkdown === "object" ? pageActions.copyMarkdown : void 0;
251
251
  const openDocsEnabled = resolveBool(pageActions?.openDocs);
252
+ const connectMcp = resolveBool(pageActions?.connectMcp) ? pageActions?.connectMcp && typeof pageActions.connectMcp === "object" ? pageActions.connectMcp : {} : void 0;
253
+ const installSkills = resolveBool(pageActions?.installSkills) ? pageActions?.installSkills && typeof pageActions.installSkills === "object" ? pageActions.installSkills : {} : void 0;
252
254
  const pageActionsPosition = pageActions?.position ?? "below-title";
253
255
  const pageActionsAlignment = pageActions?.alignment ?? "left";
254
256
  const lastUpdatedRaw = config.lastUpdated;
@@ -377,6 +379,8 @@ function TanstackDocsLayout({ config, tree, browserRuntime = false, locale, desc
377
379
  openDocsProviders,
378
380
  openDocsTarget: openDocsConfig?.target,
379
381
  openDocsPrompt: openDocsConfig?.prompt,
382
+ connectMcp,
383
+ installSkills,
380
384
  pageActionsPosition,
381
385
  pageActionsAlignment,
382
386
  editOnGithubUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/theme",
3
- "version": "0.2.103",
3
+ "version": "0.2.105",
4
4
  "description": "Theme package for @farming-labs/docs — layout, provider, MDX components, and styles",
5
5
  "keywords": [
6
6
  "docs",
@@ -161,7 +161,7 @@
161
161
  "tsdown": "^0.20.3",
162
162
  "typescript": "^5.9.3",
163
163
  "vitest": "^4.1.8",
164
- "@farming-labs/docs": "0.2.103"
164
+ "@farming-labs/docs": "0.2.105"
165
165
  },
166
166
  "peerDependencies": {
167
167
  "@farming-labs/docs": ">=0.0.1",