dingtalk-dws-mcp 1.0.1 → 1.0.2
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 +2 -2
- package/dist/src/resources/guides.d.ts +24 -0
- package/dist/src/resources/guides.js +36 -0
- package/dist/src/server.js +7 -2
- package/dist/src/tools/registry.js +1 -0
- package/guides/doc-search.md +37 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
源码包。用户能力与接入见 [`mcp/dingtalk-dws-mcp/GUIDE.md`](../../../mcp/dingtalk-dws-mcp/GUIDE.md)。
|
|
4
4
|
|
|
5
|
-
Agent:`instructions` / tool `description` / `prompts
|
|
5
|
+
Agent:`instructions` / tool `description` / `prompts` / **Resources(`guides/*.md`)**。长期巡检:`dingtalk_patrol_setup` 备料 → 传装到主机 → 停。
|
|
6
6
|
|
|
7
7
|
## 构建
|
|
8
8
|
|
|
@@ -16,6 +16,6 @@ npm run pack:deploy
|
|
|
16
16
|
可选:`DINGTALK_PATROL_INCLUDE_NODE=0`、`DINGTALK_PATROL_NODE_URL`、`DINGTALK_PATROL_INCLUDE_DWS=0`(默认尝试打入 linux ELF dws;Windows 打包机需 `DINGTALK_PATROL_DWS_PATH` 指向 ELF)。
|
|
17
17
|
`dingtalk_patrol_setup` 只写 staging(含本机已验证投递凭据);传装用 host-execution 或 SSH,目标机跑 `install.sh`。可选 `deploy.sh` 为直连 SSH helper。
|
|
18
18
|
|
|
19
|
-
配置示例(npx):[`mcp/dingtalk-dws-mcp/mcp.example.json`](../../../mcp/dingtalk-dws-mcp/mcp.example.json)。npm:`dingtalk-dws-mcp@1.0.
|
|
19
|
+
配置示例(npx):[`mcp/dingtalk-dws-mcp/mcp.example.json`](../../../mcp/dingtalk-dws-mcp/mcp.example.json)。npm:`dingtalk-dws-mcp@1.0.2`。
|
|
20
20
|
|
|
21
21
|
规格(开发者):[RFC-0007](../../../docs/rfc/RFC-0007-dingtalk-dws-mcp.md)、[RFC-0008](../../../docs/rfc/RFC-0008-dingtalk-notify-patrol.md)。
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type GuideMeta = {
|
|
2
|
+
slug: string;
|
|
3
|
+
uri: string;
|
|
4
|
+
title: string;
|
|
5
|
+
description: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function listMcpGuideResources(): Array<{
|
|
8
|
+
uri: string;
|
|
9
|
+
name: string;
|
|
10
|
+
title: string;
|
|
11
|
+
description: string;
|
|
12
|
+
mimeType: string;
|
|
13
|
+
annotations: {
|
|
14
|
+
audience: Array<"user" | "assistant">;
|
|
15
|
+
priority: number;
|
|
16
|
+
};
|
|
17
|
+
}>;
|
|
18
|
+
export declare function readMcpGuideResource(uri: string): {
|
|
19
|
+
contents: Array<{
|
|
20
|
+
uri: string;
|
|
21
|
+
mimeType: string;
|
|
22
|
+
text: string;
|
|
23
|
+
}>;
|
|
24
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { mcpPackageRoot } from "../version.js";
|
|
4
|
+
const GUIDES = [
|
|
5
|
+
{
|
|
6
|
+
slug: "doc-search",
|
|
7
|
+
uri: "dingtalk-dws://guides/doc-search",
|
|
8
|
+
title: "DingTalk cloud doc search",
|
|
9
|
+
description: "Agent overlay: drive search short path and stop-rules; prefer over upstream doc search recipes when they lag.",
|
|
10
|
+
},
|
|
11
|
+
];
|
|
12
|
+
function readMarkdown(slug) {
|
|
13
|
+
const filePath = path.join(mcpPackageRoot(), "guides", `${slug}.md`);
|
|
14
|
+
return fs.readFileSync(filePath, "utf8");
|
|
15
|
+
}
|
|
16
|
+
export function listMcpGuideResources() {
|
|
17
|
+
return GUIDES.map((g) => ({
|
|
18
|
+
uri: g.uri,
|
|
19
|
+
name: g.slug,
|
|
20
|
+
title: g.title,
|
|
21
|
+
description: g.description,
|
|
22
|
+
mimeType: "text/markdown",
|
|
23
|
+
annotations: { audience: ["assistant"], priority: 0.7 },
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
export function readMcpGuideResource(uri) {
|
|
27
|
+
const guide = GUIDES.find((g) => g.uri === uri);
|
|
28
|
+
if (!guide) {
|
|
29
|
+
const err = new Error(`Resource not found: ${uri}`);
|
|
30
|
+
err.code = -32002;
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
contents: [{ uri, mimeType: "text/markdown", text: readMarkdown(guide.slug) }],
|
|
35
|
+
};
|
|
36
|
+
}
|
package/dist/src/server.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
-
import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
4
|
import { loadConfig } from "./config.js";
|
|
5
5
|
import { IdempotencyStore } from "./notify/idempotency-store.js";
|
|
6
6
|
import { getPromptMessages, listPromptDefinitions } from "./prompts.js";
|
|
7
|
+
import { listMcpGuideResources, readMcpGuideResource } from "./resources/guides.js";
|
|
7
8
|
import { DingtalkDwsService } from "./service.js";
|
|
8
9
|
import { buildInstructions, callTool, createToolDefinitions, resolveToolSurface } from "./tools/registry.js";
|
|
9
10
|
import { readPackageVersion } from "./version.js";
|
|
@@ -22,7 +23,7 @@ export function createMcpServer(service, store) {
|
|
|
22
23
|
if (!resolvedStore)
|
|
23
24
|
throw new Error("store required");
|
|
24
25
|
const surface = resolveToolSurface(adapter);
|
|
25
|
-
const server = new Server({ name: MCP_SERVER_NAME, version: MCP_SERVER_VERSION }, { capabilities: { tools: {}, prompts: {} }, instructions: buildInstructions(surface) });
|
|
26
|
+
const server = new Server({ name: MCP_SERVER_NAME, version: MCP_SERVER_VERSION }, { capabilities: { tools: {}, prompts: {}, resources: {} }, instructions: buildInstructions(surface) });
|
|
26
27
|
const tools = createToolDefinitions(surface);
|
|
27
28
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [...tools] }));
|
|
28
29
|
server.setRequestHandler(CallToolRequestSchema, async (request) => (callTool(resolvedService, request.params.name, request.params.arguments ?? {}, surface)));
|
|
@@ -37,6 +38,10 @@ export function createMcpServer(service, store) {
|
|
|
37
38
|
throw new Error(`Unknown prompt: ${request.params.name}`);
|
|
38
39
|
}
|
|
39
40
|
});
|
|
41
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
42
|
+
resources: listMcpGuideResources(),
|
|
43
|
+
}));
|
|
44
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => (readMcpGuideResource(request.params.uri)));
|
|
40
45
|
return { server, service: resolvedService, store: resolvedStore };
|
|
41
46
|
}
|
|
42
47
|
export async function runStdioServer() {
|
|
@@ -15,6 +15,7 @@ export function buildInstructions(surface) {
|
|
|
15
15
|
// User-selected recipes → prompts. Do not duplicate those here.
|
|
16
16
|
const lines = [
|
|
17
17
|
"DingTalk robot notify + dws readiness (Agent surface of the dws ecosystem). Office stays on local dws.",
|
|
18
|
+
"Office via local dws: before following upstream Skill recipes, read this server's MCP Resources overlays when they apply.",
|
|
18
19
|
"Constraint: recurring/host patrol must not use session loops or repeated dingtalk_send — Agent derives host/cron/probe from conversation, calls dingtalk_patrol_setup once (stages bundle paths + delivery credentials), copies to host via host-execution or SSH, runs install.sh on the host, then stops. MCP env holds credentials only. Do not download Node in-session.",
|
|
19
20
|
"Call dingtalk_doctor first; connected ≠ ready.",
|
|
20
21
|
];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# DingTalk cloud doc search (Agent overlay)
|
|
2
|
+
|
|
3
|
+
Use this when the user asks to find / list / summarize DingTalk cloud docs or knowledge-base pages.
|
|
4
|
+
|
|
5
|
+
Upstream dws Skill recipes may still say `doc search` / `doc list`. Prefer this overlay when they conflict.
|
|
6
|
+
|
|
7
|
+
## Short path
|
|
8
|
+
|
|
9
|
+
1. One primary search:
|
|
10
|
+
```bash
|
|
11
|
+
dws drive search --query "<user keywords>" --format json
|
|
12
|
+
```
|
|
13
|
+
2. From `documents[]`, keep **5–10** hits with `name` / `nodeId` / `docUrl` (or `url`) / type.
|
|
14
|
+
3. Deliver a **table** (name + link + one-line why relevant). Stop here if that answers the user.
|
|
15
|
+
4. Only if the user wants depth, or top hits need a title skim: read **at most 2–3** `adoc` nodes:
|
|
16
|
+
```bash
|
|
17
|
+
dws doc read --node <nodeId> --content-format markdown --format json
|
|
18
|
+
```
|
|
19
|
+
Use the JSON field **`markdown`** (not `content`). Prefer headings / first ~20 lines — not the whole body in chat.
|
|
20
|
+
|
|
21
|
+
## Do not
|
|
22
|
+
|
|
23
|
+
- Recurse `drive list` / wiki folder listing after search (folder child names are often opaque IDs).
|
|
24
|
+
- Fire many overlapping queries (`性能优化` + `训练性能` + …) by default — one main query, optional one supplement.
|
|
25
|
+
- Pull full document bodies by default.
|
|
26
|
+
- Treat dlink / non-adoc hits as readable via `doc read` — give the link only.
|
|
27
|
+
- Treat empty `doc read` as “try another format / write a helper script” — check the `markdown` field / `--help` once, then skip or try the next hit.
|
|
28
|
+
|
|
29
|
+
## Knowledge-base scoped search
|
|
30
|
+
|
|
31
|
+
If the user already named a workspace/knowledge base:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
dws wiki node search --workspace <id> --query "<keywords>" --format json
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Otherwise stay on global `drive search`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dingtalk-dws-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "DingTalk robot notification MCP with dws readiness handshake",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"main": "dist/src/index.js",
|
|
11
11
|
"files": [
|
|
12
12
|
"dist/src",
|
|
13
|
+
"guides",
|
|
13
14
|
"README.md",
|
|
14
15
|
"LICENSE"
|
|
15
16
|
],
|