@mocrane/wecom 2026.3.9 → 2026.3.12

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 (51) hide show
  1. package/README.md +25 -22
  2. package/clawdbot.plugin.json +1 -0
  3. package/index.ts +38 -1
  4. package/openclaw.plugin.json +1 -0
  5. package/package.json +7 -4
  6. package/skills/wecom-contact-lookup/SKILL.md +162 -0
  7. package/skills/wecom-doc/SKILL.md +363 -0
  8. package/skills/wecom-doc/references/doc-api.md +224 -0
  9. package/skills/wecom-doc-manager/SKILL.md +64 -0
  10. package/skills/wecom-doc-manager/references/api-create-doc.md +56 -0
  11. package/skills/wecom-doc-manager/references/api-edit-doc-content.md +68 -0
  12. package/skills/wecom-doc-manager/references/api-export-document.md +88 -0
  13. package/skills/wecom-edit-todo/SKILL.md +249 -0
  14. package/skills/wecom-get-todo-detail/SKILL.md +143 -0
  15. package/skills/wecom-get-todo-list/SKILL.md +127 -0
  16. package/skills/wecom-meeting-create/SKILL.md +158 -0
  17. package/skills/wecom-meeting-create/references/example-full.md +30 -0
  18. package/skills/wecom-meeting-create/references/example-reminder.md +46 -0
  19. package/skills/wecom-meeting-create/references/example-security.md +22 -0
  20. package/skills/wecom-meeting-manage/SKILL.md +136 -0
  21. package/skills/wecom-meeting-query/SKILL.md +330 -0
  22. package/skills/wecom-preflight/SKILL.md +141 -0
  23. package/skills/wecom-schedule/SKILL.md +159 -0
  24. package/skills/wecom-schedule/references/api-check-availability.md +56 -0
  25. package/skills/wecom-schedule/references/api-create-schedule.md +38 -0
  26. package/skills/wecom-schedule/references/api-get-schedule-detail.md +81 -0
  27. package/skills/wecom-schedule/references/api-update-schedule.md +30 -0
  28. package/skills/wecom-schedule/references/ref-reminders.md +24 -0
  29. package/skills/wecom-smartsheet-data/SKILL.md +71 -0
  30. package/skills/wecom-smartsheet-data/references/api-get-records.md +61 -0
  31. package/skills/wecom-smartsheet-data/references/cell-value-formats.md +120 -0
  32. package/skills/wecom-smartsheet-schema/SKILL.md +92 -0
  33. package/skills/wecom-smartsheet-schema/references/field-types.md +43 -0
  34. package/src/agent/handler.ts +105 -14
  35. package/src/channel.ts +7 -4
  36. package/src/compat/plugin-sdk-shim.ts +152 -0
  37. package/src/mcp/index.ts +7 -0
  38. package/src/mcp/schema.ts +108 -0
  39. package/src/mcp/tool.ts +247 -0
  40. package/src/mcp/transport.ts +583 -0
  41. package/src/mcp-config.ts +182 -0
  42. package/src/media/const.ts +24 -0
  43. package/src/media/index.ts +15 -0
  44. package/src/media/uploader.ts +240 -0
  45. package/src/monitor.ts +362 -40
  46. package/src/onboarding.ts +45 -6
  47. package/src/outbound.ts +116 -46
  48. package/src/timeout.ts +45 -0
  49. package/src/types/index.ts +1 -0
  50. package/src/types/message.ts +10 -1
  51. package/src/ws-adapter.ts +22 -0
@@ -0,0 +1,247 @@
1
+ /**
2
+ * wecom_mcp — 模拟 MCP 调用的 Agent Tool
3
+ *
4
+ * 通过 MCP Streamable HTTP 传输协议调用企业微信 MCP Server,
5
+ * 提供 list(列出所有工具)和 call(调用工具)两个操作。
6
+ *
7
+ * 在 skills 中的使用方式:
8
+ * wecom_mcp list <category>
9
+ * wecom_mcp call <category> <method> '<jsonArgs>'
10
+ *
11
+ * 示例:
12
+ * wecom_mcp list contact
13
+ * wecom_mcp call contact getContact '{}'
14
+ */
15
+
16
+ import { sendJsonRpc, clearCategoryCache, type McpToolInfo } from "./transport.js";
17
+ import { cleanSchemaForGemini } from "./schema.js";
18
+
19
+ // ============================================================================
20
+ // 类型定义
21
+ // ============================================================================
22
+
23
+ /** wecom_mcp 的入参 */
24
+ interface WeComToolsParams {
25
+ /** 操作类型:list | call */
26
+ action: "list" | "call";
27
+ /** MCP 品类,对应 mcpConfig 中的 key,如 doc、contact */
28
+ category: string;
29
+ /** 调用的 MCP 方法名(action=call 时必填) */
30
+ method?: string;
31
+ /** 调用 MCP 方法的 JSON 参数(action=call 时使用) */
32
+ args?: string | Record<string, unknown>;
33
+ }
34
+
35
+ // ============================================================================
36
+ // 响应构造辅助
37
+ // ============================================================================
38
+
39
+ /** 构造统一的文本响应结构 */
40
+ const textResult = (data: unknown) => ({
41
+ content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }],
42
+ details: undefined,
43
+ });
44
+
45
+ /** 构造错误响应 */
46
+ const errorResult = (err: unknown) => {
47
+ // 适配企业微信 API 返回的 { errcode, errmsg, help_message, help_instruction } 结构
48
+ if (err && typeof err === "object" && "errcode" in err) {
49
+ const { errcode, errmsg, help_message, help_instruction } = err as {
50
+ errcode: number;
51
+ errmsg?: string;
52
+ help_message?: string;
53
+ help_instruction?: string;
54
+ };
55
+ const result: Record<string, unknown> = {
56
+ error: errmsg ?? `错误码: ${errcode}`,
57
+ errcode,
58
+ };
59
+ // 如果有帮助信息,一并返回(帮助用户快速解决问题)
60
+ if (help_message) {
61
+ result.help_message = help_message;
62
+ }
63
+ if (help_instruction) {
64
+ result.help_instruction = help_instruction;
65
+ }
66
+ return textResult(result);
67
+ }
68
+
69
+ const message = err instanceof Error ? err.message : String(err);
70
+ return textResult({ error: message });
71
+ };
72
+
73
+ // ============================================================================
74
+ // list 操作:列出某品类的所有 MCP 工具
75
+ // ============================================================================
76
+
77
+ const handleList = async (category: string): Promise<unknown> => {
78
+ const result = await sendJsonRpc(category, "tools/list") as { tools?: McpToolInfo[] } | undefined;
79
+
80
+ const tools = result?.tools ?? [];
81
+ if (tools.length === 0) {
82
+ return { message: `品类 "${category}" 下暂无可用工具`, tools: [] };
83
+ }
84
+
85
+ return {
86
+ category,
87
+ count: tools.length,
88
+ tools: tools.map((t) => ({
89
+ name: t.name,
90
+ description: t.description ?? "",
91
+ // 清洗 inputSchema,内联 $ref/$defs 引用并移除 Gemini 不支持的关键词,
92
+ // 避免 Gemini 模型解析 function response 时报 400 错误
93
+ inputSchema: t.inputSchema ? cleanSchemaForGemini(t.inputSchema) : undefined,
94
+ })),
95
+ };
96
+ };
97
+
98
+ // ============================================================================
99
+ // call 操作:调用某品类的某个 MCP 工具
100
+ // ============================================================================
101
+
102
+ /**
103
+ * 需要触发缓存清理的业务错误码集合
104
+ *
105
+ * 这些错误码出现在 MCP 工具调用返回的 content 文本中(业务层面),
106
+ * 与 JSON-RPC 层面的错误码不同,需要在此处额外检测。
107
+ *
108
+ * - 850002: 机器人未被授权使用对应能力,需清理缓存以便下次重新拉取配置
109
+ * - 850003: 授权已过期,需清理缓存以便下次重新拉取配置和重新授权
110
+ */
111
+ const BIZ_CACHE_CLEAR_ERROR_CODES = new Set([850002, 850003]);
112
+
113
+ /**
114
+ * 检查 tools/call 的返回结果中是否包含需要清理缓存的业务错误码
115
+ *
116
+ * MCP Server 可能在正常的 JSON-RPC 响应中返回业务层错误,
117
+ * 这些错误被包裹在 result.content[].text 中,需要解析后判断。
118
+ */
119
+ const checkBizErrorAndClearCache = (result: unknown, category: string): void => {
120
+ if (!result || typeof result !== "object") return;
121
+
122
+ const { content } = result as { content?: Array<{ type: string; text?: string }> };
123
+ if (!Array.isArray(content)) return;
124
+
125
+ for (const item of content) {
126
+ if (item.type !== "text" || !item.text) continue;
127
+ try {
128
+ const parsed = JSON.parse(item.text) as Record<string, unknown>;
129
+ if (typeof parsed.errcode === "number" && BIZ_CACHE_CLEAR_ERROR_CODES.has(parsed.errcode)) {
130
+ const errMsg = parsed.errmsg ? ` (${parsed.errmsg})` : "";
131
+ console.log(`[mcp] 检测到业务错误码 ${parsed.errcode}${errMsg} (category="${category}"),清理缓存`);
132
+ if (parsed.errcode === 850003) {
133
+ console.log(`[mcp] 提示: 授权已过期,需要重新授权。请按照返回信息中的链接进行授权。`);
134
+ }
135
+ clearCategoryCache(category);
136
+ return;
137
+ }
138
+ } catch {
139
+ // text 不是 JSON 格式,跳过
140
+ }
141
+ }
142
+ };
143
+
144
+ const handleCall = async (
145
+ category: string,
146
+ method: string,
147
+ args: Record<string, unknown>,
148
+ ): Promise<unknown> => {
149
+ const result = await sendJsonRpc(category, "tools/call", {
150
+ name: method,
151
+ arguments: args,
152
+ });
153
+
154
+ // 检查业务层错误码,必要时清理缓存
155
+ checkBizErrorAndClearCache(result, category);
156
+
157
+ return result;
158
+ };
159
+
160
+ // ============================================================================
161
+ // 参数解析
162
+ // ============================================================================
163
+
164
+ /**
165
+ * 解析 args 参数:支持 JSON 字符串或直接的对象
166
+ */
167
+ const parseArgs = (args: string | Record<string, unknown> | undefined): Record<string, unknown> => {
168
+ if (!args) return {};
169
+ if (typeof args === "object") return args;
170
+ try {
171
+ return JSON.parse(args) as Record<string, unknown>;
172
+ } catch (err) {
173
+ const detail = err instanceof SyntaxError ? err.message : String(err);
174
+ throw new Error(`args 参数不是合法的 JSON: ${args} (${detail})`);
175
+ }
176
+ };
177
+
178
+ // ============================================================================
179
+ // 工具定义 & 导出
180
+ // ============================================================================
181
+
182
+ /**
183
+ * 创建 wecom_mcp Agent Tool 定义
184
+ */
185
+ export function createWeComMcpTool() {
186
+ return {
187
+ name: "wecom_mcp",
188
+ label: "企业微信 MCP 工具",
189
+ description: [
190
+ "通过 HTTP 直接调用企业微信 MCP Server。",
191
+ "支持两种操作:",
192
+ " - list: 列出指定品类的所有 MCP 工具",
193
+ " - call: 调用指定品类的某个 MCP 工具",
194
+ "",
195
+ "使用方式:",
196
+ " wecom_mcp list <category>",
197
+ " wecom_mcp call <category> <method> '<jsonArgs>'",
198
+ "",
199
+ "示例:",
200
+ " 列出 contact 品类所有工具:wecom_mcp list contact",
201
+ " 调用 contact 的 getContact:wecom_mcp call contact getContact '{}'",
202
+ ].join("\n"),
203
+ parameters: {
204
+ type: "object" as const,
205
+ properties: {
206
+ action: {
207
+ type: "string",
208
+ enum: ["list", "call"],
209
+ description: "操作类型:list(列出工具)或 call(调用工具)",
210
+ },
211
+ category: {
212
+ type: "string",
213
+ description: "MCP 品类名称,如 doc、contact 等,对应 mcpConfig 中的 key",
214
+ },
215
+ method: {
216
+ type: "string",
217
+ description: "要调用的 MCP 方法名(action=call 时必填)",
218
+ },
219
+ args: {
220
+ type: ["string", "object"],
221
+ description: "调用 MCP 方法的参数,可以是 JSON 字符串或对象(action=call 时使用,默认 {})",
222
+ },
223
+ },
224
+ required: ["action", "category"],
225
+ },
226
+ async execute(_toolCallId: string, params: unknown) {
227
+ const p = params as WeComToolsParams;
228
+ try {
229
+ switch (p.action) {
230
+ case "list":
231
+ return textResult(await handleList(p.category));
232
+ case "call": {
233
+ if (!p.method) {
234
+ return textResult({ error: "action 为 call 时必须提供 method 参数" });
235
+ }
236
+ const args = parseArgs(p.args);
237
+ return textResult(await handleCall(p.category, p.method, args));
238
+ }
239
+ default:
240
+ return textResult({ error: `未知操作类型: ${String(p.action)},支持 list 和 call` });
241
+ }
242
+ } catch (err) {
243
+ return errorResult(err);
244
+ }
245
+ },
246
+ };
247
+ }