@primeuicom/mcp 0.1.16 → 0.1.18

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.
package/dist/service.js CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/service.ts
4
- import path7 from "path";
4
+ import path8 from "path";
5
+ import { fileURLToPath } from "url";
5
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
7
 
7
8
  // src/lib/project-link-config.ts
@@ -85,6 +86,9 @@ function toUniqueResolvedDirs(directories) {
85
86
  }
86
87
  return [...resolved];
87
88
  }
89
+ function buildPrimeUiProjectSearchRoots(options) {
90
+ return toUniqueResolvedDirs([options.cwd, ...options.fallbackCwds ?? []]);
91
+ }
88
92
  async function resolvePrimeUiProjectConfig(options) {
89
93
  const envProjectRoot = options.projectRootFromEnv?.trim();
90
94
  let projectConfigPath;
@@ -94,10 +98,10 @@ async function resolvePrimeUiProjectConfig(options) {
94
98
  PRIMEUI_PROJECT_CONFIG_RELATIVE_PATH
95
99
  );
96
100
  } else {
97
- const searchRoots = toUniqueResolvedDirs([
98
- options.cwd,
99
- ...options.fallbackCwds ?? []
100
- ]);
101
+ const searchRoots = buildPrimeUiProjectSearchRoots({
102
+ cwd: options.cwd,
103
+ fallbackCwds: options.fallbackCwds
104
+ });
101
105
  for (const searchRoot of searchRoots) {
102
106
  const foundProjectConfigPath = await findPrimeUiProjectConfigPath(searchRoot);
103
107
  if (foundProjectConfigPath) {
@@ -264,6 +268,33 @@ var inspectPageReportRowSchema = z2.object({
264
268
  title: z2.string().nullable().describe("Title extracted by MCP from component props."),
265
269
  description: z2.string().nullable().describe("Description extracted by MCP from component props.")
266
270
  });
271
+ var healthOptionSchema = z2.object({
272
+ key: z2.string().describe("Environment variable or diagnostic option name."),
273
+ description: z2.string().describe("Human-readable explanation of what this option controls."),
274
+ currentValue: z2.string().nullable().describe(
275
+ "Current effective value for this option. Null when not set or intentionally hidden."
276
+ )
277
+ });
278
+ var healthRuntimeSchema = z2.object({
279
+ cwd: z2.string().describe("process.cwd() observed by the MCP server process."),
280
+ initCwd: z2.string().nullable().describe("INIT_CWD environment value, if provided by the client runtime."),
281
+ pwd: z2.string().nullable().describe("PWD environment value, if provided by the client runtime."),
282
+ searchRoots: z2.array(z2.string()).describe(
283
+ "Resolved directories used when searching for .primeui/project.json."
284
+ ),
285
+ rootsListFallbackCwds: z2.array(z2.string()).describe(
286
+ "File-system roots derived from MCP roots/list and used as search fallbacks."
287
+ )
288
+ });
289
+ var healthConfigSchema = z2.object({
290
+ found: z2.boolean().describe("True when .primeui/project.json was found and parsed successfully."),
291
+ projectRoot: z2.string().nullable().describe("Resolved PrimeUI project root directory (parent of .primeui)."),
292
+ projectConfigPath: z2.string().nullable().describe("Absolute path to .primeui/project.json when found."),
293
+ targetProjectPath: z2.string().nullable().describe("targetProjectPath value loaded from project config."),
294
+ targetProjectRoot: z2.string().nullable().describe("Absolute target project root derived from targetProjectPath."),
295
+ source: z2.enum(["env.PRIMEUI_PROJECT_ROOT", "search"]).describe("How project config lookup was resolved."),
296
+ error: z2.string().nullable().describe("Human-readable resolution error when config is not available.")
297
+ });
267
298
  var TRIGGER_PHRASES = [
268
299
  "import page from PrimeUI",
269
300
  "add page from PrimeUI",
@@ -294,6 +325,7 @@ ${WORKFLOW_SUMMARY}
294
325
 
295
326
  CRITICAL RULES FOR PAGE IMPORT:
296
327
  - Import is PAGE-BY-PAGE only. Never import the entire project at once.
328
+ - Use health_check to inspect runtime path/config resolution when setup issues occur.
297
329
  - Always call clear_temp at the very beginning of a new import flow to avoid stale export state.
298
330
  - After get_project_info, always compare PrimeUI pages against the user's existing local project pages before proposing imports.
299
331
  - Reconciliation report MUST include: page presence match, slug/pagePath/componentsPath path match, and proposed action per page.
@@ -324,6 +356,34 @@ CRITICAL RULES FOR PAGE IMPORT:
324
356
  - If integration pattern is obvious, apply it and explicitly mark it in the report with "\u26A0 integration update".
325
357
  - If integration pattern is unclear, ask the user before editing integration files.
326
358
  `.trim();
359
+ var toolHealthCheck = {
360
+ title: "PrimeUI Health Check",
361
+ description: `Diagnostic tool for MCP runtime setup. This tool MUST NOT fail: it always returns structured diagnostics even when project config is missing.
362
+
363
+ WHEN TO USE:
364
+ - Use this first when PrimeUI MCP tools fail due to project path/config issues.
365
+ - Use this to verify cwd/env/roots behavior in different MCP clients (Codex, Claude Code, Inspector).
366
+
367
+ RETURNS:
368
+ - Runtime paths (cwd, INIT_CWD, PWD, search roots),
369
+ - roots/list fallback paths from client (if available),
370
+ - Whether .primeui/project.json was found and parsed,
371
+ - Effective resolved project root/config path (when available),
372
+ - Current option values and fallback guidance.`,
373
+ inputSchema: {},
374
+ outputSchema: {
375
+ status: z2.enum(["ok", "degraded"]).describe("Overall setup health. 'degraded' means config is unresolved."),
376
+ runtime: healthRuntimeSchema,
377
+ config: healthConfigSchema,
378
+ options: z2.array(healthOptionSchema).describe("Environment and runtime options relevant for configuration.")
379
+ },
380
+ annotations: {
381
+ readOnlyHint: true,
382
+ destructiveHint: false,
383
+ idempotentHint: true,
384
+ openWorldHint: false
385
+ }
386
+ };
327
387
  var toolGetProjectInfo = {
328
388
  title: "PrimeUI Project Info",
329
389
  description: `ENTRY POINT for all PrimeUI import operations. Always start here. Returns project metadata and the full list of available pages.
@@ -651,6 +711,38 @@ function createPrimeUiMcpServer(source) {
651
711
  instructions: initialInstructions
652
712
  }
653
713
  );
714
+ server.registerTool(
715
+ "health_check",
716
+ toolHealthCheck,
717
+ async () => {
718
+ try {
719
+ const health = await source.healthCheck();
720
+ return okResult("health_check", health);
721
+ } catch (error) {
722
+ const message = error instanceof Error ? error.message : String(error);
723
+ return okResult("health_check", {
724
+ status: "degraded",
725
+ runtime: {
726
+ cwd: process.cwd(),
727
+ initCwd: process.env.INIT_CWD?.trim() || null,
728
+ pwd: process.env.PWD?.trim() || null,
729
+ searchRoots: [process.cwd()],
730
+ rootsListFallbackCwds: []
731
+ },
732
+ config: {
733
+ found: false,
734
+ projectRoot: null,
735
+ projectConfigPath: null,
736
+ targetProjectPath: null,
737
+ targetProjectRoot: null,
738
+ source: "search",
739
+ error: `health_check fallback: ${message}`
740
+ },
741
+ options: []
742
+ });
743
+ }
744
+ }
745
+ );
654
746
  server.registerTool(
655
747
  "get_project_info",
656
748
  toolGetProjectInfo,
@@ -741,13 +833,118 @@ function createPrimeUiMcpServer(source) {
741
833
  return server;
742
834
  }
743
835
 
836
+ // src/services/health-check-service.ts
837
+ import path2 from "path";
838
+ function normalizeOptional(value) {
839
+ const trimmed = value?.trim();
840
+ return trimmed ? trimmed : null;
841
+ }
842
+ function buildHealthOptions(input) {
843
+ return [
844
+ {
845
+ key: "PRIMEUI_PROJECT_ROOT",
846
+ description: "Optional absolute root containing .primeui/project.json. Use as explicit override.",
847
+ currentValue: input.projectRootFromEnv
848
+ },
849
+ {
850
+ key: "PRIMEUI_API_BASE_URL",
851
+ description: "Optional PrimeUI API base URL override.",
852
+ currentValue: input.apiBaseUrlFromEnv
853
+ },
854
+ {
855
+ key: "PRIMEUI_API_KEY",
856
+ description: "Optional API key override. If missing, MCP uses apiKey from .primeui/project.json.",
857
+ currentValue: input.apiKeyFromEnv ? "set" : null
858
+ },
859
+ {
860
+ key: "Fallback search",
861
+ description: "When PRIMEUI_PROJECT_ROOT is not set, MCP searches from roots/list file roots, INIT_CWD, PWD, then process.cwd().",
862
+ currentValue: null
863
+ }
864
+ ];
865
+ }
866
+ async function runPrimeUiHealthCheck(input) {
867
+ const projectRootFromEnv = normalizeOptional(input.projectRootFromEnv);
868
+ const apiBaseUrlFromEnv = normalizeOptional(input.apiBaseUrlFromEnv);
869
+ const apiKeyFromEnv = normalizeOptional(input.apiKeyFromEnv);
870
+ const initCwd = normalizeOptional(input.initCwd);
871
+ const pwd = normalizeOptional(input.pwd);
872
+ const rootsListFallbackCwds = (input.rootsListFallbackCwds ?? []).map((value) => value.trim()).filter(Boolean).map((value) => path2.resolve(value));
873
+ const fallbackCwds = (input.fallbackCwds ?? []).map((value) => value.trim()).filter(Boolean);
874
+ const searchRoots = buildPrimeUiProjectSearchRoots({
875
+ cwd: input.cwd,
876
+ fallbackCwds
877
+ });
878
+ try {
879
+ const resolvedProjectConfig = await resolvePrimeUiProjectConfig({
880
+ cwd: input.cwd,
881
+ projectRootFromEnv: projectRootFromEnv ?? void 0,
882
+ fallbackCwds
883
+ });
884
+ const targetProjectPath = resolvedProjectConfig.projectConfig.targetProjectPath;
885
+ return {
886
+ status: "ok",
887
+ runtime: {
888
+ cwd: path2.resolve(input.cwd),
889
+ initCwd,
890
+ pwd,
891
+ searchRoots,
892
+ rootsListFallbackCwds
893
+ },
894
+ config: {
895
+ found: true,
896
+ projectRoot: resolvedProjectConfig.projectRoot,
897
+ projectConfigPath: resolvedProjectConfig.projectConfigPath,
898
+ targetProjectPath,
899
+ targetProjectRoot: path2.resolve(
900
+ resolvedProjectConfig.projectRoot,
901
+ targetProjectPath
902
+ ),
903
+ source: projectRootFromEnv ? "env.PRIMEUI_PROJECT_ROOT" : "search",
904
+ error: null
905
+ },
906
+ options: buildHealthOptions({
907
+ projectRootFromEnv,
908
+ apiBaseUrlFromEnv,
909
+ apiKeyFromEnv
910
+ })
911
+ };
912
+ } catch (error) {
913
+ const message = error instanceof Error ? error.message : String(error);
914
+ return {
915
+ status: "degraded",
916
+ runtime: {
917
+ cwd: path2.resolve(input.cwd),
918
+ initCwd,
919
+ pwd,
920
+ searchRoots,
921
+ rootsListFallbackCwds
922
+ },
923
+ config: {
924
+ found: false,
925
+ projectRoot: null,
926
+ projectConfigPath: null,
927
+ targetProjectPath: null,
928
+ targetProjectRoot: null,
929
+ source: projectRootFromEnv ? "env.PRIMEUI_PROJECT_ROOT" : "search",
930
+ error: message
931
+ },
932
+ options: buildHealthOptions({
933
+ projectRootFromEnv,
934
+ apiBaseUrlFromEnv,
935
+ apiKeyFromEnv
936
+ })
937
+ };
938
+ }
939
+ }
940
+
744
941
  // src/services/project-sync-service.ts
745
- import path5 from "path";
942
+ import path6 from "path";
746
943
  import { readFile as readFile4 } from "fs/promises";
747
944
 
748
945
  // src/lib/fs.ts
749
946
  import { mkdir, rm, writeFile } from "fs/promises";
750
- import path2 from "path";
947
+ import path3 from "path";
751
948
  import extractZipArchive from "extract-zip";
752
949
  async function ensureDir(dirPath) {
753
950
  await mkdir(dirPath, { recursive: true });
@@ -757,7 +954,7 @@ async function resetDir(dirPath) {
757
954
  await mkdir(dirPath, { recursive: true });
758
955
  }
759
956
  async function writeUtf8(filePath, content) {
760
- await ensureDir(path2.dirname(filePath));
957
+ await ensureDir(path3.dirname(filePath));
761
958
  await writeFile(filePath, content, "utf-8");
762
959
  }
763
960
  async function extractZip(zipPath, targetDir) {
@@ -772,12 +969,12 @@ async function extractZip(zipPath, targetDir) {
772
969
 
773
970
  // src/services/page-copy-service.ts
774
971
  import { readFile as readFile3, readdir, stat as stat3, writeFile as writeFile2 } from "fs/promises";
775
- import path4 from "path";
972
+ import path5 from "path";
776
973
 
777
974
  // src/lib/import-graph.ts
778
975
  import { readFile as readFile2, stat as stat2 } from "fs/promises";
779
976
  import { builtinModules } from "module";
780
- import path3 from "path";
977
+ import path4 from "path";
781
978
  var INTERNAL_EXTENSIONS = [
782
979
  ".ts",
783
980
  ".tsx",
@@ -853,7 +1050,7 @@ async function pathExists(filePath) {
853
1050
  }
854
1051
  }
855
1052
  async function resolveFileCandidate(candidateBase) {
856
- const ext = path3.extname(candidateBase);
1053
+ const ext = path4.extname(candidateBase);
857
1054
  if (ext) {
858
1055
  if (await pathExists(candidateBase)) {
859
1056
  return candidateBase;
@@ -873,7 +1070,7 @@ async function resolveFileCandidate(candidateBase) {
873
1070
  }
874
1071
  if (stats.isDirectory()) {
875
1072
  for (const extension of INTERNAL_EXTENSIONS) {
876
- const indexCandidate = path3.join(candidateBase, `index${extension}`);
1073
+ const indexCandidate = path4.join(candidateBase, `index${extension}`);
877
1074
  if (await pathExists(indexCandidate)) {
878
1075
  return indexCandidate;
879
1076
  }
@@ -895,13 +1092,13 @@ async function resolveImportToFile(projectRoot, importerFilePath, specifier) {
895
1092
  }
896
1093
  let candidateBase = null;
897
1094
  if (specifier.startsWith("@/")) {
898
- candidateBase = path3.join(projectRoot, "src", specifier.slice(2));
1095
+ candidateBase = path4.join(projectRoot, "src", specifier.slice(2));
899
1096
  } else if (specifier.startsWith("@root/")) {
900
- candidateBase = path3.join(projectRoot, specifier.slice(6));
1097
+ candidateBase = path4.join(projectRoot, specifier.slice(6));
901
1098
  } else if (specifier.startsWith(".")) {
902
- candidateBase = path3.resolve(path3.dirname(importerFilePath), specifier);
1099
+ candidateBase = path4.resolve(path4.dirname(importerFilePath), specifier);
903
1100
  } else if (specifier.startsWith("/")) {
904
- candidateBase = path3.join(projectRoot, specifier.slice(1));
1101
+ candidateBase = path4.join(projectRoot, specifier.slice(1));
905
1102
  } else {
906
1103
  return { kind: "unknown" };
907
1104
  }
@@ -915,12 +1112,12 @@ async function resolveImportToFile(projectRoot, importerFilePath, specifier) {
915
1112
  };
916
1113
  }
917
1114
  function shouldParseFile(filePath) {
918
- return PARSEABLE_EXTENSIONS.has(path3.extname(filePath).toLowerCase());
1115
+ return PARSEABLE_EXTENSIONS.has(path4.extname(filePath).toLowerCase());
919
1116
  }
920
1117
  async function buildImportGraph(input) {
921
1118
  const shouldFollowInternalImports = input.followInternalImports ?? true;
922
- const projectRoot = path3.resolve(input.projectRoot);
923
- const queue = input.entryFiles.map((filePath) => path3.resolve(filePath));
1119
+ const projectRoot = path4.resolve(input.projectRoot);
1120
+ const queue = input.entryFiles.map((filePath) => path4.resolve(filePath));
924
1121
  const visited = /* @__PURE__ */ new Set();
925
1122
  const internalFiles = /* @__PURE__ */ new Set();
926
1123
  const externalPackages = /* @__PURE__ */ new Set();
@@ -1173,10 +1370,10 @@ function normalizeSlug2(slug) {
1173
1370
  return withLeadingSlash.replace(/\/+$/, "") || "/";
1174
1371
  }
1175
1372
  function toPosixPath(value) {
1176
- return value.split(path4.sep).join("/");
1373
+ return value.split(path5.sep).join("/");
1177
1374
  }
1178
1375
  function toProjectRelative(rootPath, absolutePath) {
1179
- return toPosixPath(path4.relative(rootPath, absolutePath));
1376
+ return toPosixPath(path5.relative(rootPath, absolutePath));
1180
1377
  }
1181
1378
  function escapeRegExp(value) {
1182
1379
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -1233,7 +1430,7 @@ function buildPlannedSourceBuffer(sourceBuffer, sourceFilePath, importRewritePla
1233
1430
  if (!importRewritePlan) {
1234
1431
  return sourceBuffer;
1235
1432
  }
1236
- const extension = path4.extname(sourceFilePath).toLowerCase();
1433
+ const extension = path5.extname(sourceFilePath).toLowerCase();
1237
1434
  if (!REWRITABLE_IMPORT_EXTENSIONS.has(extension)) {
1238
1435
  return sourceBuffer;
1239
1436
  }
@@ -1335,9 +1532,9 @@ async function resolveManifestCandidateFiles(input) {
1335
1532
  );
1336
1533
  }
1337
1534
  const normalizedRelative = toPosixPath(trimmed).replace(/^\.\/+/, "");
1338
- const absolutePath = path4.resolve(input.exportPath, normalizedRelative);
1339
- const relative = path4.relative(input.exportPath, absolutePath);
1340
- if (relative.startsWith("..") || path4.isAbsolute(relative)) {
1535
+ const absolutePath = path5.resolve(input.exportPath, normalizedRelative);
1536
+ const relative = path5.relative(input.exportPath, absolutePath);
1537
+ if (relative.startsWith("..") || path5.isAbsolute(relative)) {
1341
1538
  throw new Error(
1342
1539
  `Export manifest for page "${input.pageSlug}" contains path outside export root: ${trimmed}`
1343
1540
  );
@@ -1368,12 +1565,12 @@ async function resolveSingleExportDirectory(exportsRoot) {
1368
1565
  const exportId = exportDirectories[0] ?? "";
1369
1566
  return {
1370
1567
  exportId,
1371
- exportPath: path4.join(exportsRoot, exportId)
1568
+ exportPath: path5.join(exportsRoot, exportId)
1372
1569
  };
1373
1570
  }
1374
1571
  function ensureSafeTargetPath(projectRoot, targetPath) {
1375
- const relative = path4.relative(projectRoot, targetPath);
1376
- if (relative.startsWith("..") || path4.isAbsolute(relative)) {
1572
+ const relative = path5.relative(projectRoot, targetPath);
1573
+ if (relative.startsWith("..") || path5.isAbsolute(relative)) {
1377
1574
  throw new Error(
1378
1575
  `Refusing to write outside project root. Computed target: ${targetPath}`
1379
1576
  );
@@ -1431,19 +1628,19 @@ function resolveTargetFilePath(input) {
1431
1628
  if (sourceFilePath === sourcePagePath) {
1432
1629
  return targetPagePath;
1433
1630
  }
1434
- const componentsPrefix = `${sourceComponentsPath}${path4.sep}`;
1631
+ const componentsPrefix = `${sourceComponentsPath}${path5.sep}`;
1435
1632
  if (sourceFilePath === sourceComponentsPath || sourceFilePath.startsWith(componentsPrefix)) {
1436
- const relativeToComponents = path4.relative(
1633
+ const relativeToComponents = path5.relative(
1437
1634
  sourceComponentsPath,
1438
1635
  sourceFilePath
1439
1636
  );
1440
- return path4.join(targetComponentsPath, relativeToComponents);
1637
+ return path5.join(targetComponentsPath, relativeToComponents);
1441
1638
  }
1442
- const relativeToExport = path4.relative(exportRoot, sourceFilePath);
1443
- if (relativeToExport.startsWith("..") || path4.isAbsolute(relativeToExport)) {
1639
+ const relativeToExport = path5.relative(exportRoot, sourceFilePath);
1640
+ if (relativeToExport.startsWith("..") || path5.isAbsolute(relativeToExport)) {
1444
1641
  throw new Error(`Source file is outside export root: ${sourceFilePath}`);
1445
1642
  }
1446
- return path4.join(projectRoot, relativeToExport);
1643
+ return path5.join(projectRoot, relativeToExport);
1447
1644
  }
1448
1645
  async function copyPageFromExport(input) {
1449
1646
  const normalizedOriginSlug = normalizeSlug2(input.originPageSlug);
@@ -1454,7 +1651,7 @@ async function copyPageFromExport(input) {
1454
1651
  const { exportId, exportPath } = await resolveSingleExportDirectory(
1455
1652
  input.exportsRoot
1456
1653
  );
1457
- const manifestPath = path4.join(
1654
+ const manifestPath = path5.join(
1458
1655
  input.exportsRoot,
1459
1656
  `${exportId}.manifest.json`
1460
1657
  );
@@ -1477,8 +1674,8 @@ async function copyPageFromExport(input) {
1477
1674
  `Page not found in manifest for slug: ${normalizedOriginSlug}`
1478
1675
  );
1479
1676
  }
1480
- const sourcePagePath = path4.join(exportPath, page.pagePath);
1481
- const sourceComponentsPath = path4.join(exportPath, page.componentsPath);
1677
+ const sourcePagePath = path5.join(exportPath, page.pagePath);
1678
+ const sourceComponentsPath = path5.join(exportPath, page.componentsPath);
1482
1679
  const sourcePageStats = await stat3(sourcePagePath).catch(() => null);
1483
1680
  if (!sourcePageStats?.isFile()) {
1484
1681
  throw new Error(`Source page file not found: ${sourcePagePath}`);
@@ -1498,8 +1695,8 @@ async function copyPageFromExport(input) {
1498
1695
  pageType: page.pageType,
1499
1696
  slug: normalizedActualSlug
1500
1697
  });
1501
- const targetPagePath = path4.join(input.projectRoot, targetPaths.pagePath);
1502
- const targetComponentsPath = path4.join(
1698
+ const targetPagePath = path5.join(input.projectRoot, targetPaths.pagePath);
1699
+ const targetComponentsPath = path5.join(
1503
1700
  input.projectRoot,
1504
1701
  targetPaths.componentsPath
1505
1702
  );
@@ -1545,7 +1742,7 @@ async function copyPageFromExport(input) {
1545
1742
  const sourceRelative = toProjectRelative(exportPath, sourceFilePath);
1546
1743
  const targetRelative = toProjectRelative(input.projectRoot, targetFilePath);
1547
1744
  if (!targetBuffer) {
1548
- await ensureDir(path4.dirname(targetFilePath));
1745
+ await ensureDir(path5.dirname(targetFilePath));
1549
1746
  await writeFile2(targetFilePath, plannedSourceBuffer);
1550
1747
  newFiles.push({
1551
1748
  sourcePath: sourceRelative,
@@ -1574,8 +1771,8 @@ async function copyPageFromExport(input) {
1574
1771
  isBinary
1575
1772
  });
1576
1773
  }
1577
- const exportPackageJsonPath = path4.join(exportPath, "package.json");
1578
- const userPackageJsonPath = path4.join(input.projectRoot, "package.json");
1774
+ const exportPackageJsonPath = path5.join(exportPath, "package.json");
1775
+ const userPackageJsonPath = path5.join(input.projectRoot, "package.json");
1579
1776
  if (!await fileExists2(exportPackageJsonPath)) {
1580
1777
  throw new Error(`Export package.json not found: ${exportPackageJsonPath}`);
1581
1778
  }
@@ -1781,7 +1978,7 @@ function isExportPage(value) {
1781
1978
  return typeof maybe.id === "string" && (typeof title === "string" || typeof title === "undefined") && typeof maybe.slug === "string" && typeof maybe.pageType === "string" && maybe.isReadyToExport === true && typeof maybe.pagePath === "string" && typeof maybe.componentsPath === "string" && isExportPageManifest(maybe.manifest);
1782
1979
  }
1783
1980
  function buildManifestPath(exportsRoot, exportId) {
1784
- return path5.join(exportsRoot, `${exportId}.manifest.json`);
1981
+ return path6.join(exportsRoot, `${exportId}.manifest.json`);
1785
1982
  }
1786
1983
  function parseExportManifest(value) {
1787
1984
  if (!value || typeof value !== "object") {
@@ -1825,9 +2022,9 @@ var ProjectSyncService = class {
1825
2022
  this.projectRoot = options.projectRoot;
1826
2023
  this.targetProjectRoot = options.targetProjectRoot;
1827
2024
  this.provider = options.provider;
1828
- this.primeUiRoot = path5.join(this.projectRoot, ".primeui");
1829
- this.tempRoot = path5.join(this.primeUiRoot, "temp");
1830
- this.exportsRoot = path5.join(this.tempRoot, "exports");
2025
+ this.primeUiRoot = path6.join(this.projectRoot, ".primeui");
2026
+ this.tempRoot = path6.join(this.primeUiRoot, "temp");
2027
+ this.exportsRoot = path6.join(this.tempRoot, "exports");
1831
2028
  }
1832
2029
  async getProjectInfo() {
1833
2030
  await this.ensureTempLayout();
@@ -1852,8 +2049,8 @@ var ProjectSyncService = class {
1852
2049
  if (!selected) {
1853
2050
  throw new Error(`Export not found: ${id}`);
1854
2051
  }
1855
- const targetZipPath = path5.join(this.exportsRoot, `${id}.zip`);
1856
- const targetProjectPath = path5.join(this.exportsRoot, id);
2052
+ const targetZipPath = path6.join(this.exportsRoot, `${id}.zip`);
2053
+ const targetProjectPath = path6.join(this.exportsRoot, id);
1857
2054
  const manifestPath = buildManifestPath(this.exportsRoot, id);
1858
2055
  let manifest;
1859
2056
  try {
@@ -1906,6 +2103,47 @@ var ProjectSyncService = class {
1906
2103
  await resetDir(this.tempRoot);
1907
2104
  await ensureDir(this.exportsRoot);
1908
2105
  }
2106
+ async healthCheck() {
2107
+ return {
2108
+ status: "ok",
2109
+ runtime: {
2110
+ cwd: process.cwd(),
2111
+ initCwd: process.env.INIT_CWD?.trim() || null,
2112
+ pwd: process.env.PWD?.trim() || null,
2113
+ searchRoots: [process.cwd()],
2114
+ rootsListFallbackCwds: []
2115
+ },
2116
+ config: {
2117
+ found: true,
2118
+ projectRoot: this.projectRoot,
2119
+ projectConfigPath: path6.join(this.projectRoot, ".primeui", "project.json"),
2120
+ targetProjectPath: path6.relative(
2121
+ this.projectRoot,
2122
+ this.targetProjectRoot
2123
+ ).startsWith(".") ? "./" : `./${path6.relative(this.projectRoot, this.targetProjectRoot)}`,
2124
+ targetProjectRoot: this.targetProjectRoot,
2125
+ source: process.env.PRIMEUI_PROJECT_ROOT?.trim() ? "env.PRIMEUI_PROJECT_ROOT" : "search",
2126
+ error: null
2127
+ },
2128
+ options: [
2129
+ {
2130
+ key: "PRIMEUI_PROJECT_ROOT",
2131
+ description: "Optional absolute root containing .primeui/project.json. Use as explicit override.",
2132
+ currentValue: process.env.PRIMEUI_PROJECT_ROOT?.trim() || null
2133
+ },
2134
+ {
2135
+ key: "PRIMEUI_API_BASE_URL",
2136
+ description: "Optional PrimeUI API base URL override.",
2137
+ currentValue: process.env.PRIMEUI_API_BASE_URL?.trim() || null
2138
+ },
2139
+ {
2140
+ key: "PRIMEUI_API_KEY",
2141
+ description: "Optional API key override. If missing, MCP uses apiKey from .primeui/project.json.",
2142
+ currentValue: process.env.PRIMEUI_API_KEY?.trim() ? "set" : null
2143
+ }
2144
+ ]
2145
+ };
2146
+ }
1909
2147
  async ensureTempLayout() {
1910
2148
  await ensureDir(this.primeUiRoot);
1911
2149
  await ensureDir(this.tempRoot);
@@ -1916,7 +2154,7 @@ var ProjectSyncService = class {
1916
2154
  // src/sources/api-provider.ts
1917
2155
  import { createWriteStream } from "fs";
1918
2156
  import { unlink } from "fs/promises";
1919
- import path6 from "path";
2157
+ import path7 from "path";
1920
2158
  import { Readable, Transform } from "stream";
1921
2159
  import { pipeline } from "stream/promises";
1922
2160
  import { z as z3 } from "zod";
@@ -2140,7 +2378,7 @@ var ApiProjectDataProvider = class {
2140
2378
  if (!response.body) {
2141
2379
  throw new PrimeUiApiContractError(endpoint, "response body is empty");
2142
2380
  }
2143
- await ensureDir(path6.dirname(destinationPath));
2381
+ await ensureDir(path7.dirname(destinationPath));
2144
2382
  const zipStream = Readable.fromWeb(
2145
2383
  response.body
2146
2384
  );
@@ -2230,17 +2468,22 @@ var ApiProjectDataProvider = class {
2230
2468
  function getProjectConfigFallbackCwds() {
2231
2469
  return [process.env.INIT_CWD, process.env.PWD].map((value) => value?.trim()).filter((value) => Boolean(value));
2232
2470
  }
2233
- async function createProjectSyncService() {
2471
+ async function createProjectSyncService(rootsFallbackCwds) {
2234
2472
  const resolvedProjectConfig = await resolvePrimeUiProjectConfig({
2235
2473
  cwd: process.cwd(),
2236
2474
  projectRootFromEnv: process.env.PRIMEUI_PROJECT_ROOT,
2237
- fallbackCwds: getProjectConfigFallbackCwds()
2475
+ fallbackCwds: [...getProjectConfigFallbackCwds(), ...rootsFallbackCwds]
2238
2476
  });
2239
2477
  const projectRoot = resolvedProjectConfig.projectRoot;
2240
- const targetProjectRoot = path7.resolve(
2478
+ const targetProjectRoot = path8.resolve(
2241
2479
  projectRoot,
2242
2480
  resolvedProjectConfig.projectConfig.targetProjectPath
2243
2481
  );
2482
+ console.error("[primeui-mcp] RESOLVED_PROJECT_ROOT:", projectRoot);
2483
+ console.error(
2484
+ "[primeui-mcp] RESOLVED_PROJECT_CONFIG:",
2485
+ resolvedProjectConfig.projectConfigPath
2486
+ );
2244
2487
  const apiKey = await resolvePrimeUiApiKey({
2245
2488
  projectConfig: resolvedProjectConfig.projectConfig,
2246
2489
  apiKeyFromEnv: process.env.PRIMEUI_API_KEY
@@ -2256,8 +2499,11 @@ async function createProjectSyncService() {
2256
2499
  });
2257
2500
  }
2258
2501
  var LazyProjectSyncSource = class {
2502
+ constructor(getRootsFallbackCwds) {
2503
+ this.getRootsFallbackCwds = getRootsFallbackCwds;
2504
+ }
2259
2505
  async withService(operation) {
2260
- const service = await createProjectSyncService();
2506
+ const service = await createProjectSyncService(this.getRootsFallbackCwds());
2261
2507
  return operation(service);
2262
2508
  }
2263
2509
  async getProjectInfo() {
@@ -2283,14 +2529,69 @@ var LazyProjectSyncSource = class {
2283
2529
  async clearTemp() {
2284
2530
  return this.withService((service) => service.clearTemp());
2285
2531
  }
2532
+ async healthCheck() {
2533
+ const rootsFallbackCwds = this.getRootsFallbackCwds();
2534
+ const result = await runPrimeUiHealthCheck({
2535
+ cwd: process.cwd(),
2536
+ initCwd: process.env.INIT_CWD,
2537
+ pwd: process.env.PWD,
2538
+ projectRootFromEnv: process.env.PRIMEUI_PROJECT_ROOT,
2539
+ apiBaseUrlFromEnv: process.env.PRIMEUI_API_BASE_URL,
2540
+ apiKeyFromEnv: process.env.PRIMEUI_API_KEY,
2541
+ fallbackCwds: [...getProjectConfigFallbackCwds(), ...rootsFallbackCwds],
2542
+ rootsListFallbackCwds: rootsFallbackCwds
2543
+ });
2544
+ if (result.config.found) {
2545
+ console.error("[primeui-mcp] RESOLVED_PROJECT_ROOT:", result.config.projectRoot);
2546
+ console.error(
2547
+ "[primeui-mcp] RESOLVED_PROJECT_CONFIG:",
2548
+ result.config.projectConfigPath
2549
+ );
2550
+ } else {
2551
+ console.error("[primeui-mcp] RESOLUTION_FAILED:", result.config.error);
2552
+ }
2553
+ return result;
2554
+ }
2286
2555
  };
2556
+ function toFileRootPath(rootUri) {
2557
+ try {
2558
+ const parsed = new URL(rootUri);
2559
+ if (parsed.protocol !== "file:") {
2560
+ return null;
2561
+ }
2562
+ return fileURLToPath(parsed);
2563
+ } catch {
2564
+ return null;
2565
+ }
2566
+ }
2567
+ async function loadClientRootFallbackCwds(server) {
2568
+ try {
2569
+ const response = await server.server.listRoots(void 0, { timeout: 1500 });
2570
+ const roots = response.roots.map((root) => root.uri);
2571
+ const resolvedRoots = roots.map((rootUri) => toFileRootPath(rootUri)).filter((rootPath) => Boolean(rootPath));
2572
+ console.error("[primeui-mcp] roots/list supported:", JSON.stringify(roots));
2573
+ console.error(
2574
+ "[primeui-mcp] roots/list file roots:",
2575
+ JSON.stringify(resolvedRoots)
2576
+ );
2577
+ return resolvedRoots;
2578
+ } catch (error) {
2579
+ const message = error instanceof Error ? error.message : String(error);
2580
+ console.error("[primeui-mcp] roots/list unavailable:", message);
2581
+ return [];
2582
+ }
2583
+ }
2287
2584
  async function main() {
2288
2585
  console.error("CWD:", process.cwd());
2289
2586
  console.error("INIT_CWD:", process.env.INIT_CWD);
2290
2587
  console.error("PWD:", process.env.PWD);
2291
- const server = createPrimeUiMcpServer(new LazyProjectSyncSource());
2588
+ let rootsFallbackCwds = [];
2589
+ const server = createPrimeUiMcpServer(
2590
+ new LazyProjectSyncSource(() => rootsFallbackCwds)
2591
+ );
2292
2592
  const transport = new StdioServerTransport();
2293
2593
  await server.connect(transport);
2594
+ rootsFallbackCwds = await loadClientRootFallbackCwds(server);
2294
2595
  }
2295
2596
  main().catch((error) => {
2296
2597
  console.error("[primeui-mcp] failed to start", error);