@imenam/mcp-documentor 1.0.74 → 1.0.76

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.
Files changed (66) hide show
  1. package/README.md +271 -259
  2. package/dist/shared/schemas/doc.schema.d.ts +1 -4
  3. package/dist/shared/schemas/doc.schema.js +1 -4
  4. package/dist/src/cli/setup-mcp.d.ts +20 -1
  5. package/dist/src/cli/setup-mcp.js +65 -6
  6. package/dist/src/core/storage-engine.d.ts +14 -15
  7. package/dist/src/core/storage-engine.js +15 -51
  8. package/dist/src/gui-worker.js +91 -34
  9. package/dist/src/index.js +99 -106
  10. package/dist/src/tools/check-links.js +1 -1
  11. package/dist/src/tools/list-projects.js +7 -5
  12. package/dist/src/version.d.ts +1 -1
  13. package/dist/src/version.js +1 -1
  14. package/gui/dist/assets/{arc-BNuZ3dSL.js → arc-CsM5OJTM.js} +1 -1
  15. package/gui/dist/assets/{architectureDiagram-3BPJPVTR-D8g3tPQx.js → architectureDiagram-3BPJPVTR-BwIu1llT.js} +1 -1
  16. package/gui/dist/assets/{blockDiagram-GPEHLZMM-limhup8_.js → blockDiagram-GPEHLZMM-BVijVnPI.js} +1 -1
  17. package/gui/dist/assets/{c4Diagram-AAUBKEIU-Ct39JMIy.js → c4Diagram-AAUBKEIU-D-rcZUZu.js} +1 -1
  18. package/gui/dist/assets/channel-DKPwwuQf.js +1 -0
  19. package/gui/dist/assets/{chunk-2J33WTMH-BkLJTezj.js → chunk-2J33WTMH-ByfTGREe.js} +1 -1
  20. package/gui/dist/assets/{chunk-4BX2VUAB-CR5NbhDR.js → chunk-4BX2VUAB-B_5oCXyz.js} +1 -1
  21. package/gui/dist/assets/{chunk-55IACEB6-CtQYMnJS.js → chunk-55IACEB6-Bc2Kq9HD.js} +1 -1
  22. package/gui/dist/assets/{chunk-727SXJPM-B2q7tUn8.js → chunk-727SXJPM-CVwmKEMQ.js} +1 -1
  23. package/gui/dist/assets/{chunk-AQP2D5EJ-Bbcnrfrp.js → chunk-AQP2D5EJ-BkMuEFxD.js} +1 -1
  24. package/gui/dist/assets/{chunk-FMBD7UC4-D8gd4zRe.js → chunk-FMBD7UC4-CU7EDl9I.js} +1 -1
  25. package/gui/dist/assets/{chunk-ND2GUHAM-DiZKzZtG.js → chunk-ND2GUHAM-1b34Dtkn.js} +1 -1
  26. package/gui/dist/assets/{chunk-QZHKN3VN-CrKx-e2_.js → chunk-QZHKN3VN-DnUfMecI.js} +1 -1
  27. package/gui/dist/assets/classDiagram-4FO5ZUOK-CnCvVGW9.js +1 -0
  28. package/gui/dist/assets/classDiagram-v2-Q7XG4LA2-CnCvVGW9.js +1 -0
  29. package/gui/dist/assets/{cose-bilkent-S5V4N54A-BDUCVkTl.js → cose-bilkent-S5V4N54A-D9OBrxKr.js} +1 -1
  30. package/gui/dist/assets/{dagre-BM42HDAG-DkoYBHXK.js → dagre-BM42HDAG-zDqwL8xs.js} +1 -1
  31. package/gui/dist/assets/{diagram-2AECGRRQ-BT1YJHon.js → diagram-2AECGRRQ-ejbP8WIj.js} +1 -1
  32. package/gui/dist/assets/{diagram-5GNKFQAL-C00r6F35.js → diagram-5GNKFQAL-Cv9WmtFO.js} +1 -1
  33. package/gui/dist/assets/{diagram-KO2AKTUF-B4wcN5a_.js → diagram-KO2AKTUF-BI0ybJiN.js} +1 -1
  34. package/gui/dist/assets/{diagram-LMA3HP47-B9FqCfel.js → diagram-LMA3HP47-DlVGttLq.js} +1 -1
  35. package/gui/dist/assets/{diagram-OG6HWLK6-COPCDzdG.js → diagram-OG6HWLK6-DjoAH-Fq.js} +1 -1
  36. package/gui/dist/assets/{erDiagram-TEJ5UH35-BGQGT2wN.js → erDiagram-TEJ5UH35-BVQHVGK4.js} +1 -1
  37. package/gui/dist/assets/{flowDiagram-I6XJVG4X-A7jKWUpi.js → flowDiagram-I6XJVG4X-oZP2Z-6T.js} +1 -1
  38. package/gui/dist/assets/{ganttDiagram-6RSMTGT7-NhFkpY-x.js → ganttDiagram-6RSMTGT7-dy4QyEOP.js} +1 -1
  39. package/gui/dist/assets/{gitGraphDiagram-PVQCEYII-z0AqyYG4.js → gitGraphDiagram-PVQCEYII-BxgLy7Ln.js} +1 -1
  40. package/gui/dist/assets/{index-Dub5OLKk.js → index-Dtv0slFQ.js} +66 -66
  41. package/gui/dist/assets/{infoDiagram-5YYISTIA-D69UR9u9.js → infoDiagram-5YYISTIA-DNOdinpR.js} +1 -1
  42. package/gui/dist/assets/{ishikawaDiagram-YF4QCWOH-DSTqIoro.js → ishikawaDiagram-YF4QCWOH-C4pQ434G.js} +1 -1
  43. package/gui/dist/assets/{journeyDiagram-JHISSGLW-CekXdcoE.js → journeyDiagram-JHISSGLW-CBrVYW94.js} +1 -1
  44. package/gui/dist/assets/{kanban-definition-UN3LZRKU-CseGvFK9.js → kanban-definition-UN3LZRKU-Df-rgiY8.js} +1 -1
  45. package/gui/dist/assets/{linear-BtVl_9_C.js → linear-B_6fRXVt.js} +1 -1
  46. package/gui/dist/assets/{mindmap-definition-RKZ34NQL-XLjQIU37.js → mindmap-definition-RKZ34NQL-CRAd_VDM.js} +1 -1
  47. package/gui/dist/assets/{pieDiagram-4H26LBE5-BcvUzWoW.js → pieDiagram-4H26LBE5-CJkmLrDI.js} +1 -1
  48. package/gui/dist/assets/{quadrantDiagram-W4KKPZXB-BzoYEmNW.js → quadrantDiagram-W4KKPZXB-D-rDEcnF.js} +1 -1
  49. package/gui/dist/assets/{requirementDiagram-4Y6WPE33-icKC1_Nd.js → requirementDiagram-4Y6WPE33-DE8MhSMe.js} +1 -1
  50. package/gui/dist/assets/{sankeyDiagram-5OEKKPKP-mi3i-2dZ.js → sankeyDiagram-5OEKKPKP-BwlxtrUs.js} +1 -1
  51. package/gui/dist/assets/{sequenceDiagram-3UESZ5HK-XrcDkRlM.js → sequenceDiagram-3UESZ5HK-B4LjBzWg.js} +1 -1
  52. package/gui/dist/assets/{stateDiagram-AJRCARHV-UXaqrCNT.js → stateDiagram-AJRCARHV-Bv72afPK.js} +1 -1
  53. package/gui/dist/assets/stateDiagram-v2-BHNVJYJU-BKJDOkkR.js +1 -0
  54. package/gui/dist/assets/{timeline-definition-PNZ67QCA-yd6607p6.js → timeline-definition-PNZ67QCA-CBxgf8iW.js} +1 -1
  55. package/gui/dist/assets/{vennDiagram-CIIHVFJN-BsRqUcIp.js → vennDiagram-CIIHVFJN-DPSdK06q.js} +1 -1
  56. package/gui/dist/assets/{wardley-L42UT6IY-BmdA7yk0.js → wardley-L42UT6IY-JIAzuoPm.js} +1 -1
  57. package/gui/dist/assets/{wardleyDiagram-YWT4CUSO-CRdmckD8.js → wardleyDiagram-YWT4CUSO-YDfaF1wT.js} +1 -1
  58. package/gui/dist/assets/{xychartDiagram-2RQKCTM6-DWpNIQY-.js → xychartDiagram-2RQKCTM6-DHlPryZX.js} +1 -1
  59. package/gui/dist/index.html +1 -1
  60. package/package.json +1 -1
  61. package/dist/src/tools/set-project.d.ts +0 -14
  62. package/dist/src/tools/set-project.js +0 -18
  63. package/gui/dist/assets/channel-uoEIgtym.js +0 -1
  64. package/gui/dist/assets/classDiagram-4FO5ZUOK-DmhYb54l.js +0 -1
  65. package/gui/dist/assets/classDiagram-v2-Q7XG4LA2-DmhYb54l.js +0 -1
  66. package/gui/dist/assets/stateDiagram-v2-BHNVJYJU-Bhdu4lv4.js +0 -1
package/dist/src/index.js CHANGED
@@ -20,7 +20,6 @@ import { searchDocs } from "./tools/search-docs.js";
20
20
  import { checkFreshness, computeFreshness } from "./tools/check-freshness.js";
21
21
  import { findAndHashSection } from "./core/section-tracker.js";
22
22
  import { toAbsolutePath } from "./core/path-resolver.js";
23
- import { setProject } from "./tools/set-project.js";
24
23
  import { listProjects } from "./tools/list-projects.js";
25
24
  import { refreshFileSnapshot } from "./tools/refresh-file-snapshot.js";
26
25
  import { getDocMetadata } from "./tools/get-doc-metadata.js";
@@ -50,8 +49,14 @@ const __dirname = path.dirname(__filename);
50
49
  // Les flags CLI sont traités avant toute initialisation (storage, sqlite, logs) :
51
50
  // ils s'exécutent dans le répertoire de l'utilisateur, pas dans un serveur MCP.
52
51
  if (process.argv.slice(2).includes("--claude-setup-mcp")) {
53
- const { runSetupMcp } = await import("./cli/setup-mcp.js");
54
- process.exit(runSetupMcp({ cwd: process.cwd(), force: process.argv.slice(2).includes("--force") }));
52
+ const argv = process.argv.slice(2);
53
+ const { runSetupMcp, parseGuiExposure } = await import("./cli/setup-mcp.js");
54
+ const parsed = parseGuiExposure(argv);
55
+ if ("error" in parsed) {
56
+ console.error(`Erreur : ${parsed.error}`);
57
+ process.exit(1);
58
+ }
59
+ process.exit(runSetupMcp({ cwd: process.cwd(), force: argv.includes("--force"), exposure: parsed.exposure }));
55
60
  }
56
61
  function findProjectRoot(startDir) {
57
62
  let current = startDir;
@@ -73,6 +78,7 @@ function getConfig() {
73
78
  }
74
79
  return {
75
80
  proxyUrl: process.env.PROXY_URL,
81
+ appPort: process.env.APP_PORT,
76
82
  version: APP_VERSION,
77
83
  };
78
84
  }
@@ -80,7 +86,9 @@ getConfig();
80
86
  const storageEngine = new StorageEngine(rootDir);
81
87
  const embeddingStore = new EmbeddingStore();
82
88
  const embeddingEngine = new EmbeddingEngine(embeddingStore);
83
- storageEngine.ensureProject(storageEngine.getConfig().currentProject);
89
+ for (const project of storageEngine.getConfig().projects) {
90
+ storageEngine.ensureProject(project);
91
+ }
84
92
  const openaiModel = process.env.MCP_DOCUMENTOR_OPENAI_MODEL || "text-embedding-3-small";
85
93
  embeddingStore.init(storageEngine.getDataDir(), openaiModel);
86
94
  const historyStore = new HistoryStore();
@@ -123,8 +131,8 @@ function handleIPCMessage(msg) {
123
131
  else if (msg.type === "GET_CONFIG") {
124
132
  handleGetConfig(msg);
125
133
  }
126
- else if (msg.type === "UPDATE_PROJECT") {
127
- handleUpdateProject(msg);
134
+ else if (msg.type === "CREATE_PROJECT") {
135
+ handleCreateProject(msg);
128
136
  }
129
137
  else if (msg.type === "LIST_PROJECTS") {
130
138
  handleListProjects(msg);
@@ -188,25 +196,45 @@ function requireProjectFromIPC(msg) {
188
196
  throw new Error("project is required for this operation");
189
197
  return project;
190
198
  }
199
+ /** Loads one project's summaries and flags each entry as stale/broken. */
200
+ async function listDocsWithFreshness(project) {
201
+ const docs = await storageEngine.listDocs(project);
202
+ for (const doc of docs) {
203
+ const full = await storageEngine.getDoc(doc.id, project);
204
+ if (full && full.tracked_files.length > 0) {
205
+ const report = computeFreshness(full.tracked_files, project);
206
+ doc.isStale = report.status === "stale" || report.status === "broken";
207
+ doc.isBroken = report.status === "broken";
208
+ }
209
+ }
210
+ return docs;
211
+ }
212
+ /**
213
+ * Tells every connected GUI that one project's docs changed. Clients showing
214
+ * that project refetch; the others ignore it. Broadcasting a full INITIAL_STATE
215
+ * here used to drag every client onto the writer's project.
216
+ */
217
+ function broadcastDocsChanged(project) {
218
+ broadcastToGUI({
219
+ type: "DOCS_CHANGED",
220
+ data: { project },
221
+ timestamp: new Date().toISOString(),
222
+ });
223
+ }
224
+ /**
225
+ * Server-side state only: the project list and server capabilities. Which
226
+ * project a client displays is that client's own business (URL + local
227
+ * preference), so no docs and no active project are pushed here.
228
+ */
191
229
  async function handleGetInitialState(msg) {
192
230
  try {
193
231
  const config = storageEngine.getConfig();
194
- const docs = await storageEngine.listDocs();
195
- for (const doc of docs) {
196
- const full = await storageEngine.getDoc(doc.id); // doc.id is now the full path
197
- if (full && full.tracked_files.length > 0) {
198
- const report = computeFreshness(full.tracked_files, config.currentProject);
199
- doc.isStale = report.status === "stale" || report.status === "broken";
200
- doc.isBroken = report.status === "broken";
201
- }
202
- }
203
232
  const bundles = storageEngine.getBundles();
204
233
  broadcastToGUI({
205
234
  type: "INITIAL_STATE",
206
235
  correlationId: msg.correlationId,
207
236
  data: {
208
237
  config,
209
- docs,
210
238
  projects: config.projects,
211
239
  bundles,
212
240
  readOnly: isReadOnly(),
@@ -228,39 +256,31 @@ async function handleGetConfig(msg) {
228
256
  timestamp: new Date().toISOString(),
229
257
  });
230
258
  }
231
- async function handleUpdateProject(msg) {
259
+ /**
260
+ * Creates an empty project. It does NOT change any server-side "active"
261
+ * project — there is none: the selector in the GUI is a per-client view.
262
+ */
263
+ async function handleCreateProject(msg) {
232
264
  try {
233
265
  const project = path.basename(msg.data.project);
234
266
  if (!project || project === "..")
235
267
  throw new Error("Invalid project name");
236
268
  storageEngine.ensureProject(project);
237
- const config = storageEngine.getConfig();
238
- config.currentProject = project;
239
- storageEngine.saveConfig(config);
240
- const docs = await storageEngine.listDocs();
241
- for (const doc of docs) {
242
- const full = await storageEngine.getDoc(doc.id);
243
- if (full && full.tracked_files.length > 0) {
244
- const report = computeFreshness(full.tracked_files, project);
245
- doc.isStale = report.status === "stale" || report.status === "broken";
246
- doc.isBroken = report.status === "broken";
247
- }
248
- }
249
269
  broadcastToGUI({
250
- type: "UPDATE_PROJECT_RESPONSE",
270
+ type: "CREATE_PROJECT_RESPONSE",
251
271
  correlationId: msg.correlationId,
252
- data: { success: true, currentProject: project },
272
+ data: { success: true, project, projects: storageEngine.getConfig().projects },
253
273
  timestamp: new Date().toISOString(),
254
274
  });
255
275
  broadcastToGUI({
256
- type: "INITIAL_STATE",
257
- data: { config: storageEngine.getConfig(), docs, projects: storageEngine.getConfig().projects },
276
+ type: "PROJECTS_CHANGED",
277
+ data: { projects: storageEngine.getConfig().projects },
258
278
  timestamp: new Date().toISOString(),
259
279
  });
260
280
  }
261
281
  catch (err) {
262
282
  broadcastToGUI({
263
- type: "UPDATE_PROJECT_RESPONSE",
283
+ type: "CREATE_PROJECT_RESPONSE",
264
284
  correlationId: msg.correlationId,
265
285
  error: err.message,
266
286
  timestamp: new Date().toISOString(),
@@ -278,16 +298,7 @@ async function handleListProjects(msg) {
278
298
  }
279
299
  async function handleListDocs(msg) {
280
300
  try {
281
- const currentProject = storageEngine.getConfig().currentProject;
282
- const docs = await storageEngine.listDocs();
283
- for (const doc of docs) {
284
- const full = await storageEngine.getDoc(doc.id);
285
- if (full && full.tracked_files.length > 0) {
286
- const report = computeFreshness(full.tracked_files, currentProject);
287
- doc.isStale = report.status === "stale" || report.status === "broken";
288
- doc.isBroken = report.status === "broken";
289
- }
290
- }
301
+ const docs = await listDocsWithFreshness(requireProjectFromIPC(msg));
291
302
  broadcastToGUI({
292
303
  type: "LIST_DOCS_RESPONSE",
293
304
  correlationId: msg.correlationId,
@@ -327,8 +338,7 @@ async function handleGetDoc(msg) {
327
338
  }
328
339
  async function handleListFoldersIPC(msg) {
329
340
  try {
330
- const project = msg.data?.project;
331
- const docs = await storageEngine.listDocs(project);
341
+ const docs = await storageEngine.listDocs(requireProjectFromIPC(msg));
332
342
  const folders = new Set();
333
343
  for (const doc of docs) {
334
344
  if (doc.folder) {
@@ -394,21 +404,7 @@ async function handleCreateDocIPC(msg) {
394
404
  data: { success: true, id },
395
405
  timestamp: new Date().toISOString(),
396
406
  });
397
- const config = storageEngine.getConfig();
398
- const docs = await storageEngine.listDocs();
399
- for (const doc of docs) {
400
- const full = await storageEngine.getDoc(doc.id);
401
- if (full && full.tracked_files.length > 0) {
402
- const report = computeFreshness(full.tracked_files, config.currentProject);
403
- doc.isStale = report.status === "stale" || report.status === "broken";
404
- doc.isBroken = report.status === "broken";
405
- }
406
- }
407
- broadcastToGUI({
408
- type: "INITIAL_STATE",
409
- data: { config, docs, projects: config.projects },
410
- timestamp: new Date().toISOString(),
411
- });
407
+ broadcastDocsChanged(project);
412
408
  }
413
409
  catch (err) {
414
410
  broadcastToGUI({
@@ -430,9 +426,10 @@ async function handleUpdateDocIPC(msg) {
430
426
  return;
431
427
  }
432
428
  try {
429
+ const project = requireProjectFromIPC(msg);
433
430
  const result = await updateDoc(storageEngine, embeddingEngine, {
434
431
  path: msg.data.id,
435
- project: requireProjectFromIPC(msg),
432
+ project,
436
433
  content: msg.data.content,
437
434
  ...(msg.data.title !== undefined && { title: msg.data.title }),
438
435
  });
@@ -453,6 +450,7 @@ async function handleUpdateDocIPC(msg) {
453
450
  data: { success: true, message },
454
451
  timestamp: new Date().toISOString(),
455
452
  });
453
+ broadcastDocsChanged(project);
456
454
  }
457
455
  catch (err) {
458
456
  broadcastToGUI({
@@ -493,21 +491,7 @@ async function handleDeleteDocIPC(msg) {
493
491
  data: { success: true, message },
494
492
  timestamp: new Date().toISOString(),
495
493
  });
496
- const config = storageEngine.getConfig();
497
- const docs = await storageEngine.listDocs();
498
- for (const doc of docs) {
499
- const full = await storageEngine.getDoc(doc.id);
500
- if (full && full.tracked_files.length > 0) {
501
- const report = computeFreshness(full.tracked_files, config.currentProject);
502
- doc.isStale = report.status === "stale" || report.status === "broken";
503
- doc.isBroken = report.status === "broken";
504
- }
505
- }
506
- broadcastToGUI({
507
- type: "INITIAL_STATE",
508
- data: { config, docs, projects: config.projects },
509
- timestamp: new Date().toISOString(),
510
- });
494
+ broadcastDocsChanged(project);
511
495
  }
512
496
  catch (err) {
513
497
  broadcastToGUI({
@@ -603,9 +587,9 @@ async function handleSearchDocs(msg) {
603
587
  });
604
588
  }
605
589
  else {
606
- const currentProject = storageEngine.getConfig().currentProject;
607
- results = await embeddingEngine.search(query, currentProject, top_k || 5, async (docPath) => {
608
- const doc = await storageEngine.getDoc(docPath);
590
+ const project = requireProjectFromIPC(msg);
591
+ results = await embeddingEngine.search(query, project, top_k || 5, async (docPath) => {
592
+ const doc = await storageEngine.getDoc(docPath, project);
609
593
  if (!doc)
610
594
  return null;
611
595
  return { title: doc.title, content: doc.content };
@@ -832,9 +816,13 @@ function startGuiWorker() {
832
816
  });
833
817
  return child;
834
818
  }
819
+ /** La GUI a besoin soit d'un proxy, soit d'un port local (mode standalone). */
820
+ function guiIsConfigured() {
821
+ return !!(process.env.PROXY_URL?.trim() || process.env.APP_PORT?.trim());
822
+ }
835
823
  function launchGUI() {
836
- if (!process.env.PROXY_URL) {
837
- logger.info('[MASTER] PROXY_URL non défini — GUI désactivée.');
824
+ if (!guiIsConfigured()) {
825
+ logger.info('[MASTER] Ni PROXY_URL ni APP_PORT défini — GUI désactivée.');
838
826
  guiStatus = "disabled";
839
827
  return;
840
828
  }
@@ -842,10 +830,10 @@ function launchGUI() {
842
830
  startGuiWorker();
843
831
  }
844
832
  async function reconnectGui(args) {
845
- getConfig(); // relit le .env au cas où PROXY_URL aurait été ajouté après le démarrage
846
- if (!process.env.PROXY_URL) {
833
+ getConfig(); // relit le .env au cas où PROXY_URL/APP_PORT aurait été ajouté après le démarrage
834
+ if (!guiIsConfigured()) {
847
835
  return {
848
- content: [{ type: "text", text: "Error: PROXY_URL is not set — the GUI is disabled. Add PROXY_URL to the .env file, then call reconnect_gui again." }],
836
+ content: [{ type: "text", text: "Error: neither PROXY_URL nor APP_PORT is set — the GUI is disabled. Add PROXY_URL (to expose the GUI through the proxy) or APP_PORT (to serve it directly on localhost) to the .env file, then call reconnect_gui again." }],
849
837
  isError: true,
850
838
  };
851
839
  }
@@ -854,9 +842,9 @@ async function reconnectGui(args) {
854
842
  const force = args?.force === true;
855
843
  if (alive && !force) {
856
844
  if (guiStatus === "starting") {
857
- return { content: [{ type: "text", text: "GUI worker is already starting (proxy registration in progress). Wait a few seconds, then call reconnect_gui again if needed." }] };
845
+ return { content: [{ type: "text", text: "GUI worker is already starting. Wait a few seconds, then call reconnect_gui again if needed." }] };
858
846
  }
859
- return { content: [{ type: "text", text: `GUI is already registered with the proxy${guiUrl ? ` at ${guiUrl}` : ""}. Nothing to do (use force=true to restart it anyway).` }] };
847
+ return { content: [{ type: "text", text: `GUI is already running${guiUrl ? ` at ${guiUrl}` : ""}. Nothing to do (use force=true to restart it anyway).` }] };
860
848
  }
861
849
  logger.info(`[MASTER] reconnect_gui: ${alive ? "restarting (force)" : "relaunching"} GUI worker...`);
862
850
  const result = await new Promise((resolve) => {
@@ -867,20 +855,24 @@ async function reconnectGui(args) {
867
855
  guiStartWaiter = (r) => { cleanup(); resolve(r); };
868
856
  newChild.on("exit", onExit);
869
857
  });
858
+ const proxyUrl = process.env.PROXY_URL?.trim();
870
859
  if (result.outcome === "ready") {
871
- return { content: [{ type: "text", text: `GUI reconnected and registered with the proxy. URL: ${result.url}` }] };
860
+ return { content: [{ type: "text", text: `GUI reconnected. URL: ${result.url}` }] };
872
861
  }
873
862
  if (result.outcome === "already_running") {
874
- return { content: [{ type: "text", text: `GUI is already registered by another MCP instance. URL: ${result.url ?? "unknown"}` }] };
863
+ return { content: [{ type: "text", text: `GUI is already served by another MCP instance. URL: ${result.url ?? "unknown"}` }] };
875
864
  }
876
865
  if (result.outcome === "exited") {
866
+ const hint = proxyUrl
867
+ ? `The proxy server is probably still unreachable at ${proxyUrl}. Start the proxy server, then call reconnect_gui again.`
868
+ : `Port ${process.env.APP_PORT} is probably already taken by another process. Free it or set another APP_PORT, then call reconnect_gui again.`;
877
869
  return {
878
- content: [{ type: "text", text: `GUI worker exited before completing registration (code=${result.code}). The proxy server is probably still unreachable at ${process.env.PROXY_URL}. Start the proxy server, then call reconnect_gui again.` }],
870
+ content: [{ type: "text", text: `GUI worker exited before it was ready (code=${result.code}). ${hint}` }],
879
871
  isError: true,
880
872
  };
881
873
  }
882
874
  return {
883
- content: [{ type: "text", text: `GUI worker started but did not confirm proxy registration within 15s. Check the logs (${resolveLogDir(rootDir)}), then retry.` }],
875
+ content: [{ type: "text", text: `GUI worker started but did not confirm it was ready within 15s. Check the logs (${resolveLogDir(rootDir)}), then retry.` }],
884
876
  isError: true,
885
877
  };
886
878
  }
@@ -1059,20 +1051,9 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
1059
1051
  required: [],
1060
1052
  },
1061
1053
  },
1062
- {
1063
- name: "set_project",
1064
- description: "Switch the active project and update it as the default for all subsequent MCP tool operations. If the project name does not exist yet, it is created automatically. Also controls what the GUI displays.",
1065
- inputSchema: {
1066
- type: "object",
1067
- properties: {
1068
- project: { type: "string", description: "Project name to switch to (created if it does not exist)" },
1069
- },
1070
- required: ["project"],
1071
- },
1072
- },
1073
1054
  {
1074
1055
  name: "list_projects",
1075
- description: "List all available documentation projects and show which one is currently active.",
1056
+ description: "List all available documentation projects. There is no active project: every tool call names the project it targets.",
1076
1057
  inputSchema: {
1077
1058
  type: "object",
1078
1059
  properties: {},
@@ -1154,9 +1135,9 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
1154
1135
  inputSchema: {
1155
1136
  type: "object",
1156
1137
  properties: {
1157
- project: { type: "string", description: "Project to check (defaults to current project)" },
1138
+ project: { type: "string", description: "Project whose entries are checked. Required — never defaulted to a current project." },
1158
1139
  },
1159
- required: [],
1140
+ required: ["project"],
1160
1141
  },
1161
1142
  },
1162
1143
  {
@@ -1410,7 +1391,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1410
1391
  case "check_freshness":
1411
1392
  return await checkFreshness(storageEngine, args);
1412
1393
  case "set_project":
1413
- return await setProject(storageEngine, args);
1394
+ // Removed: there is no server-side active project any more. Kept as an
1395
+ // explicit answer for agents that still remember the old tool.
1396
+ return {
1397
+ isError: true,
1398
+ content: [
1399
+ {
1400
+ type: "text",
1401
+ text: "set_project no longer exists: there is no active project. " +
1402
+ "Pass the project explicitly to every tool call (use list_projects to see them). " +
1403
+ "A project is created on demand by create_doc.",
1404
+ },
1405
+ ],
1406
+ };
1414
1407
  case "list_projects":
1415
1408
  return await listProjects(storageEngine);
1416
1409
  case "refresh_file_snapshot":
@@ -38,7 +38,7 @@ export async function checkLinks(storageEngine, args) {
38
38
  try {
39
39
  const validated = CheckLinksArgsSchema.parse(args);
40
40
  const config = storageEngine.getConfig();
41
- const project = validated.project ?? config.currentProject;
41
+ const project = validated.project;
42
42
  const knownProjects = new Set(config.projects);
43
43
  const docs = await storageEngine.listDocs(project);
44
44
  const validIds = new Set(docs.map((d) => d.id));
@@ -1,12 +1,14 @@
1
1
  export async function listProjects(storageEngine) {
2
2
  try {
3
3
  const config = storageEngine.getConfig();
4
- const lines = config.projects.map((p) => {
5
- const marker = p === config.currentProject ? " (active)" : "";
6
- return `- ${p}${marker}`;
7
- });
4
+ const lines = config.projects.map((p) => `- ${p}`);
8
5
  return {
9
- content: [{ type: "text", text: `Projects:\n\n${lines.join("\n")}\n\nCurrent project: ${config.currentProject}` }],
6
+ content: [
7
+ {
8
+ type: "text",
9
+ text: `Projects:\n\n${lines.join("\n")}\n\nThere is no active project: every tool call names the project it targets explicitly.`,
10
+ },
11
+ ],
10
12
  };
11
13
  }
12
14
  catch (err) {
@@ -1 +1 @@
1
- export declare const APP_VERSION = "1.0.74";
1
+ export declare const APP_VERSION = "1.0.76";
@@ -1,2 +1,2 @@
1
1
  // Auto-generated version file - do not edit manually
2
- export const APP_VERSION = '1.0.74';
2
+ export const APP_VERSION = '1.0.76';
@@ -1 +1 @@
1
- import{a0 as ln,a1 as an,a2 as y,a3 as tn,a4 as H,a5 as q,a6 as _,a7 as un,a8 as rn,a9 as L,aa as o,ab as B,ac as sn,ad as on,ae as fn}from"./index-Dub5OLKk.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,C,a){var O=I-l,i=D-h,n=C-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function W(l,h,I,D,v,A,C){var a=l-I,O=h-D,i=(C?A:-A)/L(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,F=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*L(fn(0,T*T*R-P*P)),j=(P*g-m*S)/R,z=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=j-F,e=z-t,r=w-F,G=p-t;return x*x+e*e>r*r+G*G&&(j=w,z=p),{cx:j,cy:z,x01:-n,y01:-d,x11:j*(v/T-1),y11:z*(v/T-1)}}function hn(){var l=cn,h=yn,I=B(0),D=null,v=gn,A=dn,C=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-an,c=A.apply(this,arguments)-an,F=un(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(F>tn-y)a.moveTo(s*H(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*H(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=F,S=F,j=C.apply(this,arguments)/2,z=j>y&&(D?+D.apply(this,arguments):L(u*u+s*s)),w=_(un(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if(z>y){var G=sn(z/u*q(j)),M=sn(z/s*q(j));(P-=G*2)>y?(G*=t?1:-1,R+=G,T-=G):(P=0,R=T=(f+c)/2),(S-=M*2)>y?(M*=t?1:-1,m+=M,g-=M):(S=0,m=g=(f+c)/2)}var J=s*H(m),K=s*q(m),N=u*H(T),Q=u*q(T);if(w>y){var U=s*H(g),V=s*q(g),X=u*H(R),Y=u*q(R),E;if(F<rn)if(E=pn(J,K,X,Y,U,V,N,Q)){var Z=J-E[0],$=K-E[1],b=U-E[0],k=V-E[1],nn=1/q(on((Z*b+$*k)/(L(Z*Z+$*$)*L(b*b+k*k)))/2),en=L(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=W(X,Y,J,K,s,x,t),r=W(U,V,N,Q,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(J,K),a.arc(0,0,s,m,g,!t)):a.moveTo(J,K),!(u>y)||!(P>y)?a.lineTo(N,Q):p>y?(e=W(N,Q,U,V,u,-p,t),r=W(J,K,X,Y,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-rn/2;return[H(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:B(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:B(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:B(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:B(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:B(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:B(+n),i):A},i.padAngle=function(n){return arguments.length?(C=typeof n=="function"?n:B(+n),i):C},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
1
+ import{a0 as ln,a1 as an,a2 as y,a3 as tn,a4 as H,a5 as q,a6 as _,a7 as un,a8 as rn,a9 as L,aa as o,ab as B,ac as sn,ad as on,ae as fn}from"./index-Dtv0slFQ.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,C,a){var O=I-l,i=D-h,n=C-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function W(l,h,I,D,v,A,C){var a=l-I,O=h-D,i=(C?A:-A)/L(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,F=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*L(fn(0,T*T*R-P*P)),j=(P*g-m*S)/R,z=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=j-F,e=z-t,r=w-F,G=p-t;return x*x+e*e>r*r+G*G&&(j=w,z=p),{cx:j,cy:z,x01:-n,y01:-d,x11:j*(v/T-1),y11:z*(v/T-1)}}function hn(){var l=cn,h=yn,I=B(0),D=null,v=gn,A=dn,C=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-an,c=A.apply(this,arguments)-an,F=un(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(F>tn-y)a.moveTo(s*H(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*H(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=F,S=F,j=C.apply(this,arguments)/2,z=j>y&&(D?+D.apply(this,arguments):L(u*u+s*s)),w=_(un(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if(z>y){var G=sn(z/u*q(j)),M=sn(z/s*q(j));(P-=G*2)>y?(G*=t?1:-1,R+=G,T-=G):(P=0,R=T=(f+c)/2),(S-=M*2)>y?(M*=t?1:-1,m+=M,g-=M):(S=0,m=g=(f+c)/2)}var J=s*H(m),K=s*q(m),N=u*H(T),Q=u*q(T);if(w>y){var U=s*H(g),V=s*q(g),X=u*H(R),Y=u*q(R),E;if(F<rn)if(E=pn(J,K,X,Y,U,V,N,Q)){var Z=J-E[0],$=K-E[1],b=U-E[0],k=V-E[1],nn=1/q(on((Z*b+$*k)/(L(Z*Z+$*$)*L(b*b+k*k)))/2),en=L(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=W(X,Y,J,K,s,x,t),r=W(U,V,N,Q,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(J,K),a.arc(0,0,s,m,g,!t)):a.moveTo(J,K),!(u>y)||!(P>y)?a.lineTo(N,Q):p>y?(e=W(N,Q,U,V,u,-p,t),r=W(J,K,X,Y,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-rn/2;return[H(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:B(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:B(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:B(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:B(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:B(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:B(+n),i):A},i.padAngle=function(n){return arguments.length?(C=typeof n=="function"?n:B(+n),i):C},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};