@imenam/mcp-documentor 1.0.23 → 1.0.25
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/README.md +28 -12
- package/dist/shared/schemas/doc.schema.d.ts +1 -0
- package/dist/shared/schemas/doc.schema.js +1 -0
- package/dist/src/core/storage-engine.d.ts +1 -0
- package/dist/src/core/storage-engine.js +34 -0
- package/dist/src/index.js +8 -4
- package/dist/src/tools/move-doc.js +5 -1
- package/dist/src/tools/update-doc.js +5 -0
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,12 +6,13 @@ Connect it to Cursor, Claude Desktop, or any MCP-compatible agent to let your AI
|
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
- **Create & update docs** — write markdown documentation entries linked to source files
|
|
9
|
+
- **Create & update docs** — write markdown documentation entries linked to source files; prefer partial edits via `patch_doc`
|
|
10
10
|
- **Staleness tracking** — automatically detects when tracked files have changed since the last doc update
|
|
11
11
|
- **Semantic search** — find relevant docs by meaning (requires an OpenAI API key)
|
|
12
|
-
- **Multi-project support** — organize docs into separate projects
|
|
13
|
-
- **GUI** — built-in web interface
|
|
12
|
+
- **Multi-project support** — organize docs into separate projects; MCP tools work across projects; `set_project` only changes what the GUI shows
|
|
13
|
+
- **GUI** — built-in web interface when a proxy is configured (`PROXY_URL`); without it, the GUI worker is not started
|
|
14
14
|
- **Proxy-aware** — integrates with the [proxy-setup](https://github.com/Imena/proxy-setup) for GUI exposure
|
|
15
|
+
- **Links & structure** — `check_links`, `get_doc_tree`, `move_doc`, and `git_diff_doc` for maintenance workflows
|
|
15
16
|
|
|
16
17
|
## Installation
|
|
17
18
|
|
|
@@ -25,6 +26,11 @@ Or install globally:
|
|
|
25
26
|
npm install -g @imenam/mcp-documentor
|
|
26
27
|
```
|
|
27
28
|
|
|
29
|
+
Global install exposes:
|
|
30
|
+
|
|
31
|
+
- `mcp-documentor` — MCP server
|
|
32
|
+
- `mcp-documentor-migrate` — CLI helper for migrating inline doc links (see `scripts/migrate-links.js`)
|
|
33
|
+
|
|
28
34
|
## MCP Configuration
|
|
29
35
|
|
|
30
36
|
Add to your MCP client config (e.g. Cursor `mcp.json`):
|
|
@@ -47,8 +53,12 @@ Add to your MCP client config (e.g. Cursor `mcp.json`):
|
|
|
47
53
|
|
|
48
54
|
| Variable | Required | Description |
|
|
49
55
|
|---|---|---|
|
|
50
|
-
| `OPENAI_API_KEY` | No | Enables semantic search via OpenAI embeddings |
|
|
51
|
-
| `
|
|
56
|
+
| `OPENAI_API_KEY` | No | Enables semantic search via OpenAI embeddings (`search_docs`) |
|
|
57
|
+
| `MCP_DOCUMENTOR_OPENAI_MODEL` | No | Embedding model (default: `text-embedding-3-small`) |
|
|
58
|
+
| `PROXY_URL` | No | URL of the proxy server to register and launch the GUI |
|
|
59
|
+
| `MCP_DATA_DIR` | No | Overrides the default data directory (see Data storage) |
|
|
60
|
+
|
|
61
|
+
Optional `.env` at the package root (directory containing `package.json` discovered from the installed binary) is loaded when present.
|
|
52
62
|
|
|
53
63
|
## Available Tools
|
|
54
64
|
|
|
@@ -57,17 +67,23 @@ Add to your MCP client config (e.g. Cursor `mcp.json`):
|
|
|
57
67
|
| `create_doc` | Create a new documentation entry linked to source files |
|
|
58
68
|
| `get_doc` | Retrieve a documentation entry with full content |
|
|
59
69
|
| `get_doc_metadata` | Get metadata and freshness status without loading content |
|
|
60
|
-
| `list_docs` | List
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
70
|
+
| `list_docs` | List docs with staleness indicators (optionally filter by project) |
|
|
71
|
+
| `get_doc_tree` | Return folder/doc structure for a project (or all projects) |
|
|
72
|
+
| `update_doc` | Fully replace content and/or metadata (prefer `patch_doc` for small edits) |
|
|
73
|
+
| `patch_doc` | Search-and-replace a fragment of a doc’s content |
|
|
74
|
+
| `move_doc` | Change a doc’s path (folder/name); use after renames to fix links |
|
|
75
|
+
| `delete_doc` | Permanently delete a documentation entry |
|
|
76
|
+
| `search_docs` | Semantic search over docs (natural language; requires OpenAI) |
|
|
77
|
+
| `check_freshness` | Staleness for one doc or a full project scan |
|
|
78
|
+
| `refresh_file_snapshot` | Mark tracked files as up-to-date without editing the doc |
|
|
79
|
+
| `git_diff_doc` | Git diff for tracked files vs snapshot baseline |
|
|
80
|
+
| `check_links` | Validate `related_docs`, `doc://` links, and `#heading` anchors |
|
|
65
81
|
| `list_projects` | List all documentation projects |
|
|
66
|
-
| `set_project` | Switch
|
|
82
|
+
| `set_project` | Switch the active project **for the GUI only** (not for MCP tools) |
|
|
67
83
|
|
|
68
84
|
## Data Storage
|
|
69
85
|
|
|
70
|
-
|
|
86
|
+
By default, data lives under **`.docs-data`** next to the resolved package root (the folder that contains `package.json` for the running install). Set **`MCP_DATA_DIR`** to use another directory. Contents include projects, SQLite/embeddings, and logs under `.docs-data/logs`. This tree is **not** part of the npm package; it is created at runtime.
|
|
71
87
|
|
|
72
88
|
## License
|
|
73
89
|
|
|
@@ -60,6 +60,7 @@ export declare const CreateDocArgsSchema: z.ZodObject<{
|
|
|
60
60
|
}, z.core.$strip>;
|
|
61
61
|
export declare const UpdateDocArgsSchema: z.ZodObject<{
|
|
62
62
|
path: z.ZodString;
|
|
63
|
+
title: z.ZodOptional<z.ZodString>;
|
|
63
64
|
content: z.ZodOptional<z.ZodString>;
|
|
64
65
|
tracked_files: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
65
66
|
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -32,6 +32,7 @@ export const CreateDocArgsSchema = z.object({
|
|
|
32
32
|
});
|
|
33
33
|
export const UpdateDocArgsSchema = z.object({
|
|
34
34
|
path: z.string().describe("Path of the documentation entry to update, e.g. 'backend/auth/authentication' or 'authentication' for root docs"),
|
|
35
|
+
title: z.string().optional().describe("Title of the documentation entry"),
|
|
35
36
|
content: z.string().optional().describe("New content (replaces existing)"),
|
|
36
37
|
tracked_files: z.array(z.string()).optional().describe("New list of file paths (replaces existing)"),
|
|
37
38
|
tags: z.array(z.string()).optional().describe("New tags (replaces existing)"),
|
|
@@ -25,5 +25,6 @@ export declare class StorageEngine {
|
|
|
25
25
|
} | null>;
|
|
26
26
|
deleteDoc(id: string, project?: string): Promise<boolean>;
|
|
27
27
|
moveDoc(oldId: string, newId: string, project?: string): Promise<boolean>;
|
|
28
|
+
rewriteLinks(oldId: string, newId: string, project?: string): Promise<string[]>;
|
|
28
29
|
}
|
|
29
30
|
export {};
|
|
@@ -3,6 +3,9 @@ import path from "path";
|
|
|
3
3
|
import matter from "gray-matter";
|
|
4
4
|
import { createLogger } from "@imenam/mcp-gui-interface";
|
|
5
5
|
const logger = createLogger("StorageEngine");
|
|
6
|
+
function escapeRegex(s) {
|
|
7
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8
|
+
}
|
|
6
9
|
export class StorageEngine {
|
|
7
10
|
rootDir;
|
|
8
11
|
constructor(rootDir) {
|
|
@@ -188,4 +191,35 @@ export class StorageEngine {
|
|
|
188
191
|
logger.info(`Doc moved: ${oldFilePath} → ${newFilePath}`);
|
|
189
192
|
return true;
|
|
190
193
|
}
|
|
194
|
+
async rewriteLinks(oldId, newId, project) {
|
|
195
|
+
const docs = await this.listDocs(project);
|
|
196
|
+
const updated = [];
|
|
197
|
+
for (const summary of docs) {
|
|
198
|
+
if (summary.id === newId)
|
|
199
|
+
continue;
|
|
200
|
+
const doc = await this.getDoc(summary.id, project);
|
|
201
|
+
if (!doc)
|
|
202
|
+
continue;
|
|
203
|
+
let changed = false;
|
|
204
|
+
const newContent = doc.content.replace(new RegExp(`doc://${escapeRegex(oldId)}`, "g"), `doc://${newId}`);
|
|
205
|
+
if (newContent !== doc.content) {
|
|
206
|
+
doc.content = newContent;
|
|
207
|
+
changed = true;
|
|
208
|
+
}
|
|
209
|
+
const newRelated = doc.related_docs.map((ref) => {
|
|
210
|
+
if (ref === oldId) {
|
|
211
|
+
changed = true;
|
|
212
|
+
return newId;
|
|
213
|
+
}
|
|
214
|
+
return ref;
|
|
215
|
+
});
|
|
216
|
+
doc.related_docs = newRelated;
|
|
217
|
+
if (changed) {
|
|
218
|
+
doc.updatedAt = new Date().toISOString();
|
|
219
|
+
await this.saveDoc(doc, project);
|
|
220
|
+
updated.push(summary.id);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return updated;
|
|
224
|
+
}
|
|
191
225
|
}
|
package/dist/src/index.js
CHANGED
|
@@ -404,7 +404,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
404
404
|
items: { type: "string" },
|
|
405
405
|
description: "IDs of related documentation entries",
|
|
406
406
|
},
|
|
407
|
-
project: { type: "string", description: "Project to create the doc in (defaults to current project if omitted)" },
|
|
407
|
+
project: { type: "string", description: "Project to create the doc in (defaults to current project if omitted). If the project name does not exist yet, it will be created automatically." },
|
|
408
408
|
},
|
|
409
409
|
required: ["title", "content"],
|
|
410
410
|
},
|
|
@@ -437,6 +437,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
437
437
|
type: "object",
|
|
438
438
|
properties: {
|
|
439
439
|
path: { type: "string", description: "Path of the documentation entry to update, e.g. 'backend/auth/authentication' or 'authentication' for root docs" },
|
|
440
|
+
title: {
|
|
441
|
+
type: "string",
|
|
442
|
+
description: "New display title stored in frontmatter. Does not rename the file or change the document path — use move_doc to relocate or rename on disk.",
|
|
443
|
+
},
|
|
440
444
|
content: { type: "string", description: "New content (replaces existing). Supports [text](doc://target-doc-path) to link inline to other documents, and [text](#heading-slug) to link to headings within this document. For code blocks, always specify a language identifier for syntax highlighting, e.g. ```typescript, ```python, ```bash, ```json, ```yaml, ```sql." },
|
|
441
445
|
tracked_files: {
|
|
442
446
|
type: "array",
|
|
@@ -459,7 +463,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
459
463
|
},
|
|
460
464
|
{
|
|
461
465
|
name: "move_doc",
|
|
462
|
-
description: "Move a documentation entry to a new path (changes its folder and/or name). This is the only way to relocate a document — update_doc does not change the location.
|
|
466
|
+
description: "Move a documentation entry to a new path (changes its folder and/or name). All existing doc:// links and related_docs references pointing to the old path are automatically rewritten across the project. This is the only way to relocate a document — update_doc does not change the location.",
|
|
463
467
|
inputSchema: {
|
|
464
468
|
type: "object",
|
|
465
469
|
properties: {
|
|
@@ -495,11 +499,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
495
499
|
},
|
|
496
500
|
{
|
|
497
501
|
name: "set_project",
|
|
498
|
-
description: "Switch the active project
|
|
502
|
+
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.",
|
|
499
503
|
inputSchema: {
|
|
500
504
|
type: "object",
|
|
501
505
|
properties: {
|
|
502
|
-
project: { type: "string", description: "Project name to switch to" },
|
|
506
|
+
project: { type: "string", description: "Project name to switch to (created if it does not exist)" },
|
|
503
507
|
},
|
|
504
508
|
required: ["project"],
|
|
505
509
|
},
|
|
@@ -18,11 +18,15 @@ export async function moveDoc(storageEngine, embeddingEngine, args) {
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
await embeddingEngine.moveEmbedding(validated.path, validated.new_path, resolvedProject);
|
|
21
|
+
const rewritten = await storageEngine.rewriteLinks(validated.path, validated.new_path, resolvedProject);
|
|
22
|
+
const rewriteNote = rewritten.length > 0
|
|
23
|
+
? `\n\nLiens mis à jour automatiquement dans ${rewritten.length} doc(s) :\n${rewritten.map((id) => ` - ${id}`).join("\n")}`
|
|
24
|
+
: "\n\nAucune référence existante à mettre à jour.";
|
|
21
25
|
return {
|
|
22
26
|
content: [
|
|
23
27
|
{
|
|
24
28
|
type: "text",
|
|
25
|
-
text: `Documentation entry moved successfully.\nFrom: "${validated.path}"\nTo: "${validated.new_path}"
|
|
29
|
+
text: `Documentation entry moved successfully.\nFrom: "${validated.path}"\nTo: "${validated.new_path}"${rewriteNote}`,
|
|
26
30
|
},
|
|
27
31
|
],
|
|
28
32
|
};
|
|
@@ -24,6 +24,9 @@ export async function updateDoc(storageEngine, embeddingEngine, args) {
|
|
|
24
24
|
return { isError: true, content: [{ type: "text", text: `Documentation entry "${validated.path}" not found in any project.` }] };
|
|
25
25
|
}
|
|
26
26
|
const { doc, project: resolvedProject } = resolved;
|
|
27
|
+
if (validated.title !== undefined) {
|
|
28
|
+
doc.title = validated.title;
|
|
29
|
+
}
|
|
27
30
|
if (validated.content !== undefined) {
|
|
28
31
|
doc.content = validated.content;
|
|
29
32
|
}
|
|
@@ -45,6 +48,8 @@ export async function updateDoc(storageEngine, embeddingEngine, args) {
|
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
const changes = [];
|
|
51
|
+
if (validated.title !== undefined)
|
|
52
|
+
changes.push(`title (${doc.title})`);
|
|
48
53
|
if (validated.content !== undefined)
|
|
49
54
|
changes.push("content");
|
|
50
55
|
if (validated.tracked_files !== undefined)
|
package/dist/src/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const APP_VERSION = "1.0.
|
|
1
|
+
export declare const APP_VERSION = "1.0.25";
|
package/dist/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated version file - do not edit manually
|
|
2
|
-
export const APP_VERSION = '1.0.
|
|
2
|
+
export const APP_VERSION = '1.0.25';
|