@farming-labs/theme 0.2.60 → 0.2.62

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.
@@ -78,6 +78,8 @@ interface DocsAPIOptions {
78
78
  apiReference?: DocsConfig["apiReference"];
79
79
  /** Metadata used in generated OpenAPI documents. */
80
80
  metadata?: DocsConfig["metadata"];
81
+ /** Reusable skill publication and optional real A2A service metadata. */
82
+ agent?: DocsConfig["agent"];
81
83
  }
82
84
  interface DocsMCPAPIOptions {
83
85
  rootDir?: string;
@@ -92,6 +94,7 @@ interface DocsMCPAPIOptions {
92
94
  analytics?: boolean | DocsAnalyticsConfig;
93
95
  telemetry?: boolean | DocsTelemetryConfig;
94
96
  observability?: boolean | DocsObservabilityConfig;
97
+ agent?: DocsConfig["agent"];
95
98
  }
96
99
  type LlmsTxtOptions = LlmsTxtConfig;
97
100
  /**
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_AGENT_SKILLS_INDEX_ROUTE, DEFAULT_AGENT_SKILLS_ROUTE_PREFIX, DEFAULT_API_CATALOG_ROUTE, DEFAULT_SITEMAP_MD_DOCS_ROUTE, acceptsDocsMarkdown, buildDocsAskAIContext, buildDocsConfigMap, buildDocsDiagnostics, createDocsAgentTraceContext, createDocsAgentTraceId, createDocsMarkdownResponse, createDocsRobotsResponse, createDocsSitemapResponse, createDocsStandardsDiscoveryResponse, detectDocsMarkdownAgentRequest, emitDocsAgentTraceEvent, emitDocsAnalyticsEvent, emitDocsTelemetryAgentSurfaceEvent, emitDocsTelemetryProjectEvent, formatDocsAskAIPackageHints, getDocsDiscoveryLinkHeader, getDocsLlmsTxtMaxCharsIssue, hasDocsMarkdownSignatureAgent, inferDocsTelemetryAgentSurface, isDocsConfigRequest, isDocsDiagnosticsRequest, normalizeDocsRelated, normalizePageAgentFrontmatter, performDocsSearch, renderDocsLlmsTxt, renderDocsMarkdownDocument, resolveAskAISearchRequestConfig, resolveChangelogConfig, resolveDocsAgentContractMcpTools, resolveDocsAudienceMdxContent, resolveDocsI18n, resolveDocsLlmsTxtRequest, resolveDocsLlmsTxtSections, resolveDocsLocale, resolveDocsMetadataBaseUrl, resolveDocsRequestApiRoute, resolveDocsSitemapConfig, resolveDocsStandardsDiscoveryRequest, resolvePageSidebarFolderIndexBehavior, resolveSearchRequestConfig, selectDocsLlmsTxtContent, stripGeneratedAgentProvenance } from "@farming-labs/docs";
8
- import { buildApiReferenceOpenApiDocumentAsync, createDocsMcpHttpHandler, createFilesystemDocsMcpSource, readDocsSitemapManifest, resolveApiReferenceConfig, 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, acceptsDocsMarkdown, buildDocsAskAIContext, buildDocsConfigMap, buildDocsDiagnostics, createDocsAgentTraceContext, createDocsAgentTraceId, createDocsMarkdownResponse, createDocsRobotsResponse, createDocsSitemapResponse, createDocsStandardsDiscoveryResponse, detectDocsMarkdownAgentRequest, emitDocsAgentTraceEvent, emitDocsAnalyticsEvent, emitDocsTelemetryAgentSurfaceEvent, emitDocsTelemetryProjectEvent, formatDocsAskAIPackageHints, getDocsDiscoveryLinkHeader, getDocsLlmsTxtMaxCharsIssue, getDocsMcpProtectedResourceMetadataRoutes, hasDocsMarkdownSignatureAgent, inferDocsTelemetryAgentSurface, isDocsConfigRequest, isDocsDiagnosticsRequest, normalizeDocsRelated, normalizePageAgentFrontmatter, performDocsSearch, renderDocsLlmsTxt, renderDocsMarkdownDocument, resolveAskAISearchRequestConfig, resolveChangelogConfig, resolveDocsAgentContractMcpTools, resolveDocsAudienceMdxContent, resolveDocsI18n, resolveDocsLlmsTxtRequest, resolveDocsLlmsTxtSections, resolveDocsLocale, resolveDocsMetadataBaseUrl, resolveDocsPublishedAgentSkill, resolveDocsRequestApiRoute, resolveDocsSearchAudience, resolveDocsSitemapConfig, resolveDocsStandardsDiscoveryRequest, resolvePageSidebarFolderIndexBehavior, resolveSearchRequestConfig, selectDocsLlmsTxtContent, stripGeneratedAgentProvenance } from "@farming-labs/docs";
8
+ import { buildApiReferenceOpenApiDocumentAsync, createDocsMcpHttpHandler, createFilesystemDocsMcpSource, readDocsSitemapManifest, resolveApiReferenceConfig, resolveConfiguredAgentSkills, resolveDocsMcpConfig } from "@farming-labs/docs/server";
9
9
 
10
10
  //#region src/docs-api.ts
11
11
  /**
@@ -42,7 +42,6 @@ const DEFAULT_DOCS_CLOUD_API_KEY_ENV = "DOCS_CLOUD_API_KEY";
42
42
  const DEFAULT_AGENT_SPEC_ROUTE = "/api/docs/agent/spec";
43
43
  const DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE = "/.well-known/agent";
44
44
  const DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE = "/.well-known/agent.json";
45
- const DEFAULT_MCP_ROUTE = "/api/docs/mcp";
46
45
  const DEFAULT_MCP_PUBLIC_ROUTE = "/mcp";
47
46
  const DEFAULT_MCP_WELL_KNOWN_ROUTE = "/.well-known/mcp";
48
47
  const DEFAULT_LLMS_TXT_ROUTE = "/llms.txt";
@@ -210,7 +209,7 @@ function resolveApiReferenceOpenApiDiscovery(value) {
210
209
  apiReferencePath: `/${apiReference.path}`
211
210
  };
212
211
  }
213
- function buildAgentSpec({ origin, entry, apiRoute, apiCatalog, i18n, search, mcp, feedback, llms, sitemap, robots, openapi }) {
212
+ function buildAgentSpec({ origin, entry, apiRoute, apiCatalog, i18n, search, mcp, feedback, llms, sitemap, robots, openapi, publishedSkills = [], agentCard }) {
214
213
  const normalizedEntry = normalizePathSegment(entry) || "docs";
215
214
  const localesEnabled = i18n !== null;
216
215
  const searchEnabled = isSearchEnabled(search);
@@ -218,6 +217,8 @@ function buildAgentSpec({ origin, entry, apiRoute, apiCatalog, i18n, search, mcp
218
217
  const robotsEnabled = isRobotsDiscoveryEnabled(robots);
219
218
  const llmsSections = resolveDocsLlmsTxtSections(llms);
220
219
  const agentContractMcpTools = resolveDocsAgentContractMcpTools(mcp);
220
+ const protectedResource = mcp.enabled && mcp.security?.authenticate ? mcp.security.protectedResource : void 0;
221
+ const protectedResourceMetadataRoutes = protectedResource ? getDocsMcpProtectedResourceMetadataRoutes(mcp.route) : [];
221
222
  const agentSpecRoute = apiRoute === DEFAULT_DOCS_API_ROUTE ? DEFAULT_AGENT_SPEC_ROUTE : `${apiRoute}?agent=spec`;
222
223
  return {
223
224
  version: "1",
@@ -272,7 +273,9 @@ function buildAgentSpec({ origin, entry, apiRoute, apiCatalog, i18n, search, mcp
272
273
  apiCatalog: DEFAULT_API_CATALOG_ROUTE,
273
274
  apiCatalogQuery: `${apiRoute}?format=api-catalog`
274
275
  } : {},
275
- agentSkillsIndex: DEFAULT_AGENT_SKILLS_INDEX_ROUTE
276
+ agentSkillsIndex: DEFAULT_AGENT_SKILLS_INDEX_ROUTE,
277
+ legacySkillsIndex: DEFAULT_LEGACY_SKILLS_INDEX_ROUTE,
278
+ ...agentCard ? { agentCard: DEFAULT_A2A_AGENT_CARD_ROUTE } : {}
276
279
  },
277
280
  apiCatalog: {
278
281
  enabled: apiCatalog,
@@ -390,9 +393,13 @@ function buildAgentSpec({ origin, entry, apiRoute, apiCatalog, i18n, search, mcp
390
393
  search: {
391
394
  enabled: searchEnabled,
392
395
  endpoint: `${apiRoute}?query={query}`,
396
+ agentEndpoint: `${apiRoute}?query={query}&audience=agent`,
393
397
  method: "GET",
394
398
  queryParam: "query",
395
- localeParam: "lang"
399
+ localeParam: "lang",
400
+ audienceParam: "audience",
401
+ defaultAudience: "human",
402
+ supportedAudiences: ["human", "agent"]
396
403
  },
397
404
  agents: {
398
405
  enabled: true,
@@ -414,10 +421,26 @@ function buildAgentSpec({ origin, entry, apiRoute, apiCatalog, i18n, search, mcp
414
421
  schema: "https://schemas.agentskills.io/discovery/0.2.0/schema.json",
415
422
  index: DEFAULT_AGENT_SKILLS_INDEX_ROUTE,
416
423
  artifact: `${DEFAULT_AGENT_SKILLS_ROUTE_PREFIX}/{name}/SKILL.md`,
424
+ archive: DEFAULT_AGENT_SKILLS_ARCHIVE_ROUTE_PATTERN,
425
+ file: `${DEFAULT_AGENT_SKILLS_ROUTE_PREFIX}/{name}/{path}`,
426
+ legacyIndex: DEFAULT_LEGACY_SKILLS_INDEX_ROUTE,
417
427
  apiIndex: `${apiRoute}?format=agent-skills`,
418
428
  apiArtifact: `${apiRoute}?format=agent-skill&name={name}`,
429
+ apiFile: `${apiRoute}?format=agent-skill-file&name={name}&path={path}`,
419
430
  digest: "sha256"
420
431
  },
432
+ published: publishedSkills.map((skill) => ({
433
+ name: skill.name,
434
+ description: skill.description,
435
+ type: skill.type,
436
+ url: skill.url,
437
+ digest: skill.digest,
438
+ files: skill.files.map((file) => ({
439
+ path: file.path,
440
+ url: file.url,
441
+ digest: file.digest
442
+ }))
443
+ })),
421
444
  registry: "skills.sh",
422
445
  install: "npx skills add farming-labs/docs",
423
446
  recommended: [{
@@ -432,10 +455,16 @@ function buildAgentSpec({ origin, entry, apiRoute, apiCatalog, i18n, search, mcp
432
455
  publicEndpoint: DEFAULT_MCP_PUBLIC_ROUTE,
433
456
  wellKnownEndpoint: DEFAULT_MCP_WELL_KNOWN_ROUTE,
434
457
  publicEndpoints: [DEFAULT_MCP_PUBLIC_ROUTE, DEFAULT_MCP_WELL_KNOWN_ROUTE],
435
- canonicalEndpoint: DEFAULT_MCP_ROUTE,
458
+ canonicalEndpoint: mcp.route,
436
459
  name: mcp.name,
437
460
  version: mcp.version,
438
- tools: mcp.tools
461
+ tools: mcp.tools,
462
+ ...protectedResource ? { protectedResource: {
463
+ metadataEndpoints: protectedResourceMetadataRoutes,
464
+ authorizationServers: protectedResource.authorizationServers,
465
+ scopesSupported: protectedResource.scopesSupported,
466
+ requiredScopes: protectedResource.requiredScopes
467
+ } } : {}
439
468
  },
440
469
  feedback: {
441
470
  enabled: feedback.enabled,
@@ -1253,7 +1282,7 @@ function renderSkillDocument({ origin, entry, apiRoute, apiCatalog, search, mcp,
1253
1282
  lines.push("", "## When To Use", "Use this skill when you need to read or implement against this documentation site.", "", "## Start Here", `- Fetch ${DEFAULT_AGENT_SPEC_WELL_KNOWN_JSON_ROUTE}; fall back to ${DEFAULT_AGENT_SPEC_WELL_KNOWN_ROUTE} or ${agentSpecRoute}.`);
1254
1283
  if (apiCatalog) lines.push(`- Fetch ${DEFAULT_API_CATALOG_ROUTE} for the standards-based API catalog.`);
1255
1284
  lines.push(`- Fetch ${DEFAULT_AGENT_SKILLS_INDEX_ROUTE} to discover the published site skill and verify its SHA-256 digest.`, `- Fetch /${normalizedEntry}.md for the root docs page.`, `- Fetch /${normalizedEntry}/{slug}.md for page-specific context.`, "- You can also request text/markdown from normal page URLs.");
1256
- if (searchEnabled) lines.push(`- Search with ${apiRoute}?query={query} when you do not know the page.`);
1285
+ if (searchEnabled) lines.push(`- Search with ${apiRoute}?query={query}&audience=agent when you do not know the page.`);
1257
1286
  if (openapi.enabled && openapiUrl) lines.push(`- Fetch ${openapiUrl} for the machine-readable OpenAPI schema before scraping API reference pages.`);
1258
1287
  if (llms.enabled) {
1259
1288
  lines.push(`- Use ${DEFAULT_LLMS_TXT_ROUTE} for a compact docs index.`, `- Use ${DEFAULT_LLMS_FULL_TXT_ROUTE} for full markdown context.`);
@@ -1318,7 +1347,7 @@ function renderAgentsDocument({ origin, entry, apiRoute, apiCatalog, search, mcp
1318
1347
  if (sitemapConfig.xml.enabled) lines.push(`- Use ${sitemapConfig.xml.route} for canonical URLs and freshness metadata.`);
1319
1348
  }
1320
1349
  if (robotsEnabled) lines.push(`- Check ${DEFAULT_ROBOTS_TXT_ROUTE} before crawling broadly.`);
1321
- if (searchEnabled) lines.push(`- Search with ${apiRoute}?query={query} when the route is unknown.`);
1350
+ if (searchEnabled) lines.push(`- Search with ${apiRoute}?query={query}&audience=agent when the route is unknown.`);
1322
1351
  if (openapi.enabled && openapiUrl) lines.push(`- Fetch ${openapiUrl} before scraping API reference pages; prefer schemas over prose.`);
1323
1352
  if (mcp.enabled) lines.push(`- Use MCP at ${DEFAULT_MCP_PUBLIC_ROUTE} or ${DEFAULT_MCP_WELL_KNOWN_ROUTE} when your environment supports MCP tools.`);
1324
1353
  if (feedback.enabled) lines.push(`- Read ${feedback.schemaRoute} before posting feedback to ${feedback.route}.`);
@@ -2434,6 +2463,7 @@ function generateLlmsTxt(indexes, options) {
2434
2463
  */
2435
2464
  function createDocsAPI(options) {
2436
2465
  const root = options?.rootDir ?? process.cwd();
2466
+ const publishedAgentSkills = resolveConfiguredAgentSkills(options?.agent?.skills, { rootDir: root });
2437
2467
  const entry = options?.entry ?? readEntry(root);
2438
2468
  const docsPath = normalizeDocsPublicPath(options?.docsPath ?? readDocsPath(root), entry);
2439
2469
  const analytics = options?.analytics;
@@ -2474,7 +2504,10 @@ function createDocsAPI(options) {
2474
2504
  const openapiDiscovery = resolveApiReferenceOpenApiDiscovery(apiReferenceConfig);
2475
2505
  const rawMcpConfig = options?.mcp ?? readMcpConfig(root);
2476
2506
  const mcpConfig = resolveDocsMcpConfig(rawMcpConfig, { defaultName: llmsConfig.siteTitle ?? "Documentation" });
2477
- const discoveryLinkHeader = getDocsDiscoveryLinkHeader({ includeApiCatalog: apiCatalogEnabled });
2507
+ const discoveryLinkHeader = getDocsDiscoveryLinkHeader({
2508
+ includeApiCatalog: apiCatalogEnabled,
2509
+ includeAgentCard: Boolean(options?.agent?.a2a)
2510
+ });
2478
2511
  const telemetryConfig = {
2479
2512
  entry,
2480
2513
  docsPath,
@@ -2710,6 +2743,8 @@ function createDocsAPI(options) {
2710
2743
  request,
2711
2744
  preferredSkillDocument: needsSkill ? readRootSkillDocument(root) : null,
2712
2745
  fallbackSkillDocument,
2746
+ publishedSkills: await publishedAgentSkills,
2747
+ agentCard: options?.agent?.a2a,
2713
2748
  origin: url.origin,
2714
2749
  entry,
2715
2750
  docsPath,
@@ -2787,7 +2822,25 @@ function createDocsAPI(options) {
2787
2822
  llms: llmsConfig,
2788
2823
  sitemap: sitemapConfig,
2789
2824
  robots: robotsConfig,
2790
- openapi: openapiDiscovery
2825
+ openapi: openapiDiscovery,
2826
+ publishedSkills: [await resolveDocsPublishedAgentSkill({
2827
+ preferredDocument: readRootSkillDocument(root),
2828
+ fallbackDocument: renderSkillDocument({
2829
+ origin: url.origin,
2830
+ entry,
2831
+ apiRoute: requestApiRoute,
2832
+ apiCatalog: apiCatalogEnabled,
2833
+ i18n,
2834
+ search: searchConfig,
2835
+ mcp: mcpConfig,
2836
+ feedback: agentFeedbackConfig,
2837
+ llms: llmsConfig,
2838
+ sitemap: sitemapConfig,
2839
+ robots: robotsConfig,
2840
+ openapi: openapiDiscovery
2841
+ })
2842
+ }), ...await publishedAgentSkills],
2843
+ agentCard: options?.agent?.a2a
2791
2844
  }), { headers: {
2792
2845
  "Cache-Control": "public, max-age=0, s-maxage=3600",
2793
2846
  Link: discoveryLinkHeader,
@@ -2905,6 +2958,7 @@ function createDocsAPI(options) {
2905
2958
  } });
2906
2959
  }
2907
2960
  const robotsResponse = createDocsRobotsResponse({
2961
+ agentCard: Boolean(options?.agent?.a2a),
2908
2962
  request,
2909
2963
  entry,
2910
2964
  apiCatalog: apiCatalogEnabled,
@@ -3063,13 +3117,16 @@ function createDocsAPI(options) {
3063
3117
  const query = url.searchParams.get("query")?.trim();
3064
3118
  if (!query) return new Response(JSON.stringify([]), { headers: { "Content-Type": "application/json" } });
3065
3119
  const searchStartedAt = Date.now();
3120
+ const audience = resolveDocsSearchAudience(url.searchParams.get("audience"));
3066
3121
  const results = await performDocsSearch({
3067
3122
  pages: getIndexes(ctx),
3068
3123
  query,
3069
3124
  search: resolveSearchRequestConfig(searchConfig, request.url),
3125
+ audience,
3070
3126
  locale: ctx.locale,
3071
3127
  pathname: url.searchParams.get("pathname") ?? void 0,
3072
- siteTitle: llmsConfig.siteTitle ?? "Documentation"
3128
+ siteTitle: llmsConfig.siteTitle ?? "Documentation",
3129
+ baseUrl: markdownMetadataBaseUrl || url.origin
3073
3130
  });
3074
3131
  await emitDocsAnalyticsEvent(analytics, {
3075
3132
  type: "api_search",
@@ -3081,6 +3138,7 @@ function createDocsAPI(options) {
3081
3138
  properties: {
3082
3139
  ...requestAnalyticsProperties,
3083
3140
  queryLength: query.length,
3141
+ audience,
3084
3142
  resultCount: results.length,
3085
3143
  pathname: url.searchParams.get("pathname") ?? void 0,
3086
3144
  durationMs: Math.max(0, Date.now() - searchStartedAt)
@@ -3218,13 +3276,22 @@ function createDocsMCPAPI(options = {}) {
3218
3276
  const mcpConfig = options.mcp ?? readMcpConfig(rootDir, { rejectRuntimeSecurity: true });
3219
3277
  const navTitle = typeof options.nav?.title === "string" && options.nav.title.trim().length > 0 ? options.nav.title : "Documentation";
3220
3278
  const handlers = createDocsMcpHttpHandler({
3221
- source: createFilesystemDocsMcpSource({
3222
- rootDir,
3223
- entry,
3224
- contentDir,
3225
- siteTitle: navTitle,
3226
- ordering: options.ordering
3227
- }),
3279
+ source: {
3280
+ ...createFilesystemDocsMcpSource({
3281
+ rootDir,
3282
+ entry,
3283
+ contentDir,
3284
+ siteTitle: navTitle,
3285
+ ordering: options.ordering
3286
+ }),
3287
+ async getSkills() {
3288
+ const configured = await resolveConfiguredAgentSkills(options.agent?.skills, { rootDir });
3289
+ return [await resolveDocsPublishedAgentSkill({
3290
+ preferredDocument: readRootSkillDocument(rootDir),
3291
+ fallbackDocument: `---\nname: docs\ndescription: Use the project documentation through MCP resources and tools.\n---\n\n# Documentation\n`
3292
+ }), ...configured];
3293
+ }
3294
+ },
3228
3295
  mcp: mcpConfig,
3229
3296
  search: options.search,
3230
3297
  analytics: options.analytics,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/theme",
3
- "version": "0.2.60",
3
+ "version": "0.2.62",
4
4
  "description": "Theme package for @farming-labs/docs — layout, provider, MDX components, and styles",
5
5
  "keywords": [
6
6
  "docs",
@@ -146,7 +146,7 @@
146
146
  "tsdown": "^0.20.3",
147
147
  "typescript": "^5.9.3",
148
148
  "vitest": "^4.1.8",
149
- "@farming-labs/docs": "0.2.60"
149
+ "@farming-labs/docs": "0.2.62"
150
150
  },
151
151
  "peerDependencies": {
152
152
  "@farming-labs/docs": ">=0.0.1",