@wecom/wecom-openclaw-plugin 2026.4.2-beta.0 → 2026.4.3-beta.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 +395 -10
- package/dist/index.d.ts +2 -4
- package/dist/index.js +58 -0
- package/dist/src/accounts.d.ts +57 -0
- package/dist/src/accounts.js +222 -0
- package/dist/src/agent/api-client.d.ts +94 -0
- package/dist/src/agent/api-client.js +292 -0
- package/dist/src/agent/handler.d.ts +63 -0
- package/dist/src/agent/handler.js +551 -0
- package/dist/src/agent/index.d.ts +5 -0
- package/dist/src/agent/index.js +5 -0
- package/dist/src/agent/webhook.d.ts +24 -0
- package/dist/src/agent/webhook.js +228 -0
- package/dist/src/agent/xml.d.ts +21 -0
- package/dist/src/agent/xml.js +41 -0
- package/dist/src/channel.js +579 -0
- package/dist/src/chat-queue.d.ts +31 -0
- package/dist/src/chat-queue.js +45 -0
- package/dist/{esm/types/src → src}/const.d.ts +46 -0
- package/dist/{esm/src → src}/const.js +77 -30
- package/dist/{esm/src → src}/dm-policy.js +6 -9
- package/dist/src/dynamic-agent.d.ts +37 -0
- package/dist/src/dynamic-agent.js +60 -0
- package/dist/src/dynamic-routing.d.ts +65 -0
- package/dist/src/dynamic-routing.js +58 -0
- package/dist/{esm/src → src}/group-policy.js +8 -22
- package/dist/src/http.d.ts +26 -0
- package/dist/src/http.js +93 -0
- package/dist/{esm/types/src → src}/interface.d.ts +0 -2
- package/dist/src/interface.js +4 -0
- package/dist/src/mcp/index.js +6 -0
- package/dist/{esm/src → src}/mcp/interceptors/biz-error.js +2 -6
- package/dist/{esm/src → src}/mcp/interceptors/index.js +6 -10
- package/dist/{esm/src → src}/mcp/interceptors/msg-media.js +6 -10
- package/dist/{esm/src → src}/mcp/interceptors/smartpage-create.js +2 -6
- package/dist/{esm/src → src}/mcp/interceptors/smartpage-export.js +2 -6
- package/dist/src/mcp/interceptors/types.js +7 -0
- package/dist/{esm/src → src}/mcp/schema.js +1 -4
- package/dist/{esm/src → src}/mcp/tool.js +4 -8
- package/dist/{esm/src → src}/mcp/transport.js +15 -16
- package/dist/{esm/src → src}/media-handler.js +6 -10
- package/dist/{esm/src → src}/media-uploader.js +64 -10
- package/dist/{esm/src → src}/message-parser.js +1 -4
- package/dist/{esm/types/src → src}/message-sender.d.ts +18 -0
- package/dist/{esm/src → src}/message-sender.js +45 -11
- package/dist/{esm/src → src}/monitor.js +145 -292
- package/dist/{esm/src → src}/onboarding.js +22 -26
- package/dist/{esm/types/src → src}/openclaw-compat.d.ts +49 -0
- package/dist/{esm/src → src}/openclaw-compat.js +133 -27
- package/dist/{esm/src → src}/reqid-store.js +4 -7
- package/dist/{esm/src → src}/runtime.js +2 -5
- package/dist/src/shared/command-auth.d.ts +23 -0
- package/dist/src/shared/command-auth.js +73 -0
- package/dist/src/shared/xml-parser.d.ts +46 -0
- package/dist/src/shared/xml-parser.js +210 -0
- package/dist/{esm/src → src}/state-dir-resolve.js +3 -7
- package/dist/{esm/src → src}/state-manager.js +84 -16
- package/dist/src/target.d.ts +35 -0
- package/dist/src/target.js +67 -0
- package/dist/src/template-card-manager.d.ts +55 -0
- package/dist/src/template-card-manager.js +205 -0
- package/dist/{esm/src → src}/template-card-parser.js +17 -8
- package/dist/{esm/src → src}/timeout.js +2 -5
- package/dist/src/types/account.d.ts +29 -0
- package/dist/src/types/account.js +4 -0
- package/dist/src/types/config.d.ts +91 -0
- package/dist/src/types/config.js +7 -0
- package/dist/src/types/constants.d.ts +42 -0
- package/dist/src/types/constants.js +42 -0
- package/dist/src/types/index.d.ts +7 -0
- package/dist/src/types/index.js +5 -0
- package/dist/src/types/message.d.ts +238 -0
- package/dist/src/types/message.js +5 -0
- package/dist/{esm/types/src → src}/utils.d.ts +41 -0
- package/dist/{esm/src → src}/utils.js +24 -10
- package/dist/src/version.d.ts +2 -0
- package/dist/src/version.js +24 -0
- package/dist/src/webhook/command-auth.d.ts +47 -0
- package/dist/src/webhook/command-auth.js +96 -0
- package/dist/src/webhook/gateway.d.ts +36 -0
- package/dist/src/webhook/gateway.js +235 -0
- package/dist/src/webhook/handler.d.ts +18 -0
- package/dist/src/webhook/handler.js +420 -0
- package/dist/src/webhook/helpers.d.ts +156 -0
- package/dist/src/webhook/helpers.js +833 -0
- package/dist/src/webhook/http.d.ts +26 -0
- package/dist/src/webhook/http.js +94 -0
- package/dist/src/webhook/index.d.ts +11 -0
- package/dist/src/webhook/index.js +14 -0
- package/dist/src/webhook/media.d.ts +29 -0
- package/dist/src/webhook/media.js +88 -0
- package/dist/src/webhook/monitor.d.ts +59 -0
- package/dist/src/webhook/monitor.js +1161 -0
- package/dist/src/webhook/state.d.ts +220 -0
- package/dist/src/webhook/state.js +491 -0
- package/dist/src/webhook/target.d.ts +41 -0
- package/dist/src/webhook/target.js +132 -0
- package/dist/src/webhook/types.d.ts +347 -0
- package/dist/src/webhook/types.js +33 -0
- package/dist/src/webhook/video-frame.d.ts +13 -0
- package/dist/src/webhook/video-frame.js +30 -0
- package/package.json +15 -7
- package/dist/cjs/index.js +0 -45
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/src/channel.js +0 -339
- package/dist/cjs/src/channel.js.map +0 -1
- package/dist/cjs/src/const.js +0 -137
- package/dist/cjs/src/const.js.map +0 -1
- package/dist/cjs/src/dm-policy.js +0 -89
- package/dist/cjs/src/dm-policy.js.map +0 -1
- package/dist/cjs/src/group-policy.js +0 -133
- package/dist/cjs/src/group-policy.js.map +0 -1
- package/dist/cjs/src/mcp/interceptors/biz-error.js +0 -70
- package/dist/cjs/src/mcp/interceptors/biz-error.js.map +0 -1
- package/dist/cjs/src/mcp/interceptors/index.js +0 -73
- package/dist/cjs/src/mcp/interceptors/index.js.map +0 -1
- package/dist/cjs/src/mcp/interceptors/msg-media.js +0 -159
- package/dist/cjs/src/mcp/interceptors/msg-media.js.map +0 -1
- package/dist/cjs/src/mcp/interceptors/smartpage-create.js +0 -157
- package/dist/cjs/src/mcp/interceptors/smartpage-create.js.map +0 -1
- package/dist/cjs/src/mcp/interceptors/smartpage-export.js +0 -93
- package/dist/cjs/src/mcp/interceptors/smartpage-export.js.map +0 -1
- package/dist/cjs/src/mcp/schema.js +0 -104
- package/dist/cjs/src/mcp/schema.js.map +0 -1
- package/dist/cjs/src/mcp/tool.js +0 -208
- package/dist/cjs/src/mcp/tool.js.map +0 -1
- package/dist/cjs/src/mcp/transport.js +0 -417
- package/dist/cjs/src/mcp/transport.js.map +0 -1
- package/dist/cjs/src/media-handler.js +0 -129
- package/dist/cjs/src/media-handler.js.map +0 -1
- package/dist/cjs/src/media-uploader.js +0 -284
- package/dist/cjs/src/media-uploader.js.map +0 -1
- package/dist/cjs/src/message-parser.js +0 -134
- package/dist/cjs/src/message-parser.js.map +0 -1
- package/dist/cjs/src/message-sender.js +0 -89
- package/dist/cjs/src/message-sender.js.map +0 -1
- package/dist/cjs/src/monitor.js +0 -991
- package/dist/cjs/src/monitor.js.map +0 -1
- package/dist/cjs/src/onboarding.js +0 -182
- package/dist/cjs/src/onboarding.js.map +0 -1
- package/dist/cjs/src/openclaw-compat.js +0 -400
- package/dist/cjs/src/openclaw-compat.js.map +0 -1
- package/dist/cjs/src/reqid-store.js +0 -88
- package/dist/cjs/src/reqid-store.js.map +0 -1
- package/dist/cjs/src/runtime.js +0 -16
- package/dist/cjs/src/runtime.js.map +0 -1
- package/dist/cjs/src/state-dir-resolve.js +0 -35
- package/dist/cjs/src/state-dir-resolve.js.map +0 -1
- package/dist/cjs/src/state-manager.js +0 -158
- package/dist/cjs/src/state-manager.js.map +0 -1
- package/dist/cjs/src/template-card-parser.js +0 -641
- package/dist/cjs/src/template-card-parser.js.map +0 -1
- package/dist/cjs/src/timeout.js +0 -42
- package/dist/cjs/src/timeout.js.map +0 -1
- package/dist/cjs/src/utils.js +0 -60
- package/dist/cjs/src/utils.js.map +0 -1
- package/dist/cjs/src/version.js +0 -26
- package/dist/cjs/src/version.js.map +0 -1
- package/dist/esm/index.js +0 -41
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/src/channel.js +0 -337
- package/dist/esm/src/channel.js.map +0 -1
- package/dist/esm/src/const.js.map +0 -1
- package/dist/esm/src/dm-policy.js.map +0 -1
- package/dist/esm/src/group-policy.js.map +0 -1
- package/dist/esm/src/mcp/interceptors/biz-error.js.map +0 -1
- package/dist/esm/src/mcp/interceptors/index.js.map +0 -1
- package/dist/esm/src/mcp/interceptors/msg-media.js.map +0 -1
- package/dist/esm/src/mcp/interceptors/smartpage-create.js.map +0 -1
- package/dist/esm/src/mcp/interceptors/smartpage-export.js.map +0 -1
- package/dist/esm/src/mcp/schema.js.map +0 -1
- package/dist/esm/src/mcp/tool.js.map +0 -1
- package/dist/esm/src/mcp/transport.js.map +0 -1
- package/dist/esm/src/media-handler.js.map +0 -1
- package/dist/esm/src/media-uploader.js.map +0 -1
- package/dist/esm/src/message-parser.js.map +0 -1
- package/dist/esm/src/message-sender.js.map +0 -1
- package/dist/esm/src/monitor.js.map +0 -1
- package/dist/esm/src/onboarding.js.map +0 -1
- package/dist/esm/src/openclaw-compat.js.map +0 -1
- package/dist/esm/src/reqid-store.js.map +0 -1
- package/dist/esm/src/runtime.js.map +0 -1
- package/dist/esm/src/state-dir-resolve.js.map +0 -1
- package/dist/esm/src/state-manager.js.map +0 -1
- package/dist/esm/src/template-card-parser.js.map +0 -1
- package/dist/esm/src/timeout.js.map +0 -1
- package/dist/esm/src/utils.js.map +0 -1
- package/dist/esm/src/version.js +0 -23
- package/dist/esm/src/version.js.map +0 -1
- package/dist/esm/types/index.d.ts +0 -9
- package/dist/esm/types/src/version.d.ts +0 -2
- /package/dist/{esm/types/src → src}/channel.d.ts +0 -0
- /package/dist/{esm/types/src → src}/dm-policy.d.ts +0 -0
- /package/dist/{esm/types/src → src}/group-policy.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/index.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/interceptors/biz-error.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/interceptors/index.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/interceptors/msg-media.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/interceptors/smartpage-create.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/interceptors/smartpage-export.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/interceptors/types.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/schema.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/tool.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/transport.d.ts +0 -0
- /package/dist/{esm/types/src → src}/media-handler.d.ts +0 -0
- /package/dist/{esm/types/src → src}/media-uploader.d.ts +0 -0
- /package/dist/{esm/types/src → src}/message-parser.d.ts +0 -0
- /package/dist/{esm/types/src → src}/monitor.d.ts +0 -0
- /package/dist/{esm/types/src → src}/onboarding.d.ts +0 -0
- /package/dist/{esm/types/src → src}/reqid-store.d.ts +0 -0
- /package/dist/{esm/types/src → src}/runtime.d.ts +0 -0
- /package/dist/{esm/types/src → src}/state-dir-resolve.d.ts +0 -0
- /package/dist/{esm/types/src → src}/state-manager.d.ts +0 -0
- /package/dist/{esm/types/src → src}/template-card-parser.d.ts +0 -0
- /package/dist/{esm/types/src → src}/timeout.d.ts +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"smartpage-create.js","sources":["../../../../../../../src/mcp/interceptors/smartpage-create.ts"],"sourcesContent":["/**\n * smartpage_create 请求拦截器\n *\n * 核心逻辑:\n * smartpage_create 的 pages 数组中,每个 page 可能包含 page_filepath 字段\n * (指向本地 markdown 文件),用于避免在命令行传递大段文本内容。\n * 本拦截器在 beforeCall 阶段遍历 pages 数组,逐个读取 page_filepath\n * 指向的本地文件内容,填入 page_content 字段,并移除 page_filepath。\n *\n * 入参约定:\n * wecom_mcp call doc smartpage_create '{\n * \"title\": \"主页标题\",\n * \"pages\": [\n * {\"page_title\": \"页面1\", \"page_filepath\": \"/tmp/page1.md\", \"content_type\": \"markdown\"},\n * {\"page_title\": \"页面2\", \"page_filepath\": \"/tmp/page2.md\", \"content_type\": \"markdown\"}\n * ]\n * }'\n *\n * 拦截器行为:\n * 1. 检测 args.pages 数组\n * 2. 校验文件大小:单文件不超过 10MB,所有文件总计不超过 20MB\n * 3. 遍历每个 page,若存在 page_filepath 则读取本地文件内容\n * 4. 将文件内容填入 page_content 字段,移除 page_filepath\n * 5. 返回修改后的完整 args\n *\n * 传给 MCP Server 的格式:\n * { \"title\": \"...\", \"pages\": [{\"page_title\": \"...\", \"page_content\": \"...\", \"content_type\": \"...\"}] }\n */\n\nimport * as fs from \"node:fs/promises\";\nimport type { CallInterceptor, CallContext, BeforeCallOptions } from \"./types.js\";\n\n// ============================================================================\n// 常量\n// ============================================================================\n\n/** 单个 page_filepath 文件大小上限:10MB */\nconst MAX_SINGLE_FILE_SIZE = 10 * 1024 * 1024;\n/** 所有 page_filepath 文件总大小上限:20MB */\nconst MAX_TOTAL_FILE_SIZE = 20 * 1024 * 1024;\n\n// ============================================================================\n// 内部辅助函数\n// ============================================================================\n\n/**\n * 校验所有 page_filepath 的文件大小\n *\n * 使用 fs.stat 在读取文件内容之前检查大小,避免超大文件被加载到内存。\n * - 单文件 > 10MB → 报错\n * - 所有文件累计 > 20MB → 报错\n */\nasync function validateFileSize(\n pages: Record<string, unknown>[],\n): Promise<void> {\n let totalSize = 0;\n\n for (let i = 0; i < pages.length; i++) {\n const filePath = pages[i].page_filepath;\n if (typeof filePath !== \"string\" || !filePath) continue;\n\n let stat: Awaited<ReturnType<typeof fs.stat>>;\n try {\n stat = await fs.stat(filePath);\n } catch (err) {\n // stat 失败不在这里处理,留给后续 readFile 阶段抛出更详细的错误\n continue;\n }\n\n if (stat.size > MAX_SINGLE_FILE_SIZE) {\n console.error(\n `[mcp] smartpage_create: pages[${i}] 文件 \"${filePath}\" ` +\n `大小 ${(stat.size / 1024 / 1024).toFixed(1)}MB 超过单文件上限 10MB`,\n );\n throw new Error(\"内容大小超出限制,无法创建\");\n }\n\n totalSize += stat.size;\n if (totalSize > MAX_TOTAL_FILE_SIZE) {\n console.error(\n `[mcp] smartpage_create: 累计文件大小 ${(totalSize / 1024 / 1024).toFixed(1)}MB ` +\n `超过总上限 20MB(在 pages[${i}] \"${filePath}\" 处超出)`,\n );\n throw new Error(\"内容大小超出限制,无法创建\");\n }\n }\n\n if (totalSize > 0) {\n console.log(\n `[mcp] smartpage_create: 文件大小校验通过,总计 ${(totalSize / 1024 / 1024).toFixed(2)}MB`,\n );\n }\n}\n\n/** 异步解析 pages 中的 page_filepath,返回 BeforeCallOptions */\nasync function resolvePages(\n ctx: CallContext,\n pages: Record<string, unknown>[],\n): Promise<BeforeCallOptions> {\n console.log(`[mcp] smartpage_create: 开始解析 ${pages.length} 个 page 的 page_filepath`);\n\n // 阶段 1:文件大小校验(stat 阶段,不读内容)\n await validateFileSize(pages);\n\n // 阶段 2:读取文件内容\n const resolvedPages = await Promise.all(\n pages.map(async (page: Record<string, unknown>, index: number) => {\n const filePath = page.page_filepath;\n if (typeof filePath !== \"string\" || !filePath) {\n // 该 page 没有 page_filepath,保留原样(可能已有 page_content)\n return page;\n }\n\n let fileContent: string;\n try {\n fileContent = await fs.readFile(filePath, \"utf-8\");\n } catch (err) {\n throw new Error(\n `smartpage_create: pages[${index}] 无法读取文件 \"${filePath}\": ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n console.log(\n `[mcp] smartpage_create: pages[${index}] 读取成功 \"${filePath}\" (${fileContent.length} chars)`,\n );\n\n // 构造新的 page 对象:填入 page_content,移除 page_filepath\n const { page_filepath: _, ...rest } = page;\n return { ...rest, page_content: fileContent };\n }),\n );\n\n console.log(`[mcp] smartpage_create: 所有 page_filepath 解析完成`);\n\n // 返回修改后的完整 args\n return {\n args: {\n ...ctx.args,\n pages: resolvedPages,\n },\n };\n}\n\n// ============================================================================\n// 拦截器实现\n// ============================================================================\n\nexport const smartpageCreateInterceptor: CallInterceptor = {\n name: \"smartpage-create\",\n\n /** 仅对 doc 品类的 smartpage_create 方法生效 */\n match: (ctx: CallContext) => ctx.category === \"doc\" && ctx.method === \"smartpage_create\",\n\n /** 遍历 pages 数组,逐个读取 page_filepath 填入 page_content */\n beforeCall(ctx: CallContext) {\n const pages = ctx.args.pages;\n if (!Array.isArray(pages) || pages.length === 0) {\n // 没有 pages 数组,不做拦截\n return undefined;\n }\n\n // 检查是否有任何 page 包含 page_filepath\n const hasFilePath = pages.some(\n (p: Record<string, unknown>) => typeof p.page_filepath === \"string\" && p.page_filepath,\n );\n if (!hasFilePath) {\n // 所有 page 都没有 page_filepath(可能直接传了 page_content),不做拦截\n return undefined;\n }\n\n return resolvePages(ctx, pages);\n },\n};\n"],"names":["fs"],"mappings":"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"}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var runtime = require('../../runtime.js');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* smartpage_get_export_result 响应拦截器
|
|
7
|
-
*
|
|
8
|
-
* 核心逻辑:
|
|
9
|
-
* MCP Server 返回的 smartpage_get_export_result 响应中,当 task_done=true 时
|
|
10
|
-
* 会包含 content 字段(markdown 文本内容)。该内容可能很大,直接返回给 LLM
|
|
11
|
-
* 会消耗大量 token。
|
|
12
|
-
*
|
|
13
|
-
* 本拦截器在 afterCall 阶段:
|
|
14
|
-
* 1. 检测 task_done=true 且存在 content 字段
|
|
15
|
-
* 2. 将 content 保存到本地文件(使用与 msg-media 一致的媒体目录)
|
|
16
|
-
* 3. 用 content_path(文件路径)替换 content 字段
|
|
17
|
-
*
|
|
18
|
-
* 这样 LLM 只看到轻量的文件路径信息,Skill 可通过文件路径读取完整内容。
|
|
19
|
-
*/
|
|
20
|
-
// ============================================================================
|
|
21
|
-
// 拦截器实现
|
|
22
|
-
// ============================================================================
|
|
23
|
-
const smartpageExportInterceptor = {
|
|
24
|
-
name: "smartpage-export",
|
|
25
|
-
/** 仅对 doc 品类的 smartpage_get_export_result 方法生效 */
|
|
26
|
-
match: (ctx) => ctx.category === "doc" && ctx.method === "smartpage_get_export_result",
|
|
27
|
-
/** 拦截响应:将 markdown content 保存为本地文件 */
|
|
28
|
-
async afterCall(_ctx, result) {
|
|
29
|
-
return interceptExportResponse(result);
|
|
30
|
-
},
|
|
31
|
-
};
|
|
32
|
-
// ============================================================================
|
|
33
|
-
// 内部实现
|
|
34
|
-
// ============================================================================
|
|
35
|
-
/**
|
|
36
|
-
* 拦截 smartpage_get_export_result 的 MCP 响应
|
|
37
|
-
*
|
|
38
|
-
* 1. 从 MCP result 的 content[].text 中提取业务 JSON
|
|
39
|
-
* 2. 检测 task_done=true 且存在 content 字段
|
|
40
|
-
* 3. 将 content(markdown 文本)通过 saveMediaBuffer 保存到本地媒体目录
|
|
41
|
-
* 4. 构造新响应:移除 content,添加 content_path
|
|
42
|
-
*/
|
|
43
|
-
async function interceptExportResponse(result) {
|
|
44
|
-
// 1. 提取 MCP result 中的 content 数组
|
|
45
|
-
const content = result?.content;
|
|
46
|
-
if (!Array.isArray(content))
|
|
47
|
-
return result;
|
|
48
|
-
const textItem = content.find((c) => c.type === "text" && typeof c.text === "string");
|
|
49
|
-
if (!textItem)
|
|
50
|
-
return result;
|
|
51
|
-
// 2. 解析业务 JSON
|
|
52
|
-
let bizData;
|
|
53
|
-
try {
|
|
54
|
-
bizData = JSON.parse(textItem.text);
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
// 非 JSON 格式,原样返回
|
|
58
|
-
return result;
|
|
59
|
-
}
|
|
60
|
-
// 3. 校验:errcode !== 0 或 task_done 不为 true 或无 content 时原样返回
|
|
61
|
-
if (bizData.errcode !== 0)
|
|
62
|
-
return result;
|
|
63
|
-
if (bizData.task_done !== true)
|
|
64
|
-
return result;
|
|
65
|
-
if (typeof bizData.content !== "string")
|
|
66
|
-
return result;
|
|
67
|
-
const markdownContent = bizData.content;
|
|
68
|
-
console.log(`[mcp] smartpage_get_export_result: 拦截 content (${markdownContent.length} chars),保存到本地文件`);
|
|
69
|
-
// 4. 将 markdown 内容通过 saveMediaBuffer 保存到本地媒体目录
|
|
70
|
-
// 使用 text/markdown 类型,与 msg-media 拦截器保持一致的路径管理
|
|
71
|
-
const buffer = Buffer.from(markdownContent, "utf-8");
|
|
72
|
-
const core = runtime.getWeComRuntime();
|
|
73
|
-
const saved = await core.channel.media.saveMediaBuffer(buffer, "text/markdown", "inbound", undefined, // maxBytes: markdown 文本通常不大,使用默认限制
|
|
74
|
-
"smartpage_export.md");
|
|
75
|
-
console.log(`[mcp] smartpage_get_export_result: 已保存到 ${saved.path}`);
|
|
76
|
-
// 5. 构造新响应:移除 content,添加 content_path
|
|
77
|
-
const newBizData = {
|
|
78
|
-
errcode: bizData.errcode,
|
|
79
|
-
errmsg: bizData.errmsg ?? "ok",
|
|
80
|
-
task_done: true,
|
|
81
|
-
content_path: saved.path,
|
|
82
|
-
};
|
|
83
|
-
// 6. 返回修改后的 MCP result 结构
|
|
84
|
-
return {
|
|
85
|
-
content: [{
|
|
86
|
-
type: "text",
|
|
87
|
-
text: JSON.stringify(newBizData),
|
|
88
|
-
}],
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
exports.smartpageExportInterceptor = smartpageExportInterceptor;
|
|
93
|
-
//# sourceMappingURL=smartpage-export.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"smartpage-export.js","sources":["../../../../../../../src/mcp/interceptors/smartpage-export.ts"],"sourcesContent":["/**\n * smartpage_get_export_result 响应拦截器\n *\n * 核心逻辑:\n * MCP Server 返回的 smartpage_get_export_result 响应中,当 task_done=true 时\n * 会包含 content 字段(markdown 文本内容)。该内容可能很大,直接返回给 LLM\n * 会消耗大量 token。\n *\n * 本拦截器在 afterCall 阶段:\n * 1. 检测 task_done=true 且存在 content 字段\n * 2. 将 content 保存到本地文件(使用与 msg-media 一致的媒体目录)\n * 3. 用 content_path(文件路径)替换 content 字段\n *\n * 这样 LLM 只看到轻量的文件路径信息,Skill 可通过文件路径读取完整内容。\n */\n\nimport { getWeComRuntime } from \"../../runtime.js\";\nimport type { CallInterceptor, CallContext } from \"./types.js\";\n\n// ============================================================================\n// 拦截器实现\n// ============================================================================\n\nexport const smartpageExportInterceptor: CallInterceptor = {\n name: \"smartpage-export\",\n\n /** 仅对 doc 品类的 smartpage_get_export_result 方法生效 */\n match: (ctx: CallContext) => ctx.category === \"doc\" && ctx.method === \"smartpage_get_export_result\",\n\n /** 拦截响应:将 markdown content 保存为本地文件 */\n async afterCall(_ctx: CallContext, result: unknown): Promise<unknown> {\n return interceptExportResponse(result);\n },\n};\n\n// ============================================================================\n// 内部实现\n// ============================================================================\n\n/**\n * 拦截 smartpage_get_export_result 的 MCP 响应\n *\n * 1. 从 MCP result 的 content[].text 中提取业务 JSON\n * 2. 检测 task_done=true 且存在 content 字段\n * 3. 将 content(markdown 文本)通过 saveMediaBuffer 保存到本地媒体目录\n * 4. 构造新响应:移除 content,添加 content_path\n */\nasync function interceptExportResponse(result: unknown): Promise<unknown> {\n // 1. 提取 MCP result 中的 content 数组\n const content = (result as Record<string, unknown>)?.content;\n if (!Array.isArray(content)) return result;\n\n const textItem = content.find(\n (c: Record<string, unknown>) => c.type === \"text\" && typeof c.text === \"string\",\n ) as { type: string; text: string } | undefined;\n if (!textItem) return result;\n\n // 2. 解析业务 JSON\n let bizData: Record<string, unknown>;\n try {\n bizData = JSON.parse(textItem.text) as Record<string, unknown>;\n } catch {\n // 非 JSON 格式,原样返回\n return result;\n }\n\n // 3. 校验:errcode !== 0 或 task_done 不为 true 或无 content 时原样返回\n if (bizData.errcode !== 0) return result;\n if (bizData.task_done !== true) return result;\n if (typeof bizData.content !== \"string\") return result;\n\n const markdownContent = bizData.content as string;\n\n console.log(\n `[mcp] smartpage_get_export_result: 拦截 content (${markdownContent.length} chars),保存到本地文件`,\n );\n\n // 4. 将 markdown 内容通过 saveMediaBuffer 保存到本地媒体目录\n // 使用 text/markdown 类型,与 msg-media 拦截器保持一致的路径管理\n const buffer = Buffer.from(markdownContent, \"utf-8\");\n const core = getWeComRuntime();\n const saved = await core.channel.media.saveMediaBuffer(\n buffer,\n \"text/markdown\",\n \"inbound\",\n undefined, // maxBytes: markdown 文本通常不大,使用默认限制\n \"smartpage_export.md\", // originalFilename\n );\n\n console.log(`[mcp] smartpage_get_export_result: 已保存到 ${saved.path}`);\n\n // 5. 构造新响应:移除 content,添加 content_path\n const newBizData = {\n errcode: bizData.errcode,\n errmsg: bizData.errmsg ?? \"ok\",\n task_done: true,\n content_path: saved.path,\n };\n\n // 6. 返回修改后的 MCP result 结构\n return {\n content: [{\n type: \"text\" as const,\n text: JSON.stringify(newBizData),\n }],\n };\n}\n"],"names":["getWeComRuntime"],"mappings":";;;;AAAA;;;;;;;;;;;;;;AAcG;AAKH;AACA;AACA;AAEO,MAAM,0BAA0B,GAAoB;AACzD,IAAA,IAAI,EAAE,kBAAkB;;AAGxB,IAAA,KAAK,EAAE,CAAC,GAAgB,KAAK,GAAG,CAAC,QAAQ,KAAK,KAAK,IAAI,GAAG,CAAC,MAAM,KAAK,6BAA6B;;AAGnG,IAAA,MAAM,SAAS,CAAC,IAAiB,EAAE,MAAe,EAAA;AAChD,QAAA,OAAO,uBAAuB,CAAC,MAAM,CAAC;IACxC,CAAC;;AAGH;AACA;AACA;AAEA;;;;;;;AAOG;AACH,eAAe,uBAAuB,CAAC,MAAe,EAAA;;AAEpD,IAAA,MAAM,OAAO,GAAI,MAAkC,EAAE,OAAO;AAC5D,IAAA,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;AAAE,QAAA,OAAO,MAAM;IAE1C,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAC3B,CAAC,CAA0B,KAAK,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAClC;AAC/C,IAAA,IAAI,CAAC,QAAQ;AAAE,QAAA,OAAO,MAAM;;AAG5B,IAAA,IAAI,OAAgC;AACpC,IAAA,IAAI;QACF,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAA4B;IAChE;AAAE,IAAA,MAAM;;AAEN,QAAA,OAAO,MAAM;IACf;;AAGA,IAAA,IAAI,OAAO,CAAC,OAAO,KAAK,CAAC;AAAE,QAAA,OAAO,MAAM;AACxC,IAAA,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI;AAAE,QAAA,OAAO,MAAM;AAC7C,IAAA,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ;AAAE,QAAA,OAAO,MAAM;AAEtD,IAAA,MAAM,eAAe,GAAG,OAAO,CAAC,OAAiB;IAEjD,OAAO,CAAC,GAAG,CACT,CAAA,+CAAA,EAAkD,eAAe,CAAC,MAAM,CAAA,eAAA,CAAiB,CAC1F;;;IAID,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC;AACpD,IAAA,MAAM,IAAI,GAAGA,uBAAe,EAAE;AAC9B,IAAA,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,CACpD,MAAM,EACN,eAAe,EACf,SAAS,EACT,SAAS;AACT,IAAA,qBAAqB,CACtB;IAED,OAAO,CAAC,GAAG,CAAC,CAAA,wCAAA,EAA2C,KAAK,CAAC,IAAI,CAAA,CAAE,CAAC;;AAGpE,IAAA,MAAM,UAAU,GAAG;QACjB,OAAO,EAAE,OAAO,CAAC,OAAO;AACxB,QAAA,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,IAAI;AAC9B,QAAA,SAAS,EAAE,IAAI;QACf,YAAY,EAAE,KAAK,CAAC,IAAI;KACzB;;IAGD,OAAO;AACL,QAAA,OAAO,EAAE,CAAC;AACR,gBAAA,IAAI,EAAE,MAAe;AACrB,gBAAA,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;aACjC,CAAC;KACH;AACH;;;;"}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* MCP Schema 清洗模块
|
|
5
|
-
*
|
|
6
|
-
* 负责内联 $ref/$defs 引用并移除 Gemini 不支持的 JSON Schema 关键词,
|
|
7
|
-
* 防止 Gemini 模型解析 function response 时报 400 错误。
|
|
8
|
-
*/
|
|
9
|
-
/** Gemini 不支持的 JSON Schema 关键词 */
|
|
10
|
-
const GEMINI_UNSUPPORTED_KEYWORDS = new Set([
|
|
11
|
-
"patternProperties", "additionalProperties", "$schema", "$id", "$ref", "$defs",
|
|
12
|
-
"definitions", "examples", "minLength", "maxLength", "minimum", "maximum",
|
|
13
|
-
"multipleOf", "pattern", "format", "minItems", "maxItems", "uniqueItems",
|
|
14
|
-
"minProperties", "maxProperties",
|
|
15
|
-
]);
|
|
16
|
-
/**
|
|
17
|
-
* 清洗 JSON Schema,内联 $ref 引用并移除 Gemini 不支持的关键词,
|
|
18
|
-
* 防止 Gemini 模型解析 function response 时报 400 错误。
|
|
19
|
-
*/
|
|
20
|
-
function cleanSchemaForGemini(schema) {
|
|
21
|
-
if (!schema || typeof schema !== "object")
|
|
22
|
-
return schema;
|
|
23
|
-
if (Array.isArray(schema))
|
|
24
|
-
return schema.map(cleanSchemaForGemini);
|
|
25
|
-
const obj = schema;
|
|
26
|
-
// 收集 $defs/definitions 用于后续 $ref 内联解析
|
|
27
|
-
const defs = {
|
|
28
|
-
...(obj.$defs && typeof obj.$defs === "object" ? obj.$defs : {}),
|
|
29
|
-
...(obj.definitions && typeof obj.definitions === "object" ? obj.definitions : {}),
|
|
30
|
-
};
|
|
31
|
-
return cleanWithDefs(obj, defs, new Set());
|
|
32
|
-
}
|
|
33
|
-
function cleanWithDefs(schema, defs, refStack) {
|
|
34
|
-
if (!schema || typeof schema !== "object")
|
|
35
|
-
return schema;
|
|
36
|
-
if (Array.isArray(schema))
|
|
37
|
-
return schema.map((item) => cleanWithDefs(item, defs, refStack));
|
|
38
|
-
const obj = schema;
|
|
39
|
-
// 合并当前层级的 $defs/definitions 到 defs 中
|
|
40
|
-
if (obj.$defs && typeof obj.$defs === "object") {
|
|
41
|
-
Object.assign(defs, obj.$defs);
|
|
42
|
-
}
|
|
43
|
-
if (obj.definitions && typeof obj.definitions === "object") {
|
|
44
|
-
Object.assign(defs, obj.definitions);
|
|
45
|
-
}
|
|
46
|
-
// 处理 $ref 引用:尝试内联解析
|
|
47
|
-
if (typeof obj.$ref === "string") {
|
|
48
|
-
const ref = obj.$ref;
|
|
49
|
-
if (refStack.has(ref))
|
|
50
|
-
return {}; // 防止循环引用
|
|
51
|
-
const match = ref.match(/^#\/(?:\$defs|definitions)\/(.+)$/);
|
|
52
|
-
if (match && match[1] && defs[match[1]]) {
|
|
53
|
-
const nextStack = new Set(refStack);
|
|
54
|
-
nextStack.add(ref);
|
|
55
|
-
return cleanWithDefs(defs[match[1]], defs, nextStack);
|
|
56
|
-
}
|
|
57
|
-
return {}; // 无法解析的 $ref,返回空对象
|
|
58
|
-
}
|
|
59
|
-
const cleaned = {};
|
|
60
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
61
|
-
if (GEMINI_UNSUPPORTED_KEYWORDS.has(key))
|
|
62
|
-
continue;
|
|
63
|
-
if (key === "const") {
|
|
64
|
-
cleaned.enum = [value];
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
if (key === "properties" && value && typeof value === "object" && !Array.isArray(value)) {
|
|
68
|
-
cleaned[key] = Object.fromEntries(Object.entries(value).map(([k, v]) => [
|
|
69
|
-
k, cleanWithDefs(v, defs, refStack),
|
|
70
|
-
]));
|
|
71
|
-
}
|
|
72
|
-
else if (key === "items" && value) {
|
|
73
|
-
cleaned[key] = Array.isArray(value)
|
|
74
|
-
? value.map((item) => cleanWithDefs(item, defs, refStack))
|
|
75
|
-
: cleanWithDefs(value, defs, refStack);
|
|
76
|
-
}
|
|
77
|
-
else if ((key === "anyOf" || key === "oneOf" || key === "allOf") && Array.isArray(value)) {
|
|
78
|
-
// 过滤掉 null 类型的变体
|
|
79
|
-
const nonNull = value.filter((v) => {
|
|
80
|
-
if (!v || typeof v !== "object")
|
|
81
|
-
return true;
|
|
82
|
-
const r = v;
|
|
83
|
-
return r.type !== "null";
|
|
84
|
-
});
|
|
85
|
-
if (nonNull.length === 1) {
|
|
86
|
-
// 只剩一个变体时直接内联
|
|
87
|
-
const single = cleanWithDefs(nonNull[0], defs, refStack);
|
|
88
|
-
if (single && typeof single === "object" && !Array.isArray(single)) {
|
|
89
|
-
Object.assign(cleaned, single);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
cleaned[key] = nonNull.map((v) => cleanWithDefs(v, defs, refStack));
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
cleaned[key] = value;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return cleaned;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
exports.cleanSchemaForGemini = cleanSchemaForGemini;
|
|
104
|
-
//# sourceMappingURL=schema.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sources":["../../../../../../src/mcp/schema.ts"],"sourcesContent":["/**\n * MCP Schema 清洗模块\n *\n * 负责内联 $ref/$defs 引用并移除 Gemini 不支持的 JSON Schema 关键词,\n * 防止 Gemini 模型解析 function response 时报 400 错误。\n */\n\n/** Gemini 不支持的 JSON Schema 关键词 */\nconst GEMINI_UNSUPPORTED_KEYWORDS = new Set([\n \"patternProperties\", \"additionalProperties\", \"$schema\", \"$id\", \"$ref\", \"$defs\",\n \"definitions\", \"examples\", \"minLength\", \"maxLength\", \"minimum\", \"maximum\",\n \"multipleOf\", \"pattern\", \"format\", \"minItems\", \"maxItems\", \"uniqueItems\",\n \"minProperties\", \"maxProperties\",\n]);\n\n/**\n * 清洗 JSON Schema,内联 $ref 引用并移除 Gemini 不支持的关键词,\n * 防止 Gemini 模型解析 function response 时报 400 错误。\n */\nexport function cleanSchemaForGemini(schema: unknown): unknown {\n if (!schema || typeof schema !== \"object\") return schema;\n if (Array.isArray(schema)) return schema.map(cleanSchemaForGemini);\n\n const obj = schema as Record<string, unknown>;\n\n // 收集 $defs/definitions 用于后续 $ref 内联解析\n const defs: Record<string, unknown> = {\n ...(obj.$defs && typeof obj.$defs === \"object\" ? obj.$defs as Record<string, unknown> : {}),\n ...(obj.definitions && typeof obj.definitions === \"object\" ? obj.definitions as Record<string, unknown> : {}),\n };\n\n return cleanWithDefs(obj, defs, new Set());\n}\n\nfunction cleanWithDefs(\n schema: unknown,\n defs: Record<string, unknown>,\n refStack: Set<string>,\n): unknown {\n if (!schema || typeof schema !== \"object\") return schema;\n if (Array.isArray(schema)) return schema.map((item) => cleanWithDefs(item, defs, refStack));\n\n const obj = schema as Record<string, unknown>;\n\n // 合并当前层级的 $defs/definitions 到 defs 中\n if (obj.$defs && typeof obj.$defs === \"object\") {\n Object.assign(defs, obj.$defs as Record<string, unknown>);\n }\n if (obj.definitions && typeof obj.definitions === \"object\") {\n Object.assign(defs, obj.definitions as Record<string, unknown>);\n }\n\n // 处理 $ref 引用:尝试内联解析\n if (typeof obj.$ref === \"string\") {\n const ref = obj.$ref;\n if (refStack.has(ref)) return {}; // 防止循环引用\n\n const match = ref.match(/^#\\/(?:\\$defs|definitions)\\/(.+)$/);\n if (match && match[1] && defs[match[1]]) {\n const nextStack = new Set(refStack);\n nextStack.add(ref);\n return cleanWithDefs(defs[match[1]], defs, nextStack);\n }\n return {}; // 无法解析的 $ref,返回空对象\n }\n\n const cleaned: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (GEMINI_UNSUPPORTED_KEYWORDS.has(key)) continue;\n\n if (key === \"const\") {\n cleaned.enum = [value];\n continue;\n }\n\n if (key === \"properties\" && value && typeof value === \"object\" && !Array.isArray(value)) {\n cleaned[key] = Object.fromEntries(\n Object.entries(value as Record<string, unknown>).map(([k, v]) => [\n k, cleanWithDefs(v, defs, refStack),\n ]),\n );\n } else if (key === \"items\" && value) {\n cleaned[key] = Array.isArray(value)\n ? value.map((item) => cleanWithDefs(item, defs, refStack))\n : cleanWithDefs(value, defs, refStack);\n } else if ((key === \"anyOf\" || key === \"oneOf\" || key === \"allOf\") && Array.isArray(value)) {\n // 过滤掉 null 类型的变体\n const nonNull = value.filter((v) => {\n if (!v || typeof v !== \"object\") return true;\n const r = v as Record<string, unknown>;\n return r.type !== \"null\";\n });\n if (nonNull.length === 1) {\n // 只剩一个变体时直接内联\n const single = cleanWithDefs(nonNull[0], defs, refStack);\n if (single && typeof single === \"object\" && !Array.isArray(single)) {\n Object.assign(cleaned, single as Record<string, unknown>);\n }\n } else {\n cleaned[key] = nonNull.map((v) => cleanWithDefs(v, defs, refStack));\n }\n } else {\n cleaned[key] = value;\n }\n }\n\n return cleaned;\n}\n"],"names":[],"mappings":"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"}
|
package/dist/cjs/src/mcp/tool.js
DELETED
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var transport = require('./transport.js');
|
|
4
|
-
var schema = require('./schema.js');
|
|
5
|
-
var index = require('./interceptors/index.js');
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* wecom_mcp — 模拟 MCP 调用的 Agent Tool
|
|
9
|
-
*
|
|
10
|
-
* 通过 MCP Streamable HTTP 传输协议调用企业微信 MCP Server,
|
|
11
|
-
* 提供 list(列出所有工具)和 call(调用工具)两个操作。
|
|
12
|
-
*
|
|
13
|
-
* 在 skills 中的使用方式:
|
|
14
|
-
* wecom_mcp list <category>
|
|
15
|
-
* wecom_mcp call <category> <method> '<jsonArgs>'
|
|
16
|
-
*
|
|
17
|
-
* 示例:
|
|
18
|
-
* wecom_mcp list contact
|
|
19
|
-
* wecom_mcp call contact getContact '{}'
|
|
20
|
-
*/
|
|
21
|
-
// ============================================================================
|
|
22
|
-
// 响应构造辅助
|
|
23
|
-
// ============================================================================
|
|
24
|
-
/** 构造统一的文本响应结构 */
|
|
25
|
-
const textResult = (data) => ({
|
|
26
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
27
|
-
});
|
|
28
|
-
/** 构造错误响应 */
|
|
29
|
-
const errorResult = (err) => {
|
|
30
|
-
// 适配企业微信 API 返回的 { errcode, errmsg } 结构
|
|
31
|
-
if (err && typeof err === "object" && "errcode" in err) {
|
|
32
|
-
const { errcode, errmsg } = err;
|
|
33
|
-
return textResult({ error: errmsg ?? `错误码: ${errcode}`, errcode });
|
|
34
|
-
}
|
|
35
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
36
|
-
return textResult({ error: message });
|
|
37
|
-
};
|
|
38
|
-
// ============================================================================
|
|
39
|
-
// list 操作:列出某品类的所有 MCP 工具
|
|
40
|
-
// ============================================================================
|
|
41
|
-
const handleList = async (category) => {
|
|
42
|
-
const result = await transport.sendJsonRpc(category, "tools/list");
|
|
43
|
-
const tools = result?.tools ?? [];
|
|
44
|
-
if (tools.length === 0) {
|
|
45
|
-
return { message: `品类 "${category}" 下暂无可用工具`, tools: [] };
|
|
46
|
-
}
|
|
47
|
-
return {
|
|
48
|
-
category,
|
|
49
|
-
count: tools.length,
|
|
50
|
-
tools: tools.map((t) => ({
|
|
51
|
-
name: t.name,
|
|
52
|
-
description: t.description ?? "",
|
|
53
|
-
// 清洗 inputSchema,内联 $ref/$defs 引用并移除 Gemini 不支持的关键词,
|
|
54
|
-
// 避免 Gemini 模型解析 function response 时报 400 错误
|
|
55
|
-
inputSchema: t.inputSchema ? schema.cleanSchemaForGemini(t.inputSchema) : undefined,
|
|
56
|
-
})),
|
|
57
|
-
};
|
|
58
|
-
};
|
|
59
|
-
// ============================================================================
|
|
60
|
-
// call 操作:调用某品类的某个 MCP 工具
|
|
61
|
-
// ============================================================================
|
|
62
|
-
const handleCall = async (category, method, args) => {
|
|
63
|
-
const ctx = { category, method, args };
|
|
64
|
-
const callStart = performance.now();
|
|
65
|
-
console.log(`[mcp] handleCall ${category}/${method} 入参: ${JSON.stringify(args)}`);
|
|
66
|
-
// 1. 收集拦截器的 beforeCall 配置(如超时时间、替换 args)
|
|
67
|
-
const { options, args: resolvedArgs } = await index.resolveBeforeCall(ctx);
|
|
68
|
-
const finalArgs = resolvedArgs ?? args;
|
|
69
|
-
if (resolvedArgs) {
|
|
70
|
-
console.log(`[mcp] handleCall ${category}/${method} 拦截器替换 args: ${JSON.stringify(resolvedArgs).slice(0, 500)}` +
|
|
71
|
-
(JSON.stringify(resolvedArgs).length > 500 ? "...(truncated)" : ""));
|
|
72
|
-
}
|
|
73
|
-
if (options) {
|
|
74
|
-
console.log(`[mcp] handleCall ${category}/${method} 拦截器选项: ${JSON.stringify(options)}`);
|
|
75
|
-
}
|
|
76
|
-
// 2. 执行 MCP 调用
|
|
77
|
-
const result = await transport.sendJsonRpc(category, "tools/call", {
|
|
78
|
-
name: method,
|
|
79
|
-
arguments: finalArgs,
|
|
80
|
-
}, options);
|
|
81
|
-
const rpcDone = performance.now();
|
|
82
|
-
const rpcMs = (rpcDone - callStart).toFixed(1);
|
|
83
|
-
const resultStr = JSON.stringify(result);
|
|
84
|
-
console.log(`[mcp] handleCall ${category}/${method} MCP 响应 (${rpcMs}ms): ${resultStr.slice(0, 800)}` +
|
|
85
|
-
(resultStr.length > 800 ? "...(truncated)" : ""));
|
|
86
|
-
// 3. 管道式执行 afterCall 拦截器(业务错误码检查、响应变换等)
|
|
87
|
-
const finalResult = await index.runAfterCall(ctx, result);
|
|
88
|
-
const totalMs = (performance.now() - callStart).toFixed(1);
|
|
89
|
-
const interceptMs = (performance.now() - rpcDone).toFixed(1);
|
|
90
|
-
// 有拦截器处理时打印详细耗时,否则只打印 RPC 耗时
|
|
91
|
-
if (finalResult !== result) {
|
|
92
|
-
const finalStr = JSON.stringify(finalResult);
|
|
93
|
-
console.log(`[mcp] handleCall ${category}/${method} afterCall 变换后 (${interceptMs}ms): ${finalStr.slice(0, 500)}` +
|
|
94
|
-
(finalStr.length > 500 ? "...(truncated)" : ""));
|
|
95
|
-
console.log(`[mcp] handleCall ${category}/${method} 总耗时: ${totalMs}ms` +
|
|
96
|
-
` (MCP请求: ${rpcMs}ms, 拦截处理: ${interceptMs}ms)`);
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
console.log(`[mcp] handleCall ${category}/${method} 耗时: ${rpcMs}ms`);
|
|
100
|
-
}
|
|
101
|
-
return finalResult;
|
|
102
|
-
};
|
|
103
|
-
// ============================================================================
|
|
104
|
-
// 参数解析
|
|
105
|
-
// ============================================================================
|
|
106
|
-
/**
|
|
107
|
-
* 解析 args 参数:支持 JSON 字符串或直接的对象
|
|
108
|
-
*/
|
|
109
|
-
const parseArgs = (args) => {
|
|
110
|
-
if (!args)
|
|
111
|
-
return {};
|
|
112
|
-
if (typeof args === "object")
|
|
113
|
-
return args;
|
|
114
|
-
try {
|
|
115
|
-
return JSON.parse(args);
|
|
116
|
-
}
|
|
117
|
-
catch (err) {
|
|
118
|
-
const detail = err instanceof SyntaxError ? err.message : String(err);
|
|
119
|
-
throw new Error(`args 参数不是合法的 JSON: ${args} (${detail})`);
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
// ============================================================================
|
|
123
|
-
// 工具定义 & 导出
|
|
124
|
-
// ============================================================================
|
|
125
|
-
/**
|
|
126
|
-
* 创建 wecom_mcp Agent Tool 定义
|
|
127
|
-
*/
|
|
128
|
-
function createWeComMcpTool() {
|
|
129
|
-
return {
|
|
130
|
-
name: "wecom_mcp",
|
|
131
|
-
label: "企业微信 MCP 工具",
|
|
132
|
-
description: [
|
|
133
|
-
"通过 HTTP 直接调用企业微信 MCP Server。",
|
|
134
|
-
"支持两种操作:",
|
|
135
|
-
" - list: 列出指定品类的所有 MCP 工具",
|
|
136
|
-
" - call: 调用指定品类的某个 MCP 工具",
|
|
137
|
-
"",
|
|
138
|
-
"使用方式:",
|
|
139
|
-
" wecom_mcp list <category>",
|
|
140
|
-
" wecom_mcp call <category> <method> '<jsonArgs>'",
|
|
141
|
-
"",
|
|
142
|
-
"示例:",
|
|
143
|
-
" 列出 contact 品类所有工具:wecom_mcp list contact",
|
|
144
|
-
" 调用 contact 的 getContact:wecom_mcp call contact getContact '{}'",
|
|
145
|
-
].join("\n"),
|
|
146
|
-
parameters: {
|
|
147
|
-
type: "object",
|
|
148
|
-
properties: {
|
|
149
|
-
action: {
|
|
150
|
-
type: "string",
|
|
151
|
-
enum: ["list", "call"],
|
|
152
|
-
description: "操作类型:list(列出工具)或 call(调用工具)",
|
|
153
|
-
},
|
|
154
|
-
category: {
|
|
155
|
-
type: "string",
|
|
156
|
-
description: "MCP 品类名称,如 doc、contact 等,对应 mcpConfig 中的 key",
|
|
157
|
-
},
|
|
158
|
-
method: {
|
|
159
|
-
type: "string",
|
|
160
|
-
description: "要调用的 MCP 方法名(action=call 时必填)",
|
|
161
|
-
},
|
|
162
|
-
args: {
|
|
163
|
-
type: ["string", "object"],
|
|
164
|
-
description: "调用 MCP 方法的参数,可以是 JSON 字符串或对象(action=call 时使用,默认 {})",
|
|
165
|
-
},
|
|
166
|
-
},
|
|
167
|
-
required: ["action", "category"],
|
|
168
|
-
},
|
|
169
|
-
async execute(_toolCallId, params) {
|
|
170
|
-
const p = params;
|
|
171
|
-
console.log(`[mcp] execute: action=${p.action}, category=${p.category}` +
|
|
172
|
-
(p.method ? `, method=${p.method}` : "") +
|
|
173
|
-
(p.args ? `, args=${typeof p.args === "string" ? p.args : JSON.stringify(p.args)}` : ""));
|
|
174
|
-
try {
|
|
175
|
-
let result;
|
|
176
|
-
switch (p.action) {
|
|
177
|
-
case "list":
|
|
178
|
-
result = textResult(await handleList(p.category));
|
|
179
|
-
break;
|
|
180
|
-
case "call": {
|
|
181
|
-
if (!p.method) {
|
|
182
|
-
result = textResult({ error: "action 为 call 时必须提供 method 参数" });
|
|
183
|
-
break;
|
|
184
|
-
}
|
|
185
|
-
const args = parseArgs(p.args);
|
|
186
|
-
result = textResult(await handleCall(p.category, p.method, args));
|
|
187
|
-
break;
|
|
188
|
-
}
|
|
189
|
-
default:
|
|
190
|
-
result = textResult({ error: `未知操作类型: ${String(p.action)},支持 list 和 call` });
|
|
191
|
-
}
|
|
192
|
-
console.log(`[mcp] execute: action=${p.action}, category=${p.category}` +
|
|
193
|
-
(p.method ? `, method=${p.method}` : "") +
|
|
194
|
-
` → 响应长度=${result.content[0].text.length} chars`);
|
|
195
|
-
return result;
|
|
196
|
-
}
|
|
197
|
-
catch (err) {
|
|
198
|
-
console.error(`[mcp] execute: action=${p.action}, category=${p.category}` +
|
|
199
|
-
(p.method ? `, method=${p.method}` : "") +
|
|
200
|
-
` → 异常: ${err instanceof Error ? err.message : String(err)}`);
|
|
201
|
-
return errorResult(err);
|
|
202
|
-
}
|
|
203
|
-
},
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
exports.createWeComMcpTool = createWeComMcpTool;
|
|
208
|
-
//# sourceMappingURL=tool.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tool.js","sources":["../../../../../../src/mcp/tool.ts"],"sourcesContent":["/**\n * wecom_mcp — 模拟 MCP 调用的 Agent Tool\n *\n * 通过 MCP Streamable HTTP 传输协议调用企业微信 MCP Server,\n * 提供 list(列出所有工具)和 call(调用工具)两个操作。\n *\n * 在 skills 中的使用方式:\n * wecom_mcp list <category>\n * wecom_mcp call <category> <method> '<jsonArgs>'\n *\n * 示例:\n * wecom_mcp list contact\n * wecom_mcp call contact getContact '{}'\n */\n\nimport { sendJsonRpc, type McpToolInfo } from \"./transport.js\";\nimport { cleanSchemaForGemini } from \"./schema.js\";\nimport { resolveBeforeCall, runAfterCall } from \"./interceptors/index.js\";\n\n// ============================================================================\n// 类型定义\n// ============================================================================\n\n/** wecom_mcp 的入参 */\ninterface WeComToolsParams {\n /** 操作类型:list | call */\n action: \"list\" | \"call\";\n /** MCP 品类,对应 mcpConfig 中的 key,如 doc、contact */\n category: string;\n /** 调用的 MCP 方法名(action=call 时必填) */\n method?: string;\n /** 调用 MCP 方法的 JSON 参数(action=call 时使用) */\n args?: string | Record<string, unknown>;\n}\n\n// ============================================================================\n// 响应构造辅助\n// ============================================================================\n\n/** 构造统一的文本响应结构 */\nconst textResult = (data: unknown) => ({\n content: [{ type: \"text\" as const, text: JSON.stringify(data, null, 2) }],\n});\n\n/** 构造错误响应 */\nconst errorResult = (err: unknown) => {\n // 适配企业微信 API 返回的 { errcode, errmsg } 结构\n if (err && typeof err === \"object\" && \"errcode\" in err) {\n const { errcode, errmsg } = err as { errcode: number; errmsg?: string };\n return textResult({ error: errmsg ?? `错误码: ${errcode}`, errcode });\n }\n\n const message = err instanceof Error ? err.message : String(err);\n return textResult({ error: message });\n};\n\n// ============================================================================\n// list 操作:列出某品类的所有 MCP 工具\n// ============================================================================\n\nconst handleList = async (category: string): Promise<unknown> => {\n const result = await sendJsonRpc(category, \"tools/list\") as { tools?: McpToolInfo[] } | undefined;\n\n const tools = result?.tools ?? [];\n if (tools.length === 0) {\n return { message: `品类 \"${category}\" 下暂无可用工具`, tools: [] };\n }\n\n return {\n category,\n count: tools.length,\n tools: tools.map((t) => ({\n name: t.name,\n description: t.description ?? \"\",\n // 清洗 inputSchema,内联 $ref/$defs 引用并移除 Gemini 不支持的关键词,\n // 避免 Gemini 模型解析 function response 时报 400 错误\n inputSchema: t.inputSchema ? cleanSchemaForGemini(t.inputSchema) : undefined,\n })),\n };\n};\n\n// ============================================================================\n// call 操作:调用某品类的某个 MCP 工具\n// ============================================================================\n\nconst handleCall = async (\n category: string,\n method: string,\n args: Record<string, unknown>,\n): Promise<unknown> => {\n const ctx = { category, method, args };\n const callStart = performance.now();\n\n console.log(`[mcp] handleCall ${category}/${method} 入参: ${JSON.stringify(args)}`);\n\n // 1. 收集拦截器的 beforeCall 配置(如超时时间、替换 args)\n const { options, args: resolvedArgs } = await resolveBeforeCall(ctx);\n const finalArgs = resolvedArgs ?? args;\n\n if (resolvedArgs) {\n console.log(\n `[mcp] handleCall ${category}/${method} 拦截器替换 args: ${JSON.stringify(resolvedArgs).slice(0, 500)}` +\n (JSON.stringify(resolvedArgs).length > 500 ? \"...(truncated)\" : \"\"),\n );\n }\n if (options) {\n console.log(`[mcp] handleCall ${category}/${method} 拦截器选项: ${JSON.stringify(options)}`);\n }\n\n // 2. 执行 MCP 调用\n const result = await sendJsonRpc(category, \"tools/call\", {\n name: method,\n arguments: finalArgs,\n }, options);\n\n const rpcDone = performance.now();\n const rpcMs = (rpcDone - callStart).toFixed(1);\n\n const resultStr = JSON.stringify(result);\n console.log(\n `[mcp] handleCall ${category}/${method} MCP 响应 (${rpcMs}ms): ${resultStr.slice(0, 800)}` +\n (resultStr.length > 800 ? \"...(truncated)\" : \"\"),\n );\n\n // 3. 管道式执行 afterCall 拦截器(业务错误码检查、响应变换等)\n const finalResult = await runAfterCall(ctx, result);\n\n const totalMs = (performance.now() - callStart).toFixed(1);\n const interceptMs = (performance.now() - rpcDone).toFixed(1);\n\n // 有拦截器处理时打印详细耗时,否则只打印 RPC 耗时\n if (finalResult !== result) {\n const finalStr = JSON.stringify(finalResult);\n console.log(\n `[mcp] handleCall ${category}/${method} afterCall 变换后 (${interceptMs}ms): ${finalStr.slice(0, 500)}` +\n (finalStr.length > 500 ? \"...(truncated)\" : \"\"),\n );\n console.log(\n `[mcp] handleCall ${category}/${method} 总耗时: ${totalMs}ms` +\n ` (MCP请求: ${rpcMs}ms, 拦截处理: ${interceptMs}ms)`,\n );\n } else {\n console.log(`[mcp] handleCall ${category}/${method} 耗时: ${rpcMs}ms`);\n }\n\n return finalResult;\n};\n\n// ============================================================================\n// 参数解析\n// ============================================================================\n\n/**\n * 解析 args 参数:支持 JSON 字符串或直接的对象\n */\nconst parseArgs = (args: string | Record<string, unknown> | undefined): Record<string, unknown> => {\n if (!args) return {};\n if (typeof args === \"object\") return args;\n try {\n return JSON.parse(args) as Record<string, unknown>;\n } catch (err) {\n const detail = err instanceof SyntaxError ? err.message : String(err);\n throw new Error(`args 参数不是合法的 JSON: ${args} (${detail})`);\n }\n};\n\n// ============================================================================\n// 工具定义 & 导出\n// ============================================================================\n\n/**\n * 创建 wecom_mcp Agent Tool 定义\n */\nexport function createWeComMcpTool() {\n return {\n name: \"wecom_mcp\",\n label: \"企业微信 MCP 工具\",\n description: [\n \"通过 HTTP 直接调用企业微信 MCP Server。\",\n \"支持两种操作:\",\n \" - list: 列出指定品类的所有 MCP 工具\",\n \" - call: 调用指定品类的某个 MCP 工具\",\n \"\",\n \"使用方式:\",\n \" wecom_mcp list <category>\",\n \" wecom_mcp call <category> <method> '<jsonArgs>'\",\n \"\",\n \"示例:\",\n \" 列出 contact 品类所有工具:wecom_mcp list contact\",\n \" 调用 contact 的 getContact:wecom_mcp call contact getContact '{}'\",\n ].join(\"\\n\"),\n parameters: {\n type: \"object\" as const,\n properties: {\n action: {\n type: \"string\",\n enum: [\"list\", \"call\"],\n description: \"操作类型:list(列出工具)或 call(调用工具)\",\n },\n category: {\n type: \"string\",\n description: \"MCP 品类名称,如 doc、contact 等,对应 mcpConfig 中的 key\",\n },\n method: {\n type: \"string\",\n description: \"要调用的 MCP 方法名(action=call 时必填)\",\n },\n args: {\n type: [\"string\", \"object\"],\n description: \"调用 MCP 方法的参数,可以是 JSON 字符串或对象(action=call 时使用,默认 {})\",\n },\n },\n required: [\"action\", \"category\"],\n },\n async execute(_toolCallId: string, params: unknown) {\n const p = params as WeComToolsParams;\n console.log(\n `[mcp] execute: action=${p.action}, category=${p.category}` +\n (p.method ? `, method=${p.method}` : \"\") +\n (p.args ? `, args=${typeof p.args === \"string\" ? p.args : JSON.stringify(p.args)}` : \"\"),\n );\n try {\n let result: ReturnType<typeof textResult>;\n switch (p.action) {\n case \"list\":\n result = textResult(await handleList(p.category));\n break;\n case \"call\": {\n if (!p.method) {\n result = textResult({ error: \"action 为 call 时必须提供 method 参数\" });\n break;\n }\n const args = parseArgs(p.args);\n result = textResult(await handleCall(p.category, p.method, args));\n break;\n }\n default:\n result = textResult({ error: `未知操作类型: ${String(p.action)},支持 list 和 call` });\n }\n console.log(\n `[mcp] execute: action=${p.action}, category=${p.category}` +\n (p.method ? `, method=${p.method}` : \"\") +\n ` → 响应长度=${result.content[0].text.length} chars`,\n );\n return result;\n } catch (err) {\n console.error(\n `[mcp] execute: action=${p.action}, category=${p.category}` +\n (p.method ? `, method=${p.method}` : \"\") +\n ` → 异常: ${err instanceof Error ? err.message : String(err)}`,\n );\n return errorResult(err);\n }\n },\n };\n}\n"],"names":["sendJsonRpc","cleanSchemaForGemini","resolveBeforeCall","runAfterCall"],"mappings":"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"}
|