{"version":3,"file":"server-C094o04u.mjs","names":[],"sources":["../src/mcp/shared.ts","../src/mcp/server.ts"],"sourcesContent":["/**\n * Shared MCP server utilities.\n *\n * Common helpers used by both @pagesmith/core and @pagesmith/docs MCP servers.\n */\n\nimport { existsSync, readFileSync } from \"fs\";\nimport { dirname, resolve } from \"path\";\n\nfunction resolvePackageRoot(moduleDir: string): string {\n  let current = resolve(moduleDir);\n\n  while (true) {\n    const pkgPath = resolve(current, \"package.json\");\n    if (existsSync(pkgPath)) return current;\n\n    const parent = dirname(current);\n    if (parent === current) {\n      throw new Error(`Could not resolve package root from module directory: ${moduleDir}`);\n    }\n    current = parent;\n  }\n}\n\n/** Read the package version from a package.json relative to the calling module. */\nexport function getPackageVersion(moduleDir: string): string {\n  const pkgPath = resolve(resolvePackageRoot(moduleDir), \"package.json\");\n  const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as { version?: string };\n  if (!pkg.version) {\n    throw new Error(`Missing package version in ${pkgPath}`);\n  }\n  return pkg.version;\n}\n\n/** Resolve a doc file path relative to the package root. */\nexport function resolvePackageDocPath(moduleDir: string, relativePath: string): string {\n  return resolve(resolvePackageRoot(moduleDir), relativePath);\n}\n\n/** Load a file as an MCP text resource response. Throws if the file doesn't exist. */\nexport function asTextResource(uri: string, path: string) {\n  if (!existsSync(path)) {\n    throw new Error(`Resource file not found: ${path}`);\n  }\n\n  return {\n    contents: [\n      {\n        uri,\n        mimeType: \"text/markdown\",\n        text: readFileSync(path, \"utf-8\"),\n      },\n    ],\n  };\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { basename, resolve } from \"path\";\nimport { z } from \"zod\";\nimport type { ContentLayer } from \"../content-layer.js\";\nimport type { Loader } from \"../loaders/types.js\";\nimport { asTextResource, getPackageVersion, resolvePackageDocPath } from \"./shared.js\";\n\nexport type CoreMcpServerOptions = {\n  layer: ContentLayer;\n  rootDir?: string;\n};\n\nfunction getLoaderType(loader: string | Loader): string {\n  if (typeof loader === \"string\") return loader;\n  return loader.name ?? \"custom\";\n}\n\nfunction getSchemaFieldNames(schema: z.ZodType): string[] | undefined {\n  if (schema instanceof z.ZodObject) {\n    return Object.keys(schema.shape);\n  }\n  if (schema instanceof z.ZodOptional || schema instanceof z.ZodNullable) {\n    return getSchemaFieldNames(schema.unwrap() as z.ZodType);\n  }\n  if (schema instanceof z.ZodDefault) {\n    return getSchemaFieldNames(schema._def.innerType as z.ZodType);\n  }\n  return undefined;\n}\n\nexport function createCoreMcpServer(options: CoreMcpServerOptions): McpServer {\n  const { layer } = options;\n  const baseRoot = resolve(options.rootDir ?? process.cwd());\n\n  const server = new McpServer(\n    {\n      name: \"@pagesmith/core-mcp\",\n      version: getPackageVersion(import.meta.dirname),\n    },\n    {\n      instructions: [\n        \"Use core_* tools to inspect @pagesmith/core content layers.\",\n        `Root directory: ${baseRoot}`,\n      ].join(\"\\n\"),\n    },\n  );\n\n  // ── Tools ──\n\n  server.registerTool(\n    \"core_list_collections\",\n    {\n      description:\n        \"List all configured collections with their loader type, directory, and schema field names.\",\n      inputSchema: {},\n    },\n    async () => {\n      const names = layer.getCollectionNames();\n      const collections = names.map((name) => {\n        const def = layer.getCollectionDef(name);\n        return {\n          name,\n          loader: def ? getLoaderType(def.loader) : \"unknown\",\n          directory: def?.directory,\n          schemaFields: def ? getSchemaFieldNames(def.schema) : undefined,\n        };\n      });\n\n      return {\n        content: [\n          {\n            type: \"text\",\n            text: JSON.stringify(collections, null, 2),\n          },\n        ],\n      };\n    },\n  );\n\n  server.registerTool(\n    \"core_list_entries\",\n    {\n      description:\n        \"List entries in a collection with slug, title, description, and file path. Does not render content. Supports pagination via limit/offset.\",\n      inputSchema: {\n        collection: z.string().describe(\"Collection name\"),\n        limit: z\n          .number()\n          .int()\n          .min(1)\n          .max(200)\n          .default(50)\n          .describe(\"Maximum number of entries to return (default 50)\"),\n        offset: z\n          .number()\n          .int()\n          .min(0)\n          .default(0)\n          .describe(\"Number of entries to skip (default 0)\"),\n      },\n    },\n    async ({\n      collection,\n      limit,\n      offset,\n    }: {\n      collection: string;\n      limit: number;\n      offset: number;\n    }) => {\n      const entries = await layer.getCollection(collection);\n      const total = entries.length;\n      const page = entries.slice(offset, offset + limit);\n\n      const items = page.map((entry) => {\n        const data = (entry.data ?? {}) as Record<string, unknown>;\n        return {\n          slug: entry.slug,\n          title: typeof data.title === \"string\" ? data.title : undefined,\n          description: typeof data.description === \"string\" ? data.description : undefined,\n          filePath: entry.filePath,\n        };\n      });\n\n      return {\n        content: [\n          {\n            type: \"text\",\n            text: JSON.stringify(\n              {\n                collection,\n                total,\n                offset,\n                limit,\n                count: items.length,\n                entries: items,\n              },\n              null,\n              2,\n            ),\n          },\n        ],\n      };\n    },\n  );\n\n  server.registerTool(\n    \"core_get_entry\",\n    {\n      description:\n        \"Load a single content entry by collection name and slug. Returns slug, validated data, rendered HTML, headings, and read time.\",\n      inputSchema: {\n        collection: z.string().describe(\"Collection name\"),\n        slug: z.string().describe(\"Entry slug\"),\n      },\n    },\n    async ({ collection, slug }: { collection: string; slug: string }) => {\n      const entry = await layer.getEntry(collection, slug);\n      if (!entry) {\n        throw new Error(`Entry not found: ${collection}/${slug}`);\n      }\n\n      const rendered = await entry.render();\n\n      return {\n        content: [\n          {\n            type: \"text\",\n            text: JSON.stringify(\n              {\n                slug: entry.slug,\n                collection: entry.collection,\n                filePath: entry.filePath,\n                data: entry.data,\n                html: rendered.html,\n                headings: rendered.headings,\n                readTime: rendered.readTime,\n              },\n              null,\n              2,\n            ),\n          },\n        ],\n      };\n    },\n  );\n\n  server.registerTool(\n    \"core_validate\",\n    {\n      description:\n        \"Run validation on a specific collection or all collections. Returns structured validation results with error and warning counts.\",\n      inputSchema: {\n        collection: z.string().optional().describe(\"Collection name (omit to validate all)\"),\n      },\n    },\n    async ({ collection }: { collection?: string }) => {\n      const results = await layer.validate(collection);\n\n      return {\n        content: [\n          {\n            type: \"text\",\n            text: JSON.stringify(results, null, 2),\n          },\n        ],\n      };\n    },\n  );\n\n  server.registerTool(\n    \"core_search_entries\",\n    {\n      description:\n        \"Search entries across collections by matching a query string against titles, descriptions, tags, and slugs. Case-insensitive. Returns up to 20 matches.\",\n      inputSchema: {\n        query: z.string().describe(\"Search query string\"),\n        collection: z.string().optional().describe(\"Limit search to a specific collection\"),\n      },\n    },\n    async ({ query, collection }: { query: string; collection?: string }) => {\n      if (!query.trim()) {\n        return {\n          content: [\n            { type: \"text\", text: JSON.stringify({ query, matches: [], count: 0 }, null, 2) },\n          ],\n        };\n      }\n\n      const queryLower = query.toLowerCase();\n      const maxResults = 20;\n      const collectionNames = collection ? [collection] : layer.getCollectionNames();\n\n      type SearchMatch = {\n        collection: string;\n        slug: string;\n        title?: string;\n        description?: string;\n        filePath: string;\n      };\n\n      const matches: SearchMatch[] = [];\n\n      for (const name of collectionNames) {\n        if (matches.length >= maxResults) break;\n        const entries = await layer.getCollection(name);\n        for (const entry of entries) {\n          if (matches.length >= maxResults) break;\n          const data = (entry.data ?? {}) as Record<string, unknown>;\n          const title = typeof data.title === \"string\" ? data.title : \"\";\n          const description = typeof data.description === \"string\" ? data.description : \"\";\n          const tags = Array.isArray(data.tags) ? data.tags.join(\" \") : \"\";\n          const searchText = `${entry.slug} ${title} ${description} ${tags}`.toLowerCase();\n\n          if (searchText.includes(queryLower)) {\n            matches.push({\n              collection: name,\n              slug: entry.slug,\n              title: title || undefined,\n              description: description || undefined,\n              filePath: entry.filePath,\n            });\n          }\n        }\n      }\n\n      return {\n        content: [\n          {\n            type: \"text\",\n            text: JSON.stringify(\n              { query, collection: collection ?? null, count: matches.length, matches },\n              null,\n              2,\n            ),\n          },\n        ],\n      };\n    },\n  );\n\n  // ── Resources ──\n\n  server.registerResource(\n    \"core-agent-usage\",\n    \"pagesmith://core/agents/usage\",\n    {\n      title: \"@pagesmith/core agent usage\",\n      description: \"Version-matched AI usage guide for @pagesmith/core.\",\n      mimeType: \"text/markdown\",\n    },\n    async () =>\n      asTextResource(\n        \"pagesmith://core/agents/usage\",\n        resolvePackageDocPath(\n          import.meta.dirname,\n          \"skills/pagesmith-core-setup/references/usage.md\",\n        ),\n      ),\n  );\n\n  server.registerResource(\n    \"core-llms-full\",\n    \"pagesmith://core/llms-full\",\n    {\n      title: \"@pagesmith/core llms-full\",\n      description: \"Version-matched full AI reference for @pagesmith/core.\",\n      mimeType: \"text/markdown\",\n    },\n    async () =>\n      asTextResource(\n        \"pagesmith://core/llms-full\",\n        resolvePackageDocPath(import.meta.dirname, \"llms-full.txt\"),\n      ),\n  );\n\n  server.registerResource(\n    \"core-reference\",\n    \"pagesmith://core/reference\",\n    {\n      title: \"@pagesmith/core reference\",\n      description:\n        \"Core package reference for content layer, schemas, loaders, and markdown pipeline.\",\n      mimeType: \"text/markdown\",\n    },\n    async () =>\n      asTextResource(\n        \"pagesmith://core/reference\",\n        resolvePackageDocPath(import.meta.dirname, \"REFERENCE.md\"),\n      ),\n  );\n\n  return server;\n}\n\nexport async function startCoreMcpServer(options: CoreMcpServerOptions): Promise<void> {\n  const server = createCoreMcpServer(options);\n  const transport = new StdioServerTransport();\n  await server.connect(transport);\n  // Keep logs on stderr; stdout is reserved for MCP protocol messages.\n  console.error(\n    `[pagesmith:mcp] @pagesmith/core MCP server started (root=${basename(resolve(options.rootDir ?? process.cwd()))})`,\n  );\n}\n"],"mappings":"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"}