@farming-labs/docs 0.1.1 → 0.1.3

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.
@@ -0,0 +1,46 @@
1
+ import "./api-reference-DlfH-Y9c.mjs";
2
+ import { createFilesystemDocsMcpSource, resolveDocsMcpConfig, runDocsMcpStdio } from "./mcp.mjs";
3
+ import "./server.mjs";
4
+ import { a as readStringProperty, i as readNavTitle, o as resolveDocsConfigPath, r as readBooleanProperty, s as resolveDocsContentDir, t as extractObjectLiteral } from "./config-CSywk3ou.mjs";
5
+ import { readFileSync } from "node:fs";
6
+
7
+ //#region src/cli/mcp.ts
8
+ async function runMcp(options = {}) {
9
+ const rootDir = process.cwd();
10
+ const content = readFileSync(resolveDocsConfigPath(rootDir, options.configPath), "utf-8");
11
+ const entry = readStringProperty(content, "entry") ?? "docs";
12
+ const contentDir = resolveDocsContentDir(rootDir, content, entry);
13
+ const navTitle = readNavTitle(content);
14
+ const mcp = readMcpConfig(content);
15
+ await runDocsMcpStdio({
16
+ source: createFilesystemDocsMcpSource({
17
+ rootDir,
18
+ entry,
19
+ contentDir,
20
+ siteTitle: navTitle ?? "Documentation"
21
+ }),
22
+ mcp: resolveDocsMcpConfig(mcp ?? true, { defaultName: navTitle ?? "@farming-labs/docs" }),
23
+ defaultName: navTitle ?? "@farming-labs/docs"
24
+ });
25
+ }
26
+ function readMcpConfig(content) {
27
+ if (content.match(/mcp\s*:\s*false/)) return false;
28
+ if (content.match(/mcp\s*:\s*true/)) return true;
29
+ const block = extractObjectLiteral(content, "mcp");
30
+ if (!block) return void 0;
31
+ return {
32
+ enabled: readBooleanProperty(block, "enabled"),
33
+ route: readStringProperty(block, "route"),
34
+ name: readStringProperty(block, "name"),
35
+ version: readStringProperty(block, "version"),
36
+ tools: {
37
+ listPages: readBooleanProperty(block, "listPages"),
38
+ readPage: readBooleanProperty(block, "readPage"),
39
+ searchDocs: readBooleanProperty(block, "searchDocs"),
40
+ getNavigation: readBooleanProperty(block, "getNavigation")
41
+ }
42
+ };
43
+ }
44
+
45
+ //#endregion
46
+ export { runMcp };
package/dist/mcp.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { u as DocsMcpConfig, w as OrderingItem } from "./types-Bd3kyFF1.mjs";
2
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp";
1
+ import { R as OrderingItem, b as DocsSearchConfig, f as DocsMcpConfig } from "./types-BAulrjlV.mjs";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
 
4
4
  //#region src/mcp.d.ts
5
5
  interface DocsMcpPage {
@@ -62,6 +62,7 @@ interface DocsMcpHttpHandlers {
62
62
  interface CreateDocsMcpServerOptions {
63
63
  source: DocsMcpSource;
64
64
  mcp?: boolean | DocsMcpConfig;
65
+ search?: boolean | DocsSearchConfig;
65
66
  defaultName?: string;
66
67
  defaultVersion?: string;
67
68
  }
package/dist/mcp.mjs CHANGED
@@ -1,11 +1,12 @@
1
+ import { s as performDocsSearch } from "./search-BS6C5N1i.mjs";
1
2
  import fs from "node:fs";
2
3
  import path from "node:path";
3
4
  import { randomUUID } from "node:crypto";
4
5
  import matter from "gray-matter";
5
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp";
6
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio";
7
- import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp";
8
- import { isInitializeRequest } from "@modelcontextprotocol/sdk/types";
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
+ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
9
+ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
9
10
  import * as z from "zod/v4";
10
11
 
11
12
  //#region src/mcp.ts
@@ -88,6 +89,7 @@ async function createDocsMcpServer(options) {
88
89
  defaultName: options.defaultName ?? options.source.siteTitle ?? DEFAULT_MCP_NAME,
89
90
  defaultVersion: options.defaultVersion
90
91
  });
92
+ const toolSearchConfig = resolveMcpToolSearchConfig(options.search, resolved.route);
91
93
  const server = new McpServer({
92
94
  name: resolved.name,
93
95
  version: resolved.version
@@ -144,7 +146,14 @@ async function createDocsMcpServer(options) {
144
146
  inputSchema: searchDocsInputSchema,
145
147
  annotations: { readOnlyHint: true }
146
148
  }, async ({ query, limit, locale }) => {
147
- const results = searchDocsPages(dedupePages(await options.source.getPages(locale)), query, limit ?? 10);
149
+ const results = await performDocsSearch({
150
+ pages: toSearchSourcePages(dedupePages(await options.source.getPages(locale))),
151
+ query,
152
+ search: toolSearchConfig ?? true,
153
+ locale,
154
+ siteTitle: options.source.siteTitle,
155
+ limit: limit ?? 10
156
+ });
148
157
  return { content: [{
149
158
  type: "text",
150
159
  text: JSON.stringify({ results }, null, 2)
@@ -382,6 +391,31 @@ function dedupePages(pages) {
382
391
  for (const page of pages) seen.set(page.url, page);
383
392
  return [...seen.values()];
384
393
  }
394
+ function toSearchSourcePages(pages) {
395
+ return pages.map((page) => ({
396
+ title: page.title,
397
+ url: page.url,
398
+ content: page.content,
399
+ rawContent: page.rawContent,
400
+ description: page.description
401
+ }));
402
+ }
403
+ function isSelfMcpSearchEndpoint(search, route) {
404
+ if (!search || search === true || typeof search !== "object" || search.provider !== "mcp") return false;
405
+ const endpoint = search.endpoint.trim();
406
+ if (!endpoint.startsWith("/")) return false;
407
+ return normalizeDocsMcpRoute(endpoint) === normalizeDocsMcpRoute(route);
408
+ }
409
+ function resolveMcpToolSearchConfig(search, route) {
410
+ if (!isSelfMcpSearchEndpoint(search, route)) return search;
411
+ const config = search;
412
+ return {
413
+ provider: "simple",
414
+ enabled: config.enabled,
415
+ maxResults: config.maxResults,
416
+ chunking: config.chunking
417
+ };
418
+ }
385
419
  function toPageSummaries(pages) {
386
420
  return pages.map((page) => ({
387
421
  slug: page.slug,
@@ -418,38 +452,6 @@ function normalizeUrlPath(value) {
418
452
  if (normalized === "/") return normalized;
419
453
  return normalized.replace(/\/+$/, "");
420
454
  }
421
- function searchDocsPages(pages, query, limit) {
422
- const normalizedQuery = query.toLowerCase().trim();
423
- if (!normalizedQuery) return [];
424
- const words = normalizedQuery.split(/\s+/).filter(Boolean);
425
- return pages.map((page) => {
426
- const titleScore = page.title.toLowerCase().includes(normalizedQuery) ? 10 : 0;
427
- const descriptionScore = page.description?.toLowerCase().includes(normalizedQuery) ? 4 : 0;
428
- const contentScore = words.reduce((score, word) => {
429
- return score + (page.content.toLowerCase().includes(word) ? 1 : 0);
430
- }, 0);
431
- return {
432
- slug: page.slug,
433
- url: page.url,
434
- title: page.title,
435
- description: page.description,
436
- icon: page.icon,
437
- excerpt: buildExcerpt(page, words),
438
- score: titleScore + descriptionScore + contentScore
439
- };
440
- }).filter((page) => page.score > 0).sort((left, right) => right.score - left.score).slice(0, limit).map(({ score: _score, ...page }) => page);
441
- }
442
- function buildExcerpt(page, words) {
443
- const haystack = page.rawContent ?? page.content;
444
- const lower = haystack.toLowerCase();
445
- const firstHit = words.find((word) => lower.includes(word.toLowerCase()));
446
- if (!firstHit) return page.description;
447
- const index = lower.indexOf(firstHit.toLowerCase());
448
- const start = Math.max(0, index - 80);
449
- const end = Math.min(haystack.length, index + 140);
450
- const excerpt = haystack.slice(start, end).replace(/\s+/g, " ").trim();
451
- return excerpt.length > 0 ? excerpt : page.description;
452
- }
453
455
  function renderPageDocument(page) {
454
456
  const lines = [`# ${page.title}`, `URL: ${page.url}`];
455
457
  if (page.description) lines.push(`Description: ${page.description}`);