@farming-labs/theme 0.2.103 → 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.
@@ -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, 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
  /**
@@ -1037,6 +1037,7 @@ function scanDocsDir(docsDir, entry, locale, excludedDirs = [], publicPath = `/$
1037
1037
  description,
1038
1038
  relatedInput: data.related,
1039
1039
  agent: normalizePageAgentFrontmatter(data.agent),
1040
+ okf: normalizeDocsOkfTrustMetadataInput(data.okf),
1040
1041
  content,
1041
1042
  rawContent,
1042
1043
  agentFallbackRawContent: agentRawContent !== rawContent ? agentRawContent : void 0,
@@ -1105,6 +1106,7 @@ function scanChangelogDir(changelogDir, entryPath, changelogPath, locale, public
1105
1106
  description,
1106
1107
  relatedInput: data.related,
1107
1108
  agent: normalizePageAgentFrontmatter(data.agent),
1109
+ okf: normalizeDocsOkfTrustMetadataInput(data.okf),
1108
1110
  content,
1109
1111
  rawContent,
1110
1112
  agentFallbackRawContent: agentRawContent !== rawContent ? agentRawContent : void 0,
@@ -1321,7 +1323,8 @@ function renderMarkdownDocument(page, options = {}) {
1321
1323
  return renderDocsMarkdownDocument(page, {
1322
1324
  llms: options.llmsEnabled !== false,
1323
1325
  origin: options.origin,
1324
- sitemap: options.sitemap
1326
+ sitemap: options.sitemap,
1327
+ okf: options.okf
1325
1328
  });
1326
1329
  }
1327
1330
  function renderSkillDocument({ origin, entry, apiRoute, apiCatalog, search, contentChanges, mcp, feedback, llms, sitemap, robots, openapi }) {
@@ -2771,7 +2774,8 @@ function createDocsAPI(options) {
2771
2774
  document: renderMarkdownDocument(withPublicDocsUrl(page, ctx), {
2772
2775
  llmsEnabled: llmsConfig.enabled,
2773
2776
  origin,
2774
- sitemap: sitemapConfig
2777
+ sitemap: sitemapConfig,
2778
+ okf: options?.agent?.okf
2775
2779
  }),
2776
2780
  lastModified: resolveDocsRetrievalLastModified(page, "agent")
2777
2781
  };
@@ -2784,7 +2788,8 @@ function createDocsAPI(options) {
2784
2788
  document: renderMarkdownDocument(withPublicDocsUrl(fallbackPage, ctx), {
2785
2789
  llmsEnabled: llmsConfig.enabled,
2786
2790
  origin,
2787
- sitemap: sitemapConfig
2791
+ sitemap: sitemapConfig,
2792
+ okf: options?.agent?.okf
2788
2793
  }),
2789
2794
  lastModified: resolveDocsRetrievalLastModified(fallbackPage, "agent")
2790
2795
  };
@@ -2793,7 +2798,8 @@ function createDocsAPI(options) {
2793
2798
  document: renderMarkdownDocument(withPublicDocsUrl(page, ctx), {
2794
2799
  llmsEnabled: llmsConfig.enabled,
2795
2800
  origin,
2796
- sitemap: sitemapConfig
2801
+ sitemap: sitemapConfig,
2802
+ okf: options?.agent?.okf
2797
2803
  }),
2798
2804
  lastModified: resolveDocsRetrievalLastModified(page, "agent")
2799
2805
  };
@@ -3429,27 +3435,39 @@ function createDocsMCPAPI(options = {}) {
3429
3435
  if (!configuredAgentSkills) configuredAgentSkills = Array.isArray(options._preloadedAgentSkills) ? Promise.resolve([...options._preloadedAgentSkills]) : resolveConfiguredAgentSkills(options.agent?.skills, { rootDir });
3430
3436
  return configuredAgentSkills;
3431
3437
  }
3438
+ const source = {
3439
+ ...createFilesystemDocsMcpSource({
3440
+ rootDir,
3441
+ entry,
3442
+ contentDir,
3443
+ siteTitle: navTitle,
3444
+ baseUrl: options.baseUrl?.trim() || resolveDocsMetadataBaseUrl(options) || void 0,
3445
+ ordering: options.ordering
3446
+ }),
3447
+ async getSkills() {
3448
+ const configured = await getConfiguredAgentSkills();
3449
+ return [await resolveDocsPublishedAgentSkill({
3450
+ preferredDocument: readRootSkillDocument(),
3451
+ fallbackDocument: `---\nname: docs\ndescription: Use the project documentation through MCP resources and tools.\n---\n\n# Documentation\n`
3452
+ }), ...configured];
3453
+ }
3454
+ };
3455
+ const mcpApiReference = resolveApiReferenceConfig(options.apiReference).mcp;
3432
3456
  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
- },
3457
+ source,
3450
3458
  mcp: mcpConfig,
3459
+ okf: options.agent?.okf,
3460
+ openapi: mcpApiReference ? {
3461
+ config: mcpApiReference,
3462
+ document: () => buildApiReferenceOpenApiDocumentAsync(options, {
3463
+ framework: "next",
3464
+ rootDir,
3465
+ baseUrl: options.baseUrl?.trim() || resolveDocsMetadataBaseUrl(options) || void 0
3466
+ })
3467
+ } : void 0,
3451
3468
  contentChanges: options.agent?.contentChanges,
3452
3469
  evaluations: options.agent?.evaluations,
3470
+ feedback: options.feedback,
3453
3471
  contentChangeFeed,
3454
3472
  search: options.search,
3455
3473
  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.104",
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.104"
165
165
  },
166
166
  "peerDependencies": {
167
167
  "@farming-labs/docs": ">=0.0.1",