@khoinguyen2002/doc-mcp 1.0.3 → 1.0.4
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/mcp-server.js +13 -24
- package/dist/tools/driveTools.d.ts.map +1 -1
- package/dist/tools/driveTools.js +6 -0
- package/dist/tools/knowledgeTools.d.ts +9 -0
- package/dist/tools/knowledgeTools.d.ts.map +1 -1
- package/dist/tools/knowledgeTools.js +11 -5
- package/package.json +1 -1
- package/src/mcp-server.ts +18 -32
- package/src/tools/driveTools.ts +7 -0
- package/src/tools/knowledgeTools.ts +17 -12
package/dist/mcp-server.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
import { listDriveFiles, readDriveDocument
|
|
6
|
-
import {
|
|
5
|
+
import { listDriveFiles, readDriveDocument } from "./tools/driveTools.js";
|
|
6
|
+
import { searchKnowledge } from "./tools/knowledgeTools.js";
|
|
7
7
|
import { config } from "./config.js";
|
|
8
8
|
const DRIVE_FOLDER_ID = config.DOC_MCP_DRIVE_FOLDER_ID;
|
|
9
9
|
if (!DRIVE_FOLDER_ID) {
|
|
@@ -12,7 +12,7 @@ if (!DRIVE_FOLDER_ID) {
|
|
|
12
12
|
}
|
|
13
13
|
const server = new McpServer({
|
|
14
14
|
name: "doc-agent",
|
|
15
|
-
version: "1.0.
|
|
15
|
+
version: "1.0.4",
|
|
16
16
|
});
|
|
17
17
|
// Register tools
|
|
18
18
|
server.registerTool("list_drive_files", {
|
|
@@ -40,11 +40,17 @@ server.registerTool("list_drive_files", {
|
|
|
40
40
|
};
|
|
41
41
|
});
|
|
42
42
|
server.registerTool("read_drive_document", {
|
|
43
|
-
description: "Read the content of a specific Google Drive document.
|
|
43
|
+
description: "Read the content of a specific Google Drive document. You can use the 'offset' parameter (obtained from search_knowledge) to read a specific chunk of text.",
|
|
44
44
|
inputSchema: {
|
|
45
45
|
fileId: z.string().describe("The Google Drive file ID to read"),
|
|
46
|
-
offset: z
|
|
47
|
-
|
|
46
|
+
offset: z
|
|
47
|
+
.number()
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("Starting character index (default: 0)"),
|
|
50
|
+
limit: z
|
|
51
|
+
.number()
|
|
52
|
+
.optional()
|
|
53
|
+
.describe("Maximum number of characters to return (default: 10000)"),
|
|
48
54
|
},
|
|
49
55
|
}, async ({ fileId, offset, limit }) => {
|
|
50
56
|
const res = await readDriveDocument(fileId, offset, limit);
|
|
@@ -58,25 +64,8 @@ server.registerTool("read_drive_document", {
|
|
|
58
64
|
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
59
65
|
};
|
|
60
66
|
});
|
|
61
|
-
server.registerTool("save_agent_note", {
|
|
62
|
-
description: "Save an agent note, thought, or summary directly into the vector memory.",
|
|
63
|
-
inputSchema: {
|
|
64
|
-
content: z.string().describe("The note or knowledge content to store"),
|
|
65
|
-
},
|
|
66
|
-
}, async ({ content }) => {
|
|
67
|
-
const res = await saveAgentNote(content);
|
|
68
|
-
if (!res.success) {
|
|
69
|
-
return {
|
|
70
|
-
content: [{ type: "text", text: `Error: ${res.error}` }],
|
|
71
|
-
isError: true,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
return {
|
|
75
|
-
content: [{ type: "text", text: res.message || "Saved successfully" }],
|
|
76
|
-
};
|
|
77
|
-
});
|
|
78
67
|
server.registerTool("search_knowledge", {
|
|
79
|
-
description: "Search the folder's vector memory for relevant context or knowledge.",
|
|
68
|
+
description: "Search the folder's vector memory for relevant context or knowledge. Returns structured JSON array of matching chunks.",
|
|
80
69
|
inputSchema: {
|
|
81
70
|
query: z.string().describe("The search query"),
|
|
82
71
|
topK: z
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"driveTools.d.ts","sourceRoot":"","sources":["../../src/tools/driveTools.ts"],"names":[],"mappings":"AAiCA,wBAAsB,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM;;;;;;;;GAoC7E;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;
|
|
1
|
+
{"version":3,"file":"driveTools.d.ts","sourceRoot":"","sources":["../../src/tools/driveTools.ts"],"names":[],"mappings":"AAiCA,wBAAsB,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM;;;;;;;;GAoC7E;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;GAoDxE;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAU,EAAE,KAAK,GAAE,MAAc;;;;;;;;;;;;;;;;;GAsDhG;AAED,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM;;;;;;GAqDrD"}
|
package/dist/tools/driveTools.js
CHANGED
|
@@ -79,12 +79,18 @@ export async function syncSingleDocument(fileId, folderId) {
|
|
|
79
79
|
chunkOverlap: config.CHUNK_OVERLAP,
|
|
80
80
|
});
|
|
81
81
|
const chunks = await splitter.splitText(content);
|
|
82
|
+
let currentOffset = 0;
|
|
82
83
|
for (const chunk of chunks) {
|
|
84
|
+
const offset = content.indexOf(chunk, currentOffset);
|
|
85
|
+
if (offset !== -1) {
|
|
86
|
+
currentOffset = offset;
|
|
87
|
+
}
|
|
83
88
|
await upsertProjectDocument(folderId, chunk, {
|
|
84
89
|
title: fileInfo.data.name || "Untitled Google Doc",
|
|
85
90
|
source: "google_drive",
|
|
86
91
|
file_id: fileId,
|
|
87
92
|
modified_time: driveModifiedTime,
|
|
93
|
+
offset: offset !== -1 ? offset : 0,
|
|
88
94
|
});
|
|
89
95
|
}
|
|
90
96
|
return { synced: true, content, driveModifiedTime };
|
|
@@ -15,5 +15,14 @@ export declare function searchKnowledge(query: string, topK?: number): Promise<{
|
|
|
15
15
|
success: boolean;
|
|
16
16
|
results: string;
|
|
17
17
|
error?: undefined;
|
|
18
|
+
} | {
|
|
19
|
+
success: boolean;
|
|
20
|
+
results: {
|
|
21
|
+
title: string;
|
|
22
|
+
fileId: any;
|
|
23
|
+
offset: any;
|
|
24
|
+
text: any;
|
|
25
|
+
}[];
|
|
26
|
+
error?: undefined;
|
|
18
27
|
}>;
|
|
19
28
|
//# sourceMappingURL=knowledgeTools.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"knowledgeTools.d.ts","sourceRoot":"","sources":["../../src/tools/knowledgeTools.ts"],"names":[],"mappings":"AAIA,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM;;;;;;;;GAmBlD;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,MAAU
|
|
1
|
+
{"version":3,"file":"knowledgeTools.d.ts","sourceRoot":"","sources":["../../src/tools/knowledgeTools.ts"],"names":[],"mappings":"AAIA,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM;;;;;;;;GAmBlD;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,MAAU;;;;;;;;;;;;;;;;;GA0CpE"}
|
|
@@ -39,20 +39,26 @@ export async function searchKnowledge(query, topK = 3) {
|
|
|
39
39
|
}
|
|
40
40
|
return {
|
|
41
41
|
success: true,
|
|
42
|
-
results: results
|
|
43
|
-
.map((r) => {
|
|
42
|
+
results: results.map((r) => {
|
|
44
43
|
let title = "Unknown Source";
|
|
44
|
+
let offset = undefined;
|
|
45
45
|
if (r.metadata) {
|
|
46
46
|
try {
|
|
47
47
|
const metaObj = JSON.parse(r.metadata);
|
|
48
48
|
if (metaObj.title)
|
|
49
49
|
title = metaObj.title;
|
|
50
|
+
if (metaObj.offset !== undefined)
|
|
51
|
+
offset = metaObj.offset;
|
|
50
52
|
}
|
|
51
53
|
catch (e) { }
|
|
52
54
|
}
|
|
53
|
-
return
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
return {
|
|
56
|
+
title,
|
|
57
|
+
fileId: r.file_id || "N/A",
|
|
58
|
+
offset,
|
|
59
|
+
text: r.text,
|
|
60
|
+
};
|
|
61
|
+
}),
|
|
56
62
|
};
|
|
57
63
|
}
|
|
58
64
|
catch (err) {
|
package/package.json
CHANGED
package/src/mcp-server.ts
CHANGED
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
import {
|
|
6
|
-
listDriveFiles,
|
|
7
|
-
readDriveDocument,
|
|
8
|
-
} from "./tools/driveTools.js";
|
|
5
|
+
import { listDriveFiles, readDriveDocument } from "./tools/driveTools.js";
|
|
9
6
|
import { saveAgentNote, searchKnowledge } from "./tools/knowledgeTools.js";
|
|
10
7
|
import { config } from "./config.js";
|
|
11
8
|
|
|
@@ -20,14 +17,15 @@ if (!DRIVE_FOLDER_ID) {
|
|
|
20
17
|
|
|
21
18
|
const server = new McpServer({
|
|
22
19
|
name: "doc-agent",
|
|
23
|
-
version: "1.0.
|
|
20
|
+
version: "1.0.4",
|
|
24
21
|
});
|
|
25
22
|
|
|
26
23
|
// Register tools
|
|
27
24
|
server.registerTool(
|
|
28
25
|
"list_drive_files",
|
|
29
26
|
{
|
|
30
|
-
description:
|
|
27
|
+
description:
|
|
28
|
+
"List and search for Google Drive documents and subfolders in a specific folder.",
|
|
31
29
|
inputSchema: {
|
|
32
30
|
keyword: z
|
|
33
31
|
.string()
|
|
@@ -36,7 +34,9 @@ server.registerTool(
|
|
|
36
34
|
targetFolderId: z
|
|
37
35
|
.string()
|
|
38
36
|
.optional()
|
|
39
|
-
.describe(
|
|
37
|
+
.describe(
|
|
38
|
+
"Optional Google Drive folder ID to list contents from. Defaults to the root knowledge folder.",
|
|
39
|
+
),
|
|
40
40
|
},
|
|
41
41
|
},
|
|
42
42
|
async ({ keyword, targetFolderId }) => {
|
|
@@ -57,11 +57,17 @@ server.registerTool(
|
|
|
57
57
|
"read_drive_document",
|
|
58
58
|
{
|
|
59
59
|
description:
|
|
60
|
-
"Read the content of a specific Google Drive document.
|
|
60
|
+
"Read the content of a specific Google Drive document. You can use the 'offset' parameter (obtained from search_knowledge) to read a specific chunk of text.",
|
|
61
61
|
inputSchema: {
|
|
62
62
|
fileId: z.string().describe("The Google Drive file ID to read"),
|
|
63
|
-
offset: z
|
|
64
|
-
|
|
63
|
+
offset: z
|
|
64
|
+
.number()
|
|
65
|
+
.optional()
|
|
66
|
+
.describe("Starting character index (default: 0)"),
|
|
67
|
+
limit: z
|
|
68
|
+
.number()
|
|
69
|
+
.optional()
|
|
70
|
+
.describe("Maximum number of characters to return (default: 10000)"),
|
|
65
71
|
},
|
|
66
72
|
},
|
|
67
73
|
async ({ fileId, offset, limit }) => {
|
|
@@ -78,33 +84,13 @@ server.registerTool(
|
|
|
78
84
|
},
|
|
79
85
|
);
|
|
80
86
|
|
|
81
|
-
|
|
82
|
-
"save_agent_note",
|
|
83
|
-
{
|
|
84
|
-
description: "Save an agent note, thought, or summary directly into the vector memory.",
|
|
85
|
-
inputSchema: {
|
|
86
|
-
content: z.string().describe("The note or knowledge content to store"),
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
async ({ content }) => {
|
|
90
|
-
const res = await saveAgentNote(content);
|
|
91
|
-
if (!res.success) {
|
|
92
|
-
return {
|
|
93
|
-
content: [{ type: "text", text: `Error: ${res.error}` }],
|
|
94
|
-
isError: true,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
return {
|
|
98
|
-
content: [{ type: "text", text: res.message || "Saved successfully" }],
|
|
99
|
-
};
|
|
100
|
-
},
|
|
101
|
-
);
|
|
87
|
+
|
|
102
88
|
|
|
103
89
|
server.registerTool(
|
|
104
90
|
"search_knowledge",
|
|
105
91
|
{
|
|
106
92
|
description:
|
|
107
|
-
"Search the folder's vector memory for relevant context or knowledge.",
|
|
93
|
+
"Search the folder's vector memory for relevant context or knowledge. Returns structured JSON array of matching chunks.",
|
|
108
94
|
inputSchema: {
|
|
109
95
|
query: z.string().describe("The search query"),
|
|
110
96
|
topK: z
|
package/src/tools/driveTools.ts
CHANGED
|
@@ -102,12 +102,19 @@ export async function syncSingleDocument(fileId: string, folderId: string) {
|
|
|
102
102
|
});
|
|
103
103
|
const chunks = await splitter.splitText(content);
|
|
104
104
|
|
|
105
|
+
let currentOffset = 0;
|
|
105
106
|
for (const chunk of chunks) {
|
|
107
|
+
const offset = content.indexOf(chunk, currentOffset);
|
|
108
|
+
if (offset !== -1) {
|
|
109
|
+
currentOffset = offset;
|
|
110
|
+
}
|
|
111
|
+
|
|
106
112
|
await upsertProjectDocument(folderId, chunk, {
|
|
107
113
|
title: fileInfo.data.name || "Untitled Google Doc",
|
|
108
114
|
source: "google_drive",
|
|
109
115
|
file_id: fileId,
|
|
110
116
|
modified_time: driveModifiedTime,
|
|
117
|
+
offset: offset !== -1 ? offset : 0,
|
|
111
118
|
});
|
|
112
119
|
}
|
|
113
120
|
return { synced: true, content, driveModifiedTime };
|
|
@@ -44,18 +44,23 @@ export async function searchKnowledge(query: string, topK: number = 3) {
|
|
|
44
44
|
|
|
45
45
|
return {
|
|
46
46
|
success: true,
|
|
47
|
-
results: results
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
47
|
+
results: results.map((r: any) => {
|
|
48
|
+
let title = "Unknown Source";
|
|
49
|
+
let offset = undefined;
|
|
50
|
+
if (r.metadata) {
|
|
51
|
+
try {
|
|
52
|
+
const metaObj = JSON.parse(r.metadata);
|
|
53
|
+
if (metaObj.title) title = metaObj.title;
|
|
54
|
+
if (metaObj.offset !== undefined) offset = metaObj.offset;
|
|
55
|
+
} catch (e) {}
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
title,
|
|
59
|
+
fileId: r.file_id || "N/A",
|
|
60
|
+
offset,
|
|
61
|
+
text: r.text,
|
|
62
|
+
};
|
|
63
|
+
}),
|
|
59
64
|
};
|
|
60
65
|
} catch (err: any) {
|
|
61
66
|
return { success: false, error: `Failed to search: ${err.message}` };
|