@insightsuen/dingdoc 0.1.0
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 +200 -0
- package/dist/auth.d.ts +19 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +178 -0
- package/dist/auth.js.map +1 -0
- package/dist/browser.d.ts +10 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +192 -0
- package/dist/browser.js.map +1 -0
- package/dist/client.d.ts +10 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +68 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +788 -0
- package/dist/index.js.map +1 -0
- package/dist/stream.d.ts +26 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +114 -0
- package/dist/stream.js.map +1 -0
- package/dist/tools/get-document.d.ts +3 -0
- package/dist/tools/get-document.d.ts.map +1 -0
- package/dist/tools/get-document.js +117 -0
- package/dist/tools/get-document.js.map +1 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +15 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list-nodes.d.ts +3 -0
- package/dist/tools/list-nodes.d.ts.map +1 -0
- package/dist/tools/list-nodes.js +73 -0
- package/dist/tools/list-nodes.js.map +1 -0
- package/dist/tools/list-spaces.d.ts +3 -0
- package/dist/tools/list-spaces.d.ts.map +1 -0
- package/dist/tools/list-spaces.js +31 -0
- package/dist/tools/list-spaces.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,6CAA6C;AAC7C,MAAM,QAAQ,GACZ,CAAC,GAAG,IAAe,EAAE,EAAE,CACrB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;AAC5D,OAAO,CAAC,GAAG,GAAG,QAAQ,CAAC;AACvB,OAAO,CAAC,IAAI,GAAG,QAAQ,CAAC;AACxB,OAAO,CAAC,IAAI,GAAG,QAAQ,CAAC;AACxB,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC;AACzB,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACrG,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,IAAI,YAAY,GAAG,KAAK,CAAC;AAEzB,IAAI,eAAe,EAAE,EAAE,CAAC;IACtB,IAAI,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC3B,MAAM,mBAAmB,EAAE,CAAC;QAC5B,YAAY,GAAG,IAAI,CAAC;QACpB,eAAe,CAAC,IAAI,CAAC,CAAC;QAEtB,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,iBAAiB,EAAE,CAAC;QAClD,IAAI,MAAM,IAAI,SAAS,EAAE,CAAC;YACxB,MAAM,CAAC,IAAI,CAAC,iCAAiC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC;YAC3E,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,MAAM,CAAC,IAAI,CAAC,0BAA0B,GAAG,EAAE,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC;KAAM,CAAC;IACN,MAAM,CAAC,IAAI,CACT,wFAAwF;QACxF,yCAAyC;QACzC,uCAAuC,CACxC,CAAC;AACJ,CAAC;AAED,MAAM,MAAM,GAAG,YAAY,CAAC;IAC1B,IAAI,EAAE,iBAAiB;IACvB,OAAO,EAAE,OAAO;IAChB,WAAW,EAAE,mCAAmC;CACjD,CAAC,CAAC;AAEH,oBAAoB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAE3C,MAAM,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;AAC1C,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC3B,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAC;IAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/stream.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface DocExportResult {
|
|
2
|
+
taskId: number;
|
|
3
|
+
success: boolean;
|
|
4
|
+
content?: string;
|
|
5
|
+
url?: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
extension?: string;
|
|
8
|
+
format?: string;
|
|
9
|
+
dentryUuid?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 初始化 DingTalk Stream 长连接。
|
|
13
|
+
* 服务启动时调用一次,用于接收文档导出完成事件(doc_content_export_result)。
|
|
14
|
+
*/
|
|
15
|
+
export declare function initDocStream(appKey: string, appSecret: string): void;
|
|
16
|
+
/**
|
|
17
|
+
* 等待指定 taskId 的文档导出结果。
|
|
18
|
+
* 需在调用 QueryDocContent 接口后立即调用,以避免事件先于等待注册到达。
|
|
19
|
+
*/
|
|
20
|
+
export declare function waitForDocExport(taskId: number, timeoutMs?: number): Promise<DocExportResult>;
|
|
21
|
+
export declare function isStreamReady(): boolean;
|
|
22
|
+
/** 从缓存中获取文档内容(若有) */
|
|
23
|
+
export declare function getCachedDocContent(nodeId: string): string | undefined;
|
|
24
|
+
/** 将文档内容写入缓存 */
|
|
25
|
+
export declare function cacheDocContent(nodeId: string, content: string): void;
|
|
26
|
+
//# sourceMappingURL=stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAqBD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAsDrE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,SAAS,SAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAiC1B;AAED,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED,qBAAqB;AACrB,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEtE;AAED,gBAAgB;AAChB,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAGrE"}
|
package/dist/stream.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { DWClient, EventAck } from "dingtalk-stream";
|
|
2
|
+
import { logger } from "@insight-mcp/shared";
|
|
3
|
+
const DOC_CONTENT_EXPORT_EVENT = "doc_content_export_result";
|
|
4
|
+
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
5
|
+
/** taskId → Promise resolver(等待中的请求) */
|
|
6
|
+
const pending = new Map();
|
|
7
|
+
/**
|
|
8
|
+
* 近期到达的导出事件缓存(TTL 60s)。
|
|
9
|
+
* 解决竞态:事件在 waitForDocExport 注册前到达时,先暂存到此,供后续查询。
|
|
10
|
+
*/
|
|
11
|
+
const recentEvents = new Map();
|
|
12
|
+
const RECENT_EVENT_TTL_MS = 60_000;
|
|
13
|
+
let streamClient = null;
|
|
14
|
+
let streamReady = false;
|
|
15
|
+
/**
|
|
16
|
+
* 已成功获取过内容的文档缓存(nodeId → Markdown 内容)。
|
|
17
|
+
* DingTalk 导出事件每个 taskId 只推送一次,进程重启后需重新获取。
|
|
18
|
+
*/
|
|
19
|
+
const docContentCache = new Map();
|
|
20
|
+
/**
|
|
21
|
+
* 初始化 DingTalk Stream 长连接。
|
|
22
|
+
* 服务启动时调用一次,用于接收文档导出完成事件(doc_content_export_result)。
|
|
23
|
+
*/
|
|
24
|
+
export function initDocStream(appKey, appSecret) {
|
|
25
|
+
if (streamClient)
|
|
26
|
+
return;
|
|
27
|
+
streamClient = new DWClient({ clientId: appKey, clientSecret: appSecret });
|
|
28
|
+
streamClient.registerAllEventListener((msg) => {
|
|
29
|
+
// 调试:打印所有收到的事件类型
|
|
30
|
+
logger.info(`[Stream] 收到事件 type=${msg.type} topic=${msg.headers.topic} eventType=${msg.headers.eventType ?? "(none)"}`);
|
|
31
|
+
if (msg.headers.eventType === DOC_CONTENT_EXPORT_EVENT) {
|
|
32
|
+
try {
|
|
33
|
+
logger.info(`[Stream] 事件原始 data: ${msg.data}`);
|
|
34
|
+
const data = JSON.parse(msg.data);
|
|
35
|
+
const taskId = Number(data.taskId);
|
|
36
|
+
const normalized = { ...data, taskId };
|
|
37
|
+
logger.info(`[Stream] 文档导出事件解析完成,taskId=${taskId}(原始: ${String(data.taskId)}),success=${String(data.success)},pending中的key列表: [${[...pending.keys()].join(", ")}]`);
|
|
38
|
+
// 成功获取内容时,按 nodeId 缓存,避免因 DingTalk 只推送一次事件而导致后续请求超时
|
|
39
|
+
if (normalized.success && normalized.content && normalized.dentryUuid) {
|
|
40
|
+
cacheDocContent(normalized.dentryUuid, normalized.content);
|
|
41
|
+
}
|
|
42
|
+
const resolver = pending.get(taskId);
|
|
43
|
+
if (resolver) {
|
|
44
|
+
// 有等待中的请求,直接分发(无论成功或失败都分发,由调用方决策)
|
|
45
|
+
pending.delete(taskId);
|
|
46
|
+
resolver(normalized);
|
|
47
|
+
}
|
|
48
|
+
else if (normalized.success) {
|
|
49
|
+
// 仅缓存成功结果,供稍后的 waitForDocExport 查询(解决事件先于等待注册的竞态)
|
|
50
|
+
// 失败结果不缓存,避免污染后续请求
|
|
51
|
+
logger.info(`[Stream] taskId ${taskId} 暂存到近期事件缓存`);
|
|
52
|
+
recentEvents.set(taskId, normalized);
|
|
53
|
+
setTimeout(() => recentEvents.delete(taskId), RECENT_EVENT_TTL_MS);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
logger.warn(`[Stream] taskId ${taskId} 导出失败且无等待请求,丢弃`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
logger.error("解析文档导出事件失败", err);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { status: EventAck.SUCCESS };
|
|
64
|
+
});
|
|
65
|
+
streamClient
|
|
66
|
+
.connect()
|
|
67
|
+
.then(() => {
|
|
68
|
+
streamReady = true;
|
|
69
|
+
logger.info("✓ 钉钉 Stream 连接已建立,开始监听文档导出事件");
|
|
70
|
+
})
|
|
71
|
+
.catch((err) => {
|
|
72
|
+
logger.error("钉钉 Stream 连接失败,get_document 工具将无法获取文档内容", err);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* 等待指定 taskId 的文档导出结果。
|
|
77
|
+
* 需在调用 QueryDocContent 接口后立即调用,以避免事件先于等待注册到达。
|
|
78
|
+
*/
|
|
79
|
+
export function waitForDocExport(taskId, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
80
|
+
if (!streamClient) {
|
|
81
|
+
return Promise.reject(new Error("DingTalk Stream 客户端未初始化,无法接收文档导出结果。请检查服务启动日志。"));
|
|
82
|
+
}
|
|
83
|
+
// 先查近期事件缓存(解决竞态:事件在注册前已到达的情况)
|
|
84
|
+
const cached = recentEvents.get(taskId);
|
|
85
|
+
if (cached) {
|
|
86
|
+
logger.info(`[Stream] taskId ${taskId} 命中近期事件缓存,直接返回`);
|
|
87
|
+
recentEvents.delete(taskId);
|
|
88
|
+
return Promise.resolve(cached);
|
|
89
|
+
}
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
const timer = setTimeout(() => {
|
|
92
|
+
pending.delete(taskId);
|
|
93
|
+
reject(new Error(`文档导出超时(taskId: ${taskId},等待 ${timeoutMs / 1000}s)。` +
|
|
94
|
+
`\n 可能原因:Stream 连接断开、导出服务繁忙,或文档不支持导出。`));
|
|
95
|
+
}, timeoutMs);
|
|
96
|
+
pending.set(taskId, (result) => {
|
|
97
|
+
clearTimeout(timer);
|
|
98
|
+
resolve(result);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
export function isStreamReady() {
|
|
103
|
+
return streamReady;
|
|
104
|
+
}
|
|
105
|
+
/** 从缓存中获取文档内容(若有) */
|
|
106
|
+
export function getCachedDocContent(nodeId) {
|
|
107
|
+
return docContentCache.get(nodeId);
|
|
108
|
+
}
|
|
109
|
+
/** 将文档内容写入缓存 */
|
|
110
|
+
export function cacheDocContent(nodeId, content) {
|
|
111
|
+
docContentCache.set(nodeId, content);
|
|
112
|
+
logger.info(`[Cache] 文档内容已缓存,nodeId: ${nodeId},长度: ${content.length} 字符`);
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-document.d.ts","sourceRoot":"","sources":["../../src/tools/get-document.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAmFrD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA+D3D"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { toToolError, logger } from "@insight-mcp/shared";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { dingtalkGet, DingTalkApiError } from "../client.js";
|
|
4
|
+
import { waitForDocExport, isStreamReady, getCachedDocContent } from "../stream.js";
|
|
5
|
+
import { fetchDocViaBrowser } from "../browser.js";
|
|
6
|
+
import { isApiAvailable } from "../auth.js";
|
|
7
|
+
/** 触发 browser fallback 的 DingTalk API 错误码 */
|
|
8
|
+
const BROWSER_FALLBACK_CODES = new Set(["permissionDenied", "documentNotExist"]);
|
|
9
|
+
const ALIDOC_URL_PATTERN = /alidocs\.dingtalk\.com\/i\/nodes\/([A-Za-z0-9_-]+)/;
|
|
10
|
+
/** 从钉钉文档 URL 中提取 nodeId */
|
|
11
|
+
function extractNodeId(url) {
|
|
12
|
+
return ALIDOC_URL_PATTERN.exec(url)?.[1] ?? null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 触发一次文档导出并等待 Stream 事件。
|
|
16
|
+
*
|
|
17
|
+
* 钉钉有导出缓存机制:若文档未变更,API 返回相同 taskId 但不推送新的 Stream 事件。
|
|
18
|
+
* 调用方应在首次超时后再重试一次,重试会强制钉钉重新导出并推送事件。
|
|
19
|
+
*/
|
|
20
|
+
async function triggerAndWaitExport(nodeId, timeoutMs) {
|
|
21
|
+
logger.info(`正在触发文档导出,nodeId: ${nodeId}`);
|
|
22
|
+
const resp = await dingtalkGet(`/v2.0/doc/query/${nodeId}/contents`, { targetFormat: "markdown" });
|
|
23
|
+
const taskId = Number(resp.taskId);
|
|
24
|
+
logger.info(`文档导出任务已触发,taskId: ${taskId},等待导出结果(最长 ${timeoutMs / 1000}s)...`);
|
|
25
|
+
const result = await waitForDocExport(taskId, timeoutMs);
|
|
26
|
+
if (!result.success) {
|
|
27
|
+
throw new Error(`文档导出失败(taskId: ${taskId})`);
|
|
28
|
+
}
|
|
29
|
+
const content = result.content ?? "";
|
|
30
|
+
logger.info(`文档导出成功,taskId: ${taskId},内容长度: ${content.length} 字符`);
|
|
31
|
+
return content;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 通过 API + Stream 获取文档 Markdown 内容。
|
|
35
|
+
* 首次超时后自动重试一次(钉钉缓存机制导致相同 taskId 可能不推送新事件)。
|
|
36
|
+
*/
|
|
37
|
+
async function fetchDocContent(nodeId) {
|
|
38
|
+
if (!isStreamReady()) {
|
|
39
|
+
throw new Error("DingTalk Stream 连接尚未建立,无法获取文档内容。" +
|
|
40
|
+
"\n 请等待服务启动完成后重试,或检查启动日志中是否有 Stream 连接错误。");
|
|
41
|
+
}
|
|
42
|
+
// 优先使用进程内缓存(DingTalk 每个 taskId 只推送一次 Stream 事件)
|
|
43
|
+
const cached = getCachedDocContent(nodeId);
|
|
44
|
+
if (cached) {
|
|
45
|
+
logger.info(`[Cache] 命中文档缓存,nodeId: ${nodeId},直接返回`);
|
|
46
|
+
return cached;
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
return await triggerAndWaitExport(nodeId, 20_000);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
// 权限错误直接上抛,由外层决定是否 fallback 到浏览器
|
|
53
|
+
if (err instanceof DingTalkApiError && BROWSER_FALLBACK_CODES.has(err.code)) {
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
// 超时或其他错误重试一次
|
|
57
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
58
|
+
logger.warn(`首次导出失败,自动重试(nodeId: ${nodeId},原因: ${reason})...`);
|
|
59
|
+
return await triggerAndWaitExport(nodeId, 30_000);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export function registerGetDocument(server) {
|
|
63
|
+
server.tool("dingtalk_get_document", "读取钉钉知识库文档内容,返回 Markdown 格式。支持通过 nodeId 或文档 URL(https://alidocs.dingtalk.com/i/nodes/...)两种方式指定文档", {
|
|
64
|
+
nodeId: z.string().optional().describe("文档节点 ID(与 url 二选一)"),
|
|
65
|
+
url: z
|
|
66
|
+
.string()
|
|
67
|
+
.optional()
|
|
68
|
+
.describe("文档 URL,格式:https://alidocs.dingtalk.com/i/nodes/{nodeId}(与 nodeId 二选一)"),
|
|
69
|
+
}, async ({ nodeId, url }) => {
|
|
70
|
+
let resolvedNodeId;
|
|
71
|
+
if (nodeId) {
|
|
72
|
+
resolvedNodeId = nodeId;
|
|
73
|
+
}
|
|
74
|
+
else if (url) {
|
|
75
|
+
const extracted = extractNodeId(url);
|
|
76
|
+
if (!extracted) {
|
|
77
|
+
return toToolError(new Error("无法从 URL 中解析 nodeId,请确认格式为:https://alidocs.dingtalk.com/i/nodes/{nodeId}"));
|
|
78
|
+
}
|
|
79
|
+
resolvedNodeId = extracted;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
return toToolError(new Error("请提供 nodeId 或 url 参数(二选一)"));
|
|
83
|
+
}
|
|
84
|
+
// API 不可用时直接走浏览器
|
|
85
|
+
if (!isApiAvailable()) {
|
|
86
|
+
logger.info(`[Browser] API 不可用,使用浏览器读取文档(nodeId: ${resolvedNodeId})`);
|
|
87
|
+
try {
|
|
88
|
+
const content = await fetchDocViaBrowser(resolvedNodeId);
|
|
89
|
+
return { content: [{ type: "text", text: content }] };
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
return toToolError(err);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const content = await fetchDocContent(resolvedNodeId);
|
|
97
|
+
return { content: [{ type: "text", text: content }] };
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
// API 权限不足时 fallback 到浏览器
|
|
101
|
+
if (err instanceof DingTalkApiError && BROWSER_FALLBACK_CODES.has(err.code)) {
|
|
102
|
+
logger.warn(`[Fallback] API 返回 ${err.code},切换浏览器访问(nodeId: ${resolvedNodeId})`);
|
|
103
|
+
try {
|
|
104
|
+
const content = await fetchDocViaBrowser(resolvedNodeId);
|
|
105
|
+
return {
|
|
106
|
+
content: [{ type: "text", text: `> ⚠️ API 无权访问此文档,以下内容通过浏览器读取。\n\n${content}` }],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
catch (browserErr) {
|
|
110
|
+
return toToolError(new Error(`API 和浏览器均无法获取文档内容:\n API: ${err.message}\n 浏览器: ${browserErr instanceof Error ? browserErr.message : String(browserErr)}`));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return toToolError(err);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=get-document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-document.js","sourceRoot":"","sources":["../../src/tools/get-document.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAMrD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,GAAG,IAAI,CAQnF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { logger } from "@insight-mcp/shared";
|
|
2
|
+
import { registerListSpaces } from "./list-spaces.js";
|
|
3
|
+
import { registerListNodes } from "./list-nodes.js";
|
|
4
|
+
import { registerGetDocument } from "./get-document.js";
|
|
5
|
+
export function registerDingdocTools(server, apiAvailable) {
|
|
6
|
+
if (apiAvailable) {
|
|
7
|
+
registerListSpaces(server);
|
|
8
|
+
registerListNodes(server);
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
logger.info("仅浏览器模式:仅注册 dingtalk_get_document 工具(list_spaces / list_nodes 需要 API 凭证)");
|
|
12
|
+
}
|
|
13
|
+
registerGetDocument(server);
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,MAAM,UAAU,oBAAoB,CAAC,MAAiB,EAAE,YAAqB;IAC3E,IAAI,YAAY,EAAE,CAAC;QACjB,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAC3B,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC,yEAAyE,CAAC,CAAC;IACzF,CAAC;IACD,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-nodes.d.ts","sourceRoot":"","sources":["../../src/tools/list-nodes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AA4ErD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAsCzD"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { toToolError } from "@insight-mcp/shared";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { dingtalkGet } from "../client.js";
|
|
4
|
+
async function fetchNodes(parentNodeId) {
|
|
5
|
+
const nodes = [];
|
|
6
|
+
let nextToken;
|
|
7
|
+
do {
|
|
8
|
+
const params = {
|
|
9
|
+
parentNodeId,
|
|
10
|
+
maxResults: 50,
|
|
11
|
+
};
|
|
12
|
+
if (nextToken)
|
|
13
|
+
params["nextToken"] = nextToken;
|
|
14
|
+
const resp = await dingtalkGet("/v2.0/wiki/nodes", params);
|
|
15
|
+
nodes.push(...(resp.nodes ?? []));
|
|
16
|
+
nextToken = resp.nextToken;
|
|
17
|
+
} while (nextToken);
|
|
18
|
+
return nodes;
|
|
19
|
+
}
|
|
20
|
+
async function fetchNodesRecursive(parentNodeId) {
|
|
21
|
+
const nodes = await fetchNodes(parentNodeId);
|
|
22
|
+
const results = [];
|
|
23
|
+
for (const node of nodes) {
|
|
24
|
+
const result = {
|
|
25
|
+
nodeId: node.nodeId,
|
|
26
|
+
name: node.name,
|
|
27
|
+
type: node.type,
|
|
28
|
+
category: node.category ?? null,
|
|
29
|
+
url: node.url ?? null,
|
|
30
|
+
hasChildren: node.hasChildren,
|
|
31
|
+
};
|
|
32
|
+
if (node.hasChildren && node.type === "FOLDER") {
|
|
33
|
+
result.children = await fetchNodesRecursive(node.nodeId);
|
|
34
|
+
}
|
|
35
|
+
results.push(result);
|
|
36
|
+
}
|
|
37
|
+
return results;
|
|
38
|
+
}
|
|
39
|
+
export function registerListNodes(server) {
|
|
40
|
+
server.tool("dingtalk_list_nodes", "列出钉钉知识库节点下的子节点。nodeId 可从 dingtalk_list_spaces 获取的 rootNodeId 开始。支持 recursive 递归展开整个目录树(节点数量多时耗时较长)", {
|
|
41
|
+
nodeId: z.string().describe("父节点 ID(dentryUuid),如知识库的 rootNodeId"),
|
|
42
|
+
recursive: z
|
|
43
|
+
.boolean()
|
|
44
|
+
.optional()
|
|
45
|
+
.default(false)
|
|
46
|
+
.describe("是否递归展开所有子节点,默认 false"),
|
|
47
|
+
}, async ({ nodeId, recursive }) => {
|
|
48
|
+
try {
|
|
49
|
+
let result;
|
|
50
|
+
if (recursive) {
|
|
51
|
+
result = await fetchNodesRecursive(nodeId);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const nodes = await fetchNodes(nodeId);
|
|
55
|
+
result = nodes.map((node) => ({
|
|
56
|
+
nodeId: node.nodeId,
|
|
57
|
+
name: node.name,
|
|
58
|
+
type: node.type,
|
|
59
|
+
category: node.category ?? null,
|
|
60
|
+
url: node.url ?? null,
|
|
61
|
+
hasChildren: node.hasChildren,
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
return toToolError(err);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=list-nodes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-nodes.js","sourceRoot":"","sources":["../../src/tools/list-nodes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-spaces.d.ts","sourceRoot":"","sources":["../../src/tools/list-spaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAgBrD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAkC1D"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { toToolError } from "@insight-mcp/shared";
|
|
2
|
+
import { dingtalkGet } from "../client.js";
|
|
3
|
+
export function registerListSpaces(server) {
|
|
4
|
+
server.tool("dingtalk_list_spaces", "列出当前用户有权限的所有钉钉知识库空间,返回 spaceId、名称、rootNodeId 和访问链接", {}, async () => {
|
|
5
|
+
try {
|
|
6
|
+
const spaces = [];
|
|
7
|
+
let nextToken;
|
|
8
|
+
do {
|
|
9
|
+
const params = { maxResults: 30 };
|
|
10
|
+
if (nextToken)
|
|
11
|
+
params["nextToken"] = nextToken;
|
|
12
|
+
const resp = await dingtalkGet("/v2.0/wiki/workspaces", params);
|
|
13
|
+
spaces.push(...(resp.workspaces ?? []));
|
|
14
|
+
nextToken = resp.nextToken;
|
|
15
|
+
} while (nextToken);
|
|
16
|
+
const result = spaces.map((s) => ({
|
|
17
|
+
spaceId: s.workspaceId,
|
|
18
|
+
name: s.name,
|
|
19
|
+
rootNodeId: s.rootNodeId,
|
|
20
|
+
url: s.url ?? null,
|
|
21
|
+
}));
|
|
22
|
+
return {
|
|
23
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
return toToolError(err);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=list-spaces.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-spaces.js","sourceRoot":"","sources":["../../src/tools/list-spaces.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAc3C,MAAM,UAAU,kBAAkB,CAAC,MAAiB;IAClD,MAAM,CAAC,IAAI,CACT,sBAAsB,EACtB,oDAAoD,EACpD,EAAE,EACF,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,MAAM,GAAgB,EAAE,CAAC;YAC/B,IAAI,SAA6B,CAAC;YAElC,GAAG,CAAC;gBACF,MAAM,MAAM,GAAoC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;gBACnE,IAAI,SAAS;oBAAE,MAAM,CAAC,WAAW,CAAC,GAAG,SAAS,CAAC;gBAE/C,MAAM,IAAI,GAAG,MAAM,WAAW,CAAyB,uBAAuB,EAAE,MAAM,CAAC,CAAC;gBACxF,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,CAAC;gBACxC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;YAC7B,CAAC,QAAQ,SAAS,EAAE;YAEpB,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAChC,OAAO,EAAE,CAAC,CAAC,WAAW;gBACtB,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,UAAU,EAAE,CAAC,CAAC,UAAU;gBACxB,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,IAAI;aACnB,CAAC,CAAC,CAAC;YAEJ,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;aACnE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@insightsuen/dingdoc",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for reading DingTalk knowledge base documents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"dingdoc-mcp": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=20"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"mcp",
|
|
18
|
+
"dingtalk",
|
|
19
|
+
"dingdoc",
|
|
20
|
+
"knowledge-base",
|
|
21
|
+
"model-context-protocol"
|
|
22
|
+
],
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "node build.mjs",
|
|
26
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
27
|
+
"clean": "rm -rf dist *.tsbuildinfo",
|
|
28
|
+
"prepublishOnly": "pnpm run build",
|
|
29
|
+
"start": "node dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
33
|
+
"dingtalk-stream": "^2.1.4",
|
|
34
|
+
"playwright": "^1.58.2",
|
|
35
|
+
"turndown": "^7.2.2",
|
|
36
|
+
"zod": "^3.25.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^22.10.0",
|
|
40
|
+
"@types/turndown": "^5.0.6",
|
|
41
|
+
"esbuild": "^0.25.0",
|
|
42
|
+
"typescript": "^5.7.2"
|
|
43
|
+
}
|
|
44
|
+
}
|