server.js 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363
  1. #!/usr/bin/env node
  2. const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js');
  3. const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
  4. const { z } = require('zod');
  5. const { redactSecret } = require('./providers/fmode-wecom-gateway');
  6. const { searchQiweiApis, getQiweiApiDoc, callQiweiApi } = require('./tools/qiwei-api-catalog-run');
  7. const {
  8. qiweiLoginStatus,
  9. qiweiLoginStart,
  10. qiweiLoginCheck,
  11. qiweiLoginVerify
  12. } = require('./tools/qiwei-login-run');
  13. const {
  14. qiweiSubscriptionStatus,
  15. qiweiSubscribe,
  16. qiweiSubscriptionAutoRenew
  17. } = require('./tools/qiwei-subscription-run');
  18. const {
  19. qiweiOfficialStatus,
  20. qiweiOfficialPrepare,
  21. qiweiOfficialHelp,
  22. qiweiOfficialCall
  23. } = require('./tools/wecom-official-cli-run');
  24. const {
  25. qiweiBatchAddFriends,
  26. qiweiAutoCreateGroup,
  27. qiweiCheckFriendStatus,
  28. qiweiGetCustomerProfile
  29. } = require('./tools/qiwei-customer-ops-run');
  30. const {
  31. qiweiSyncExternalGroups,
  32. qiweiListExternalGroups,
  33. qiweiAnalyzeGroupMembers,
  34. qiweiConfirmExternalGroup,
  35. qiweiAddExternalGroup,
  36. qiweiConfigureGroupKeywords,
  37. qiweiRejectExternalGroup,
  38. qiweiSyncGroupMessages
  39. } = require('./tools/qiwei-group-management-run');
  40. const {
  41. qiweiGroupOpsGetContext,
  42. qiweiGroupOpsGeneratePlan,
  43. qiweiGroupOpsBatchGeneratePlans,
  44. qiweiGroupOpsGenerateCopy,
  45. qiweiGroupOpsReviewMessages,
  46. qiweiGroupOpsDailyBrief,
  47. qiweiGroupOpsManagePlaybook,
  48. qiweiGroupOpsExecuteItem,
  49. qiweiGroupOpsUpdateFinding,
  50. qiweiGroupOpsInsights,
  51. qiweiGroupOpsManageTask,
  52. qiweiGroupOpsAutomation,
  53. qiweiGroupOpsManageAccess
  54. } = require('./tools/qiwei-group-operations-run');
  55. const {
  56. qiweiPrepareCustomerPortrait,
  57. qiweiUpdateCustomerPortrait,
  58. qiweiSaveCustomerPortrait,
  59. qiweiBatchUpdateCustomerPortrait,
  60. qiweiBatchSaveCustomerPortrait,
  61. qiweiExportCustomerPortraits,
  62. qiweiAddCustomerTags,
  63. qiweiRemoveCustomerTags,
  64. qiweiListCustomerTags,
  65. qiweiListAllTags,
  66. qiweiSyncPersonalLabels,
  67. qiweiCreatePersonalLabel,
  68. qiweiUpdatePersonalLabel,
  69. qiweiDeletePersonalLabel,
  70. qiweiApplyPersonalLabels
  71. } = require('./tools/qiwei-portrait-tags-run');
  72. const {
  73. qiweiDistillBroker,
  74. qiweiPrepareBrokerPlaybook,
  75. qiweiSaveBrokerPlaybook,
  76. qiweiBatchDistillBroker,
  77. qiweiBatchSaveBrokerPlaybook,
  78. qiweiExportBrokerPlaybooks,
  79. qiweiGetBrokerPlaybook
  80. } = require('./tools/qiwei-broker-playbook-run');
  81. const {
  82. qiweiPreviewTransferPackage,
  83. qiweiExecuteTransfer
  84. } = require('./tools/qiwei-customer-transfer-run');
  85. const {
  86. qiweiGoalCreatePlan,
  87. qiweiGoalGet,
  88. qiweiGoalUpdateTask,
  89. qiweiGoalImportMeetingActions
  90. } = require('./tools/qiwei-goal-management-run');
  91. const { qiweiTranscribeVoice } = require('./tools/qiwei-voice-run');
  92. const {
  93. qiweiWebhookStatus,
  94. qiweiWebhookDiscover,
  95. qiweiWebhookServerStart,
  96. qiweiWebhookServerStop,
  97. qiweiWebhookAutoSetup,
  98. qiweiWebhookSetup,
  99. qiweiRelayConfig,
  100. qiweiRelaySaveConfig,
  101. qiweiRelayRegister,
  102. qiweiRelayConnect
  103. } = require('./tools/qiwei-webhook-relay-run');
  104. const {
  105. qiweiAgentDashboardStart,
  106. qiweiAgentBindController,
  107. qiweiAgentStatus,
  108. qiweiAgentListConversations,
  109. qiweiAgentSessionGuide,
  110. qiweiAgentCustomerIntelligence,
  111. qiweiAgentInbox,
  112. qiweiAgentListener,
  113. qiweiAgentSetGlobal,
  114. qiweiAgentGenerateDraft
  115. } = require('./tools/qiwei-agent-control-run');
  116. const goalTaskSchema = z.object({
  117. title: z.string(),
  118. owner: z.string().optional(),
  119. deadline: z.string().optional(),
  120. priority: z.string().optional(),
  121. acceptance: z.string().optional(),
  122. status: z.enum(['planned', 'in_progress', 'blocked', 'done', 'cancelled']).optional(),
  123. note: z.string().optional(),
  124. blockedReason: z.string().optional()
  125. });
  126. const goalMilestoneSchema = z.object({
  127. title: z.string(),
  128. owner: z.string().optional(),
  129. deadline: z.string().optional(),
  130. acceptance: z.string().optional(),
  131. status: z.enum(['planned', 'in_progress', 'blocked', 'done', 'cancelled']).optional(),
  132. tasks: z.array(goalTaskSchema).optional()
  133. });
  134. function asToolResult(result) {
  135. const normalized = result && typeof result === 'object' ? result : { status: 'ok', assistantMessage: String(result) };
  136. return {
  137. content: [
  138. {
  139. type: 'text',
  140. text: normalized.assistantMessage || JSON.stringify(normalized, null, 2)
  141. }
  142. ],
  143. structuredContent: normalized,
  144. isError: normalized.status === 'error'
  145. };
  146. }
  147. function wrap(handler) {
  148. return async (input = {}) => {
  149. try {
  150. return asToolResult(await handler(input));
  151. } catch (error) {
  152. const safeMessage = redactSecret(error && error.message ? error.message : error);
  153. return asToolResult({
  154. status: 'error',
  155. assistantMessage: `工具执行异常:${safeMessage}`,
  156. errors: [{ message: safeMessage }]
  157. });
  158. }
  159. };
  160. }
  161. function createServer() {
  162. const server = new McpServer({
  163. name: 'enterprise-wechat-assistant',
  164. version: '0.4.0'
  165. });
  166. server.registerTool(
  167. 'qiwei_api_search',
  168. {
  169. title: '检索企业微信开放接口清单',
  170. description: [
  171. '检索企业微信能力的全量接口清单(100+ 个接口:登录/设备、消息收发、群管理、联系人、朋友圈、标签、会话、文件等模块)。',
  172. '当需要对企业微信做任何操作(发消息、拉群、加好友、同步消息、发朋友圈等)时,先来这里查有哪些接口。',
  173. '入参 query(关键词,中英文均可)、module(模块 key:login/client/msg/room/contact/sns/label/session/user/cloud)、tag、limit 均可选;都不传返回全量清单。'
  174. ].join(' '),
  175. inputSchema: {
  176. query: z.string().optional(),
  177. module: z.string().optional(),
  178. tag: z.string().optional(),
  179. limit: z.number().int().min(1).max(200).optional()
  180. }
  181. },
  182. wrap(searchQiweiApis)
  183. );
  184. server.registerTool(
  185. 'qiwei_api_doc',
  186. {
  187. title: '读取企业微信接口的详细参数文档',
  188. description: '读取清单中某个企业微信接口的完整参数文档(参数表、必填项、返回字段说明、调用模板)。入参 id(如 msg.sendText,来自 qiwei_api_search)或 method 路径(如 /msg/sendText)。',
  189. inputSchema: {
  190. id: z.string().optional(),
  191. method: z.string().optional()
  192. }
  193. },
  194. wrap(getQiweiApiDoc)
  195. );
  196. server.registerTool(
  197. 'qiwei_api_call',
  198. {
  199. title: '调用企业微信开放接口',
  200. description: [
  201. '通过 Fmode 网关转发的企业微信接口执行请求,接口访问凭据和设备上下文由 Fmode 网关管理。',
  202. '推荐传 id(清单接口 id)+ params;未登记接口可传 rawMethod(如 /msg/sendText)+ params。',
  203. '客户端只需 Fmode 鉴权 token 和 uid,不接触企业微信接口访问凭据。'
  204. ].join(' '),
  205. inputSchema: {
  206. id: z.string().optional(),
  207. rawMethod: z.string().optional(),
  208. params: z.record(z.any()).optional(),
  209. uid: z.string().optional(),
  210. authToken: z.string().optional(),
  211. apiBase: z.string().optional()
  212. }
  213. },
  214. wrap(callQiweiApi)
  215. );
  216. server.registerTool(
  217. 'qiwei_login_status',
  218. {
  219. title: '查询企业微信登录/配置状态',
  220. description: '通过 Fmode 网关转发的企业微信接口查询当前 uid 对应的设备在线状态。技能包安装后、或任何接口报设备/登录错误时先调用。',
  221. inputSchema: {
  222. uid: z.string().optional(),
  223. authToken: z.string().optional(),
  224. apiBase: z.string().optional()
  225. }
  226. },
  227. wrap(qiweiLoginStatus)
  228. );
  229. server.registerTool(
  230. 'qiwei_login_start',
  231. {
  232. title: '开始企业微信扫码登录',
  233. description: [
  234. '登录两步流程入口:默认在本地端口(默认 4310)启动流程页服务并打开浏览器,页面自动推进:未开通订阅时先展示套餐页(每号 ¥200/月,飞马余额扣费),开通后自动生成二维码并每 3 秒轮询扫码状态,需要验证码时页面提示输入。',
  235. '传 flowUi=false 回退到静态模式:校验订阅(未开通返回 needs_subscription 并生成套餐页)后生成二维码保存到 outputs/login/qiwei-login-qrcode.png(openBrowser=false 可关闭自动打开)。',
  236. '需要有效企微包月订阅和可用席位;无需提供企业微信接口访问凭据、代理或设备参数。'
  237. ].join(' '),
  238. inputSchema: {
  239. uid: z.string().optional(),
  240. authToken: z.string().optional(),
  241. apiBase: z.string().optional(),
  242. openBrowser: z.boolean().optional(),
  243. flowUi: z.boolean().optional(),
  244. flowPort: z.number().int().optional()
  245. }
  246. },
  247. wrap(qiweiLoginStart)
  248. );
  249. server.registerTool(
  250. 'qiwei_login_check',
  251. {
  252. title: '检测扫码登录状态',
  253. description: '通过 Fmode 网关转发的企业微信接口轮询扫码登录状态(用户扫码后每 3-5 秒调一次)。状态 10 表示需要 6 位验证码;manual=true 时先尝试免扫码登录。',
  254. inputSchema: {
  255. manual: z.boolean().optional(),
  256. uid: z.string().optional(),
  257. authToken: z.string().optional(),
  258. apiBase: z.string().optional()
  259. }
  260. },
  261. wrap(qiweiLoginCheck)
  262. );
  263. server.registerTool(
  264. 'qiwei_login_verify',
  265. {
  266. title: '提交扫码登录的 6 位验证码',
  267. description: '当 qiwei_login_check 返回状态 10(需要验证码)时,向用户索要手机端显示的 6 位验证码并通过本工具提交,随后再调 qiwei_login_check 确认登录成功。',
  268. inputSchema: {
  269. code: z.string(),
  270. uid: z.string().optional(),
  271. authToken: z.string().optional(),
  272. apiBase: z.string().optional()
  273. }
  274. },
  275. wrap(qiweiLoginVerify)
  276. );
  277. server.registerTool(
  278. 'qiwei_subscription_status',
  279. {
  280. title: '查询企微包月订阅与席位状态',
  281. description: '查询企微包月订阅、到期时间、自动续费、已购/已用席位和飞马余额。首次登录或收到 402/403 时先调用。',
  282. inputSchema: {
  283. authToken: z.string().optional(),
  284. apiBase: z.string().optional()
  285. }
  286. },
  287. wrap(qiweiSubscriptionStatus)
  288. );
  289. server.registerTool(
  290. 'qiwei_subscribe',
  291. {
  292. title: '开通、续费或增购企微席位',
  293. description: '使用飞马余额开通或续费企微包月订阅。1 个席位对应 1 个企业微信账号;seats 不传时默认 1。',
  294. inputSchema: {
  295. seats: z.number().int().min(1).optional(),
  296. authToken: z.string().optional(),
  297. apiBase: z.string().optional()
  298. }
  299. },
  300. wrap(qiweiSubscribe)
  301. );
  302. server.registerTool(
  303. 'qiwei_subscription_auto_renew',
  304. {
  305. title: '设置企微订阅自动续费',
  306. description: '开启或关闭企微包月订阅到期自动续费。',
  307. inputSchema: {
  308. autoRenew: z.boolean(),
  309. authToken: z.string().optional(),
  310. apiBase: z.string().optional()
  311. }
  312. },
  313. wrap(qiweiSubscriptionAutoRenew)
  314. );
  315. server.registerTool(
  316. 'qiwei_official_status',
  317. {
  318. title: '检查企业微信官方 CLI 状态',
  319. description: '检查固定版本官方 CLI 是否已下载、版本是否正确,以及官方机器人扫码授权是否完成。此工具与 Fmode 网关登录状态完全独立。',
  320. inputSchema: {}
  321. },
  322. wrap(qiweiOfficialStatus)
  323. );
  324. server.registerTool(
  325. 'qiwei_official_prepare',
  326. {
  327. title: '下载企业微信官方 CLI',
  328. description: '将固定版本 @wecom/cli 下载到用户级缓存并校验。不会全局安装,不修改官方 CLI,也不会改动 Fmode 网关转发的企业微信接口通道。',
  329. inputSchema: {
  330. force: z.boolean().optional()
  331. }
  332. },
  333. wrap(qiweiOfficialPrepare)
  334. );
  335. server.registerTool(
  336. 'qiwei_official_help',
  337. {
  338. title: '读取企业微信官方 CLI 帮助',
  339. description: '读取官方 CLI 总帮助、category 工具列表或具体 method 的参数 schema。category/method 级帮助需要先完成官方 CLI 初始化。',
  340. inputSchema: {
  341. category: z.enum(['contact', 'doc', 'meeting', 'msg', 'schedule', 'todo']).optional(),
  342. method: z.string().regex(/^[a-z][a-z0-9_]*$/).optional()
  343. }
  344. },
  345. wrap(qiweiOfficialHelp)
  346. );
  347. server.registerTool(
  348. 'qiwei_official_call',
  349. {
  350. title: '调用企业微信官方 CLI 能力',
  351. description: '通过固定版本官方 CLI 调用通讯录、文档、会议、消息、日程或待办。参数以结构化 JSON 传递,不经过 shell,也不经过 Fmode 网关。',
  352. inputSchema: {
  353. category: z.enum(['contact', 'doc', 'meeting', 'msg', 'schedule', 'todo']),
  354. method: z.string().regex(/^[a-z][a-z0-9_]*$/),
  355. args: z.record(z.any()).optional(),
  356. timeoutMs: z.number().int().min(1000).max(300000).optional()
  357. }
  358. },
  359. wrap(qiweiOfficialCall)
  360. );
  361. server.registerTool(
  362. 'qiwei_batch_add_friends',
  363. {
  364. title: '批量搜索并添加企微好友',
  365. description: [
  366. '迁移自 Qiwei 项目的批量加好友能力。按手机号搜索联系人,依据 qiwei-endpoints.json 中的 /contact/searchContact、/contact/addSearchWxContact、/contact/addSearchWxWorkContact 调用 Fmode 网关。',
  367. '不修改 qiwei_login_* 和 qiwei_api_* 既有流程;需要已登录设备 guid。支持 customers、phones,或传入 .xlsx filePath 读取 Excel。'
  368. ].join(' '),
  369. inputSchema: {
  370. guid: z.string().optional(),
  371. customers: z.array(z.object({
  372. phone: z.string(),
  373. name: z.string().optional(),
  374. greeting: z.string().optional(),
  375. verifyText: z.string().optional(),
  376. groupName: z.string().optional(),
  377. supportMemberIds: z.array(z.string()).optional()
  378. })).optional(),
  379. phones: z.array(z.string()).optional(),
  380. filePath: z.string().optional(),
  381. greeting: z.string().optional(),
  382. defaultGreeting: z.string().optional(),
  383. rateLimitPerMinute: z.number().int().min(1).max(60).optional(),
  384. maxAttempts: z.number().int().min(1).max(5).optional(),
  385. uid: z.string().optional(),
  386. authToken: z.string().optional(),
  387. apiBase: z.string().optional()
  388. }
  389. },
  390. wrap(qiweiBatchAddFriends)
  391. );
  392. server.registerTool(
  393. 'qiwei_auto_create_group',
  394. {
  395. title: '自动创建企微服务群',
  396. description: [
  397. '迁移自 Qiwei 项目的自动建群能力。基于 qiwei-endpoints.json 中的 /room/createRoom、/room/modifyRoomName、/room/inviteRoomMember、/msg/sendText 执行建群、改群名、邀请协作成员和发送欢迎语。',
  398. '不修改目标包已有登录、订阅、通用 API 调用流程;需要已登录设备 guid。'
  399. ].join(' '),
  400. inputSchema: {
  401. guid: z.string().optional(),
  402. customerUserId: z.string().optional(),
  403. externalUserId: z.string().optional(),
  404. memberList: z.array(z.string()).optional(),
  405. supportMemberIds: z.array(z.string()).optional(),
  406. groupName: z.string().optional(),
  407. welcomeText: z.string().optional(),
  408. isOuterRoom: z.number().int().optional(),
  409. uid: z.string().optional(),
  410. authToken: z.string().optional(),
  411. apiBase: z.string().optional()
  412. }
  413. },
  414. wrap(qiweiAutoCreateGroup)
  415. );
  416. server.registerTool(
  417. 'qiwei_check_friend_status',
  418. {
  419. title: '检查好友申请状态',
  420. description: '按手机号批量检查联系人搜索状态,判断对方是否已是好友、已被其他人添加、未添加或未找到。需要已登录设备 guid。',
  421. inputSchema: {
  422. guid: z.string().optional(),
  423. customers: z.array(z.object({
  424. phone: z.string(),
  425. name: z.string().optional()
  426. })).optional(),
  427. phones: z.array(z.string()).optional(),
  428. externalUserIds: z.array(z.string()).optional(),
  429. uid: z.string().optional(),
  430. authToken: z.string().optional(),
  431. apiBase: z.string().optional()
  432. }
  433. },
  434. wrap(qiweiCheckFriendStatus)
  435. );
  436. server.registerTool(
  437. 'qiwei_get_customer_profile',
  438. {
  439. title: '查询客户档案',
  440. description: '按手机号或 externalUserId 查询客户档案:搜索联系人、拉取外部联系人列表、拉取群列表,并读取本地已保存的画像文件。需要已登录设备 guid。',
  441. inputSchema: {
  442. guid: z.string().optional(),
  443. phone: z.string().optional(),
  444. externalUserId: z.string().optional(),
  445. uid: z.string().optional(),
  446. authToken: z.string().optional(),
  447. apiBase: z.string().optional()
  448. }
  449. },
  450. wrap(qiweiGetCustomerProfile)
  451. );
  452. server.registerTool(
  453. 'qiwei_sync_external_groups',
  454. {
  455. title: '同步外部群列表',
  456. description: '从多个来源扫描群列表:当前账号创建的群(self)、最近会话中的群(session)、消息流反推的群(messages)或全部(all)。支持按可配置关键词自动分类客户群,结果写入 outputs/groups/。',
  457. inputSchema: {
  458. guid: z.string().optional(),
  459. scope: z.enum(['self', 'session', 'messages', 'all']).optional(),
  460. scanFromMessages: z.boolean().optional(),
  461. includeInternalGroups: z.boolean().optional(),
  462. customerKeywords: z.array(z.string()).optional(),
  463. matchMode: z.enum(['any', 'threshold']).optional(),
  464. threshold: z.number().int().min(1).optional(),
  465. highConfidenceTerms: z.array(z.string()).optional(),
  466. autoClassify: z.boolean().optional(),
  467. maxPages: z.number().int().min(1).max(300).optional(),
  468. uid: z.string().optional(),
  469. authToken: z.string().optional(),
  470. apiBase: z.string().optional(),
  471. fromMessages: z.boolean().optional().describe('已废弃,请使用 scope=messages 或 scanFromMessages')
  472. }
  473. },
  474. wrap(qiweiSyncExternalGroups)
  475. );
  476. server.registerTool(
  477. 'qiwei_list_external_groups',
  478. {
  479. title: '列出外部群',
  480. description: '读取最近一次同步的群列表和已确认/已拒绝映射,返回每个群的状态(IMPORTED / SUGGESTED / AUTO_CONFIRMED / CONFIRMED / REJECTED),支持按关键词、状态、来源过滤。',
  481. inputSchema: {
  482. keyword: z.string().optional(),
  483. status: z.enum(['IMPORTED', 'SUGGESTED', 'AUTO_CONFIRMED', 'CONFIRMED', 'REJECTED']).optional(),
  484. source: z.enum(['roomList', 'session', 'messages']).optional(),
  485. includeRejected: z.boolean().optional(),
  486. customerKeywords: z.array(z.string()).optional(),
  487. matchMode: z.enum(['any', 'threshold']).optional(),
  488. threshold: z.number().int().min(1).optional(),
  489. highConfidenceTerms: z.array(z.string()).optional()
  490. }
  491. },
  492. wrap(qiweiListExternalGroups)
  493. );
  494. server.registerTool(
  495. 'qiwei_analyze_group_members',
  496. {
  497. title: '分析群详情',
  498. description: '对指定的 roomId 列表调用 /room/batchGetRoomDetail,按群名关键词判断哪些可能是客户群;可拉取最近 N 条消息辅助识别。',
  499. inputSchema: {
  500. guid: z.string().optional(),
  501. roomIds: z.array(z.string()),
  502. customerKeywords: z.array(z.string()).optional(),
  503. matchMode: z.enum(['any', 'threshold']).optional(),
  504. threshold: z.number().int().min(1).optional(),
  505. highConfidenceTerms: z.array(z.string()).optional(),
  506. autoClassify: z.boolean().optional(),
  507. previewMessages: z.number().int().min(0).max(100).optional(),
  508. updateSnapshot: z.boolean().optional(),
  509. uid: z.string().optional(),
  510. authToken: z.string().optional(),
  511. apiBase: z.string().optional()
  512. }
  513. },
  514. wrap(qiweiAnalyzeGroupMembers)
  515. );
  516. server.registerTool(
  517. 'qiwei_confirm_external_group',
  518. {
  519. title: '确认外部群为客户群',
  520. description: '对最近一次同步列表中的某个 roomId 标记为 CONFIRMED,并关联 customerId / externalUserId。映射写入 outputs/groups/confirmed-mapping.json。',
  521. inputSchema: {
  522. roomId: z.string(),
  523. customerId: z.string().optional(),
  524. externalUserId: z.string().optional(),
  525. customerName: z.string().optional()
  526. }
  527. },
  528. wrap(qiweiConfirmExternalGroup)
  529. );
  530. server.registerTool(
  531. 'qiwei_add_external_group',
  532. {
  533. title: '手动添加外部群',
  534. description: '直接按 roomId 添加外部群并标记为 CONFIRMED,关联 customerId / externalUserId。适用于同步列表之外的群。',
  535. inputSchema: {
  536. roomId: z.string(),
  537. roomName: z.string().optional(),
  538. customerId: z.string().optional(),
  539. externalUserId: z.string().optional(),
  540. customerName: z.string().optional()
  541. }
  542. },
  543. wrap(qiweiAddExternalGroup)
  544. );
  545. server.registerTool(
  546. 'qiwei_configure_group_keywords',
  547. {
  548. title: '配置客户群关键词',
  549. description: '持久化配置客户群识别关键词、匹配模式(any/threshold)、阈值和高置信度词,写入 outputs/groups/customer-keywords.json。传 reset=true 可恢复默认。',
  550. inputSchema: {
  551. keywords: z.array(z.string()).optional(),
  552. highConfidenceTerms: z.array(z.string()).optional(),
  553. matchMode: z.enum(['any', 'threshold']).optional(),
  554. threshold: z.number().int().min(1).optional(),
  555. reset: z.boolean().optional()
  556. }
  557. },
  558. wrap(qiweiConfigureGroupKeywords)
  559. );
  560. server.registerTool(
  561. 'qiwei_reject_external_group',
  562. {
  563. title: '拒绝外部群',
  564. description: '将最近一次同步列表中的某个 roomId 标记为 REJECTED,写入 outputs/groups/rejected-mapping.json。',
  565. inputSchema: {
  566. roomId: z.string(),
  567. reason: z.string().optional()
  568. }
  569. },
  570. wrap(qiweiRejectExternalGroup)
  571. );
  572. server.registerTool(
  573. 'qiwei_sync_group_messages',
  574. {
  575. title: '同步群消息',
  576. description: '对指定 roomId 列表或所有已确认客户群调用 /msg/syncMsg 分页同步历史消息,按 room 写入 outputs/messages/。',
  577. inputSchema: {
  578. guid: z.string().optional(),
  579. roomIds: z.array(z.string()).optional(),
  580. maxPages: z.number().int().min(1).max(300).optional(),
  581. maxMessagesPerRoom: z.number().int().min(1).max(5000).optional(),
  582. uid: z.string().optional(),
  583. authToken: z.string().optional(),
  584. apiBase: z.string().optional()
  585. }
  586. },
  587. wrap(qiweiSyncGroupMessages)
  588. );
  589. server.registerTool('qiwei_group_ops_get_context', {
  590. title: '读取社群运营上下文',
  591. description: '读取当前账号的群运营总览,或指定群的主档、已发布 SOP、计划和质检风险。用于制定计划、写话术和生成管理简报前获取事实。',
  592. inputSchema: { accountKey: z.string().optional(), guid: z.string().optional(), roomId: z.string().optional(), date: z.string().optional(), group: z.record(z.any()).optional() }
  593. }, wrap(qiweiGroupOpsGetContext));
  594. server.registerTool('qiwei_group_ops_generate_plan', {
  595. title: '生成客户群运营计划',
  596. description: '依据已发布 SOP 或显式计划项,为指定客户群生成每日运营计划草稿。结果只进入待审核状态,不自动发送。',
  597. inputSchema: { accountKey: z.string().optional(), guid: z.string().optional(), roomId: z.string(), roomName: z.string().optional(), planDate: z.string().optional(), playbookId: z.string().optional(), items: z.array(z.record(z.any())).optional(), actor: z.string().optional() }
  598. }, wrap(qiweiGroupOpsGeneratePlan));
  599. server.registerTool('qiwei_group_ops_batch_generate_plans', {
  600. title: '批量生成客户群运营计划',
  601. description: '按门店或显式群列表批量生成指定日期的运营计划。每个群独立幂等,失败项不会影响成功项,全部结果进入待审核。',
  602. inputSchema: { accountKey: z.string().optional(), guid: z.string().optional(), planDate: z.string().optional(), storeId: z.string().optional(), playbookId: z.string().optional(), groups: z.array(z.record(z.any())).optional(), actor: z.string().optional() }
  603. }, wrap(qiweiGroupOpsBatchGeneratePlans));
  604. server.registerTool('qiwei_group_ops_generate_copy', {
  605. title: '生成并检查群运营话术',
  606. description: '替换话术变量并执行缺失变量、敏感承诺等基础规则检查。仅返回可审核草稿。',
  607. inputSchema: { content: z.string().optional(), template: z.string().optional(), variables: z.record(z.any()).optional(), riskTerms: z.array(z.string()).optional() }
  608. }, wrap(qiweiGroupOpsGenerateCopy));
  609. server.registerTool('qiwei_group_ops_review_messages', {
  610. title: '质检客户群消息',
  611. description: '对指定客户群消息执行投诉风险和超时未回应检查,每个结论保留原消息证据。',
  612. inputSchema: { accountKey: z.string().optional(), guid: z.string().optional(), roomId: z.string(), messages: z.array(z.record(z.any())).optional(), startAt: z.string().optional(), endAt: z.string().optional(), slaMinutes: z.number().int().positive().optional(), riskTerms: z.array(z.string()).optional(), actor: z.string().optional() }
  613. }, wrap(qiweiGroupOpsReviewMessages));
  614. server.registerTool('qiwei_group_ops_daily_brief', {
  615. title: '生成社群运营每日简报',
  616. description: '汇总今日应执行、已完成、待审核和高风险事项,并返回可下钻的计划与质检数据。',
  617. inputSchema: { accountKey: z.string().optional(), guid: z.string().optional(), date: z.string().optional() }
  618. }, wrap(qiweiGroupOpsDailyBrief));
  619. server.registerTool('qiwei_group_ops_manage_playbook', {
  620. title: '管理群运营 SOP',
  621. description: '列出、创建、创建新版本、发布或回滚群运营 SOP。发布与回滚属于受控写操作,调用前必须向用户确认。',
  622. inputSchema: { accountKey: z.string().optional(), guid: z.string().optional(), action: z.enum(['list','create','new_version','versions','preview','publish','rollback']), playbookId: z.string().optional(), name: z.string().optional(), groupType: z.string().optional(), content: z.record(z.any()).optional(), scope: z.record(z.any()).optional(), version: z.number().int().positive().optional(), changeNote: z.string().optional(), confirmed: z.boolean().optional(), actor: z.string().optional() }
  623. }, wrap(qiweiGroupOpsManagePlaybook));
  624. server.registerTool('qiwei_group_ops_execute_item', {
  625. title: '审核或记录群运营计划项',
  626. description: '批准、拒绝、跳过或记录计划项已由人工发送。真实自动发送在接口回执验证前保持关闭。',
  627. inputSchema: { accountKey: z.string().optional(), guid: z.string().optional(), itemId: z.string(), action: z.enum(['approve','reject','skip','mark_sent','send']), content: z.string().optional(), reason: z.string().optional(), externalMessageId: z.string().optional(), actor: z.string().optional() }
  628. }, wrap(qiweiGroupOpsExecuteItem));
  629. server.registerTool('qiwei_group_ops_update_finding', {
  630. title: '处理群质检事项',
  631. description: '认领、确认、关闭或标记群质检误报,并保留处理审计。',
  632. inputSchema: { accountKey: z.string().optional(), guid: z.string().optional(), findingId: z.string(), status: z.enum(['open','acknowledged','resolved','false_positive']).optional(), assignee: z.string().optional(), resolution: z.string().optional(), actor: z.string().optional() }
  633. }, wrap(qiweiGroupOpsUpdateFinding));
  634. server.registerTool('qiwei_group_ops_insights', {
  635. title: '分析社群运营与配置质检规则',
  636. description: '查看跨门店执行对比、群活跃趋势、生命周期建议、优秀话术和质检反馈;也可更新当前账号的 SLA、风险词和禁止承诺。',
  637. inputSchema: { accountKey: z.string().optional(), guid: z.string().optional(), action: z.enum(['get','update_settings']).optional(), roomId: z.string().optional(), date: z.string().optional(), slaMinutes: z.number().int().positive().optional(), riskTerms: z.array(z.string()).optional(), forbiddenClaims: z.array(z.string()).optional(), actor: z.string().optional() }
  638. }, wrap(qiweiGroupOpsInsights));
  639. server.registerTool('qiwei_group_ops_manage_task', {
  640. title: '管理社群整改任务',
  641. description: '创建、查询、推进社群整改任务,并在负责人精确匹配且人工确认后同步为真实企微官方待办。',
  642. inputSchema: { accountKey: z.string().optional(), guid: z.string().optional(), action: z.enum(['list','create','update','sync_official']), taskId: z.string().optional(), findingId: z.string().optional(), roomId: z.string().optional(), title: z.string().optional(), owner: z.string().optional(), dueAt: z.string().optional(), priority: z.enum(['low','medium','high','urgent']).optional(), status: z.enum(['open','in_progress','done','cancelled']).optional(), remindType: z.number().int().optional(), confirmed: z.boolean().optional(), actor: z.string().optional() }
  643. }, wrap(qiweiGroupOpsManageTask));
  644. server.registerTool('qiwei_group_ops_automation', {
  645. title: '管理社群受控自动化',
  646. description: '创建和激活自动化策略,配置白名单、允许类型、时间窗、频控、灰度与熔断,执行 dry-run 或在全部安全条件满足时受控发送。默认暂停且真实发送环境开关关闭。',
  647. inputSchema: { accountKey: z.string().optional(), guid: z.string().optional(), action: z.enum(['status','create_policy','activate','pause','resume','reset_breaker','preview','run']), policyId: z.string().optional(), policy: z.record(z.any()).optional(), changeNote: z.string().optional(), itemIds: z.array(z.string()).optional(), limit: z.number().int().positive().max(500).optional(), at: z.string().optional(), execute: z.boolean().optional(), confirmed: z.boolean().optional(), actor: z.string().optional() }
  648. }, wrap(qiweiGroupOpsAutomation));
  649. server.registerTool('qiwei_group_ops_manage_access', {
  650. title: '管理社群运营角色权限',
  651. description: '配置或查看当前企微账号下的总部管理员、区域/门店经理、执行员工和审计者权限。首次初始化必须显式确认。',
  652. inputSchema: { accountKey: z.string().optional(), guid: z.string().optional(), requester: z.string().optional(), principal: z.string().optional(), actor: z.string().optional(), action: z.enum(['list','upsert']).optional(), role: z.enum(['headquarters_admin','regional_manager','store_manager','executor','auditor']).optional(), storeScope: z.array(z.string()).optional(), status: z.enum(['active','disabled']).optional(), bootstrapConfirmed: z.boolean().optional() }
  653. }, wrap(qiweiGroupOpsManageAccess));
  654. server.registerTool(
  655. 'qiwei_prepare_customer_portrait',
  656. {
  657. title: '准备客户画像分析上下文',
  658. description: '从 outputs/messages/ 中收集指定 externalUserId 发送的消息,生成画像分析上下文并写入 outputs/portraits/,供 Agent 分析后保存。',
  659. inputSchema: {
  660. externalUserId: z.string(),
  661. roomIds: z.array(z.string()).optional()
  662. }
  663. },
  664. wrap(qiweiPrepareCustomerPortrait)
  665. );
  666. server.registerTool(
  667. 'qiwei_update_customer_portrait',
  668. {
  669. title: '更新客户画像',
  670. description: '默认 Agent 驱动:准备上下文并返回给调用方分析。传 aiMode=keyword 时直接基于关键词生成简版画像并保存。',
  671. inputSchema: {
  672. externalUserId: z.string(),
  673. aiMode: z.enum(['keyword']).optional(),
  674. roomIds: z.array(z.string()).optional()
  675. }
  676. },
  677. wrap(qiweiUpdateCustomerPortrait)
  678. );
  679. server.registerTool(
  680. 'qiwei_save_customer_portrait',
  681. {
  682. title: '保存客户画像',
  683. description: '将 Agent 分析后的画像 JSON 保存到 outputs/portraits/<externalUserId>.json。',
  684. inputSchema: {
  685. externalUserId: z.string(),
  686. portrait: z.record(z.any()),
  687. source: z.string().optional(),
  688. messageCount: z.number().int().optional()
  689. }
  690. },
  691. wrap(qiweiSaveCustomerPortrait)
  692. );
  693. server.registerTool(
  694. 'qiwei_batch_update_customer_portrait',
  695. {
  696. title: '批量更新客户画像',
  697. description: '对多个 externalUserId 调用 qiwei_update_customer_portrait。',
  698. inputSchema: {
  699. externalUserIds: z.array(z.string()),
  700. aiMode: z.enum(['keyword']).optional(),
  701. roomIds: z.array(z.string()).optional()
  702. }
  703. },
  704. wrap(qiweiBatchUpdateCustomerPortrait)
  705. );
  706. server.registerTool(
  707. 'qiwei_batch_save_customer_portrait',
  708. {
  709. title: '批量保存客户画像',
  710. description: '批量保存多个客户的画像。',
  711. inputSchema: {
  712. items: z.array(z.object({ externalUserId: z.string(), portrait: z.record(z.any()), source: z.string().optional() }))
  713. }
  714. },
  715. wrap(qiweiBatchSaveCustomerPortrait)
  716. );
  717. server.registerTool(
  718. 'qiwei_export_customer_portraits',
  719. {
  720. title: '导出客户画像',
  721. description: '将 outputs/portraits/ 中保存的画像导出为 Excel。未传 externalUserIds 时导出全部。',
  722. inputSchema: {
  723. externalUserIds: z.array(z.string()).optional()
  724. }
  725. },
  726. wrap(qiweiExportCustomerPortraits)
  727. );
  728. server.registerTool(
  729. 'qiwei_add_customer_tags',
  730. {
  731. title: '添加客户标签',
  732. description: '为 externalUserId 添加本地标签,去重保存到 outputs/tags/<externalUserId>.json。',
  733. inputSchema: {
  734. externalUserId: z.string(),
  735. tags: z.array(z.string())
  736. }
  737. },
  738. wrap(qiweiAddCustomerTags)
  739. );
  740. server.registerTool(
  741. 'qiwei_remove_customer_tags',
  742. {
  743. title: '移除客户标签',
  744. description: '为 externalUserId 移除本地标签。',
  745. inputSchema: {
  746. externalUserId: z.string(),
  747. tags: z.array(z.string())
  748. }
  749. },
  750. wrap(qiweiRemoveCustomerTags)
  751. );
  752. server.registerTool(
  753. 'qiwei_list_customer_tags',
  754. {
  755. title: '列出客户标签',
  756. description: '读取 outputs/tags/<externalUserId>.json 返回标签列表。',
  757. inputSchema: {
  758. externalUserId: z.string()
  759. }
  760. },
  761. wrap(qiweiListCustomerTags)
  762. );
  763. server.registerTool(
  764. 'qiwei_list_all_tags',
  765. {
  766. title: '列出所有本地标签',
  767. description: '汇总所有客户的本地标签并去重返回。',
  768. inputSchema: {}
  769. },
  770. wrap(qiweiListAllTags)
  771. );
  772. server.registerTool(
  773. 'qiwei_sync_personal_labels',
  774. {
  775. title: '同步企微个人标签',
  776. description: '调用 /label/syncLabelList 同步企微个人标签列表。',
  777. inputSchema: {
  778. guid: z.string().optional(),
  779. uid: z.string().optional(),
  780. authToken: z.string().optional(),
  781. apiBase: z.string().optional()
  782. }
  783. },
  784. wrap(qiweiSyncPersonalLabels)
  785. );
  786. server.registerTool(
  787. 'qiwei_create_personal_label',
  788. {
  789. title: '创建企微个人标签',
  790. description: '调用 /label/editLabel opType=1 创建个人标签。',
  791. inputSchema: {
  792. guid: z.string().optional(),
  793. labelName: z.string(),
  794. labelSuperId: z.string().optional(),
  795. uid: z.string().optional(),
  796. authToken: z.string().optional(),
  797. apiBase: z.string().optional()
  798. }
  799. },
  800. wrap(qiweiCreatePersonalLabel)
  801. );
  802. server.registerTool(
  803. 'qiwei_update_personal_label',
  804. {
  805. title: '更新企微个人标签',
  806. description: '调用 /label/editLabel opType=3 更新个人标签。',
  807. inputSchema: {
  808. guid: z.string().optional(),
  809. labelId: z.string(),
  810. labelName: z.string(),
  811. labelSuperId: z.string().optional(),
  812. uid: z.string().optional(),
  813. authToken: z.string().optional(),
  814. apiBase: z.string().optional()
  815. }
  816. },
  817. wrap(qiweiUpdatePersonalLabel)
  818. );
  819. server.registerTool(
  820. 'qiwei_delete_personal_label',
  821. {
  822. title: '删除企微个人标签',
  823. description: '调用 /label/editLabel opType=2 删除个人标签。',
  824. inputSchema: {
  825. guid: z.string().optional(),
  826. labelId: z.string(),
  827. uid: z.string().optional(),
  828. authToken: z.string().optional(),
  829. apiBase: z.string().optional()
  830. }
  831. },
  832. wrap(qiweiDeletePersonalLabel)
  833. );
  834. server.registerTool(
  835. 'qiwei_apply_personal_labels',
  836. {
  837. title: '应用企微个人标签到客户',
  838. description: '调用 /label/contactEditLabel 为客户添加/移除个人标签。',
  839. inputSchema: {
  840. guid: z.string().optional(),
  841. externalUserId: z.string(),
  842. addLabels: z.array(z.string()).optional(),
  843. removeLabels: z.array(z.string()).optional(),
  844. uid: z.string().optional(),
  845. authToken: z.string().optional(),
  846. apiBase: z.string().optional()
  847. }
  848. },
  849. wrap(qiweiApplyPersonalLabels)
  850. );
  851. server.registerTool(
  852. 'qiwei_prepare_broker_playbook',
  853. {
  854. title: '准备顾问 playbook 分析上下文',
  855. description: '从 outputs/messages/ 中收集指定顾问发送的消息,生成 playbook 分析上下文并写入 outputs/broker-playbooks/。',
  856. inputSchema: {
  857. brokerUserId: z.string(),
  858. roomIds: z.array(z.string()).optional()
  859. }
  860. },
  861. wrap(qiweiPrepareBrokerPlaybook)
  862. );
  863. server.registerTool(
  864. 'qiwei_distill_broker',
  865. {
  866. title: '蒸馏顾问 playbook',
  867. description: '默认 Agent 驱动:准备上下文并返回给调用方分析。传 aiMode=keyword 时直接基于关键词生成简版 playbook 并保存。',
  868. inputSchema: {
  869. brokerUserId: z.string(),
  870. aiMode: z.enum(['keyword']).optional(),
  871. roomIds: z.array(z.string()).optional()
  872. }
  873. },
  874. wrap(qiweiDistillBroker)
  875. );
  876. server.registerTool(
  877. 'qiwei_save_broker_playbook',
  878. {
  879. title: '保存顾问 playbook',
  880. description: '将 Agent 分析后的 playbook JSON 保存到 outputs/broker-playbooks/<brokerUserId>.json。',
  881. inputSchema: {
  882. brokerUserId: z.string(),
  883. playbook: z.record(z.any()),
  884. source: z.string().optional(),
  885. messageCount: z.number().int().optional()
  886. }
  887. },
  888. wrap(qiweiSaveBrokerPlaybook)
  889. );
  890. server.registerTool(
  891. 'qiwei_batch_distill_broker',
  892. {
  893. title: '批量蒸馏顾问 playbook',
  894. description: '对多个 brokerUserId 执行 qiwei_distill_broker。',
  895. inputSchema: {
  896. brokerUserIds: z.array(z.string()),
  897. aiMode: z.enum(['keyword']).optional(),
  898. roomIds: z.array(z.string()).optional()
  899. }
  900. },
  901. wrap(qiweiBatchDistillBroker)
  902. );
  903. server.registerTool(
  904. 'qiwei_batch_save_broker_playbook',
  905. {
  906. title: '批量保存顾问 playbook',
  907. description: '批量保存多个顾问的 playbook。',
  908. inputSchema: {
  909. items: z.array(z.object({ brokerUserId: z.string(), playbook: z.record(z.any()), source: z.string().optional() }))
  910. }
  911. },
  912. wrap(qiweiBatchSaveBrokerPlaybook)
  913. );
  914. server.registerTool(
  915. 'qiwei_export_broker_playbooks',
  916. {
  917. title: '导出顾问 playbook',
  918. description: '将 outputs/broker-playbooks/ 中保存的 playbook 导出为 Excel。未传 brokerUserIds 时导出全部。',
  919. inputSchema: {
  920. brokerUserIds: z.array(z.string()).optional()
  921. }
  922. },
  923. wrap(qiweiExportBrokerPlaybooks)
  924. );
  925. server.registerTool(
  926. 'qiwei_get_broker_playbook',
  927. {
  928. title: '读取顾问 playbook',
  929. description: '读取 outputs/broker-playbooks/<brokerUserId>.json 返回 playbook。',
  930. inputSchema: {
  931. brokerUserId: z.string()
  932. }
  933. },
  934. wrap(qiweiGetBrokerPlaybook)
  935. );
  936. server.registerTool(
  937. 'qiwei_preview_transfer_package',
  938. {
  939. title: '预览客户交接包',
  940. description: '根据 fromUserId/toUserId 和 roomIds/externalUserIds 生成交接包预览,写入 outputs/transfers/。',
  941. inputSchema: {
  942. fromUserId: z.string(),
  943. toUserId: z.string(),
  944. roomIds: z.array(z.string()).optional(),
  945. externalUserIds: z.array(z.string()).optional()
  946. }
  947. },
  948. wrap(qiweiPreviewTransferPackage)
  949. );
  950. server.registerTool(
  951. 'qiwei_execute_transfer',
  952. {
  953. title: '执行客户交接',
  954. description: '读取交接包预览,对每个 room 邀请新顾问入群,可选移除旧顾问并发送欢迎语。结果写入 outputs/transfers/。',
  955. inputSchema: {
  956. guid: z.string().optional(),
  957. previewFile: z.string().optional(),
  958. removeOldBroker: z.boolean().optional(),
  959. welcomeText: z.string().optional(),
  960. uid: z.string().optional(),
  961. authToken: z.string().optional(),
  962. apiBase: z.string().optional()
  963. }
  964. },
  965. wrap(qiweiExecuteTransfer)
  966. );
  967. server.registerTool(
  968. 'qiwei_transcribe_voice',
  969. {
  970. title: '语音转写',
  971. description: '接收语音 URL、本地文件路径或 base64,保存到 outputs/voice/,可选解码 silk 并调用转写服务。',
  972. inputSchema: {
  973. voiceUrl: z.string().optional(),
  974. filePath: z.string().optional(),
  975. base64Audio: z.string().optional(),
  976. transcribe: z.boolean().optional(),
  977. uid: z.string().optional(),
  978. authToken: z.string().optional(),
  979. apiBase: z.string().optional()
  980. }
  981. },
  982. wrap(qiweiTranscribeVoice)
  983. );
  984. server.registerTool(
  985. 'qiwei_webhook_status',
  986. {
  987. title: '查询 webhook 状态',
  988. description: '查询本地 webhook server 运行状态和已保存的回调配置。',
  989. inputSchema: {}
  990. },
  991. wrap(qiweiWebhookStatus)
  992. );
  993. server.registerTool(
  994. 'qiwei_webhook_discover',
  995. {
  996. title: '获取本地 webhook 回调地址',
  997. description: '返回本地 webhook server 的回调 URL。',
  998. inputSchema: {}
  999. },
  1000. wrap(qiweiWebhookDiscover)
  1001. );
  1002. server.registerTool(
  1003. 'qiwei_webhook_server_start',
  1004. {
  1005. title: '启动本地 webhook server',
  1006. description: '在 127.0.0.1 指定端口启动本地 HTTP server 接收企微回调,事件写入 outputs/webhook/。',
  1007. inputSchema: {
  1008. port: z.number().int().min(0).max(65535).optional()
  1009. }
  1010. },
  1011. wrap(qiweiWebhookServerStart)
  1012. );
  1013. server.registerTool(
  1014. 'qiwei_webhook_server_stop',
  1015. {
  1016. title: '停止本地 webhook server',
  1017. description: '停止正在运行的本地 webhook server。',
  1018. inputSchema: {}
  1019. },
  1020. wrap(qiweiWebhookServerStop)
  1021. );
  1022. server.registerTool(
  1023. 'qiwei_webhook_auto_setup',
  1024. {
  1025. title: '自动配置企微回调',
  1026. description: '启动本地 webhook server(如未运行)并调用 /client/setCallback 将回调地址配置到 Fmode 网关。',
  1027. inputSchema: {
  1028. guid: z.string().optional(),
  1029. port: z.number().int().min(0).max(65535).optional(),
  1030. callbackUrl: z.string().optional(),
  1031. secret: z.string().optional(),
  1032. uid: z.string().optional(),
  1033. authToken: z.string().optional(),
  1034. apiBase: z.string().optional()
  1035. }
  1036. },
  1037. wrap(qiweiWebhookAutoSetup)
  1038. );
  1039. server.registerTool(
  1040. 'qiwei_webhook_setup',
  1041. {
  1042. title: '配置企微回调',
  1043. description: '调用 /client/setCallback 配置指定的回调地址。',
  1044. inputSchema: {
  1045. guid: z.string().optional(),
  1046. callbackUrl: z.string(),
  1047. secret: z.string().optional(),
  1048. uid: z.string().optional(),
  1049. authToken: z.string().optional(),
  1050. apiBase: z.string().optional()
  1051. }
  1052. },
  1053. wrap(qiweiWebhookSetup)
  1054. );
  1055. server.registerTool(
  1056. 'qiwei_relay_config',
  1057. {
  1058. title: '读取 relay 配置',
  1059. description: '读取 outputs/webhook/relay-config.json 中的中央 relay 配置。',
  1060. inputSchema: {}
  1061. },
  1062. wrap(qiweiRelayConfig)
  1063. );
  1064. server.registerTool(
  1065. 'qiwei_relay_save_config',
  1066. {
  1067. title: '保存 relay 配置',
  1068. description: '保存中央 relay 配置到 outputs/webhook/relay-config.json。',
  1069. inputSchema: {
  1070. relayBaseUrl: z.string(),
  1071. tenantId: z.string().optional(),
  1072. publicKey: z.string().optional()
  1073. }
  1074. },
  1075. wrap(qiweiRelaySaveConfig)
  1076. );
  1077. server.registerTool(
  1078. 'qiwei_relay_register',
  1079. {
  1080. title: '注册 Relay 租户',
  1081. description: '使用 Fmode token 在中央 Relay 服务端注册租户,自动保存 tenantId/apiKey/apiSecret/privateKey 到 .env.local 和 outputs/webhook/relay-config.json。',
  1082. inputSchema: {
  1083. relayBaseUrl: z.string().optional(),
  1084. description: z.string().optional(),
  1085. guid: z.string().optional(),
  1086. force: z.boolean().optional()
  1087. }
  1088. },
  1089. wrap(qiweiRelayRegister)
  1090. );
  1091. server.registerTool(
  1092. 'qiwei_relay_connect',
  1093. {
  1094. title: '连接中央 relay',
  1095. description: '检查 relay 配置,注册设备到 Relay,调用 /client/setCallback 设置回调地址,并提示启动独立轮询客户端。',
  1096. inputSchema: {
  1097. guid: z.string().optional(),
  1098. force: z.boolean().optional()
  1099. }
  1100. },
  1101. wrap(qiweiRelayConnect)
  1102. );
  1103. server.registerTool(
  1104. 'qiwei_agent_dashboard_start',
  1105. {
  1106. title: '启动企微智能工作台',
  1107. description: '在当前技能包项目中启动企微 Dashboard(默认 4320 端口),返回智能会话页面地址。主控 Claude Code 会话首次使用时调用。',
  1108. inputSchema: {}
  1109. },
  1110. wrap(qiweiAgentDashboardStart)
  1111. );
  1112. server.registerTool(
  1113. 'qiwei_goal_create_plan',
  1114. {
  1115. title: '建立目标推进计划',
  1116. description: '把已确认的大目标、里程碑和任务保存为本地推进台账;不会自动创建企微待办。',
  1117. inputSchema: {
  1118. title: z.string(),
  1119. objective: z.string(),
  1120. owner: z.string().optional(),
  1121. deadline: z.string().optional(),
  1122. acceptance: z.string().optional(),
  1123. status: z.enum(['planned', 'in_progress', 'blocked', 'done', 'cancelled']).optional(),
  1124. milestones: z.array(goalMilestoneSchema).optional()
  1125. }
  1126. },
  1127. wrap(qiweiGoalCreatePlan)
  1128. );
  1129. server.registerTool(
  1130. 'qiwei_goal_get',
  1131. {
  1132. title: '查看目标与推进状态',
  1133. description: '列出目标计划,或读取单个计划的里程碑、任务、完成率、阻塞和逾期情况。',
  1134. inputSchema: { goalId: z.string().optional() }
  1135. },
  1136. wrap(qiweiGoalGet)
  1137. );
  1138. server.registerTool(
  1139. 'qiwei_goal_update_task',
  1140. {
  1141. title: '更新目标任务进度',
  1142. description: '更新目标任务的状态、负责人、截止时间、完成说明或阻塞原因。',
  1143. inputSchema: {
  1144. goalId: z.string(),
  1145. taskId: z.string(),
  1146. status: z.enum(['planned', 'in_progress', 'blocked', 'done', 'cancelled']).optional(),
  1147. owner: z.string().optional(),
  1148. deadline: z.string().optional(),
  1149. note: z.string().optional(),
  1150. blockedReason: z.string().optional()
  1151. }
  1152. },
  1153. wrap(qiweiGoalUpdateTask)
  1154. );
  1155. server.registerTool(
  1156. 'qiwei_goal_import_meeting_actions',
  1157. {
  1158. title: '把会议行动项写入目标计划',
  1159. description: '把已经过用户确认的会议行动项写入目标推进台账,并标记缺失的负责人或截止时间。',
  1160. inputSchema: {
  1161. goalId: z.string(),
  1162. meetingTitle: z.string(),
  1163. meetingDate: z.string().optional(),
  1164. sourceUrl: z.string().optional(),
  1165. actions: z.array(goalTaskSchema).min(1)
  1166. }
  1167. },
  1168. wrap(qiweiGoalImportMeetingActions)
  1169. );
  1170. server.registerTool(
  1171. 'qiwei_agent_bind_controller',
  1172. {
  1173. title: '绑定企微项目主控会话',
  1174. description: '把当前或指定 Claude Code Session 绑定为项目主控,并让每个客户专属 Session 记录该主控关联。通常由 qiwei_agent_dashboard_start 自动尝试。',
  1175. inputSchema: { sessionId: z.string().uuid().optional() }
  1176. },
  1177. wrap(qiweiAgentBindController)
  1178. );
  1179. server.registerTool(
  1180. 'qiwei_agent_status',
  1181. {
  1182. title: '查看企微 Agent 状态',
  1183. description: '读取账号在线状态、监听状态、全局策略、Claude Code/Fmode 模型和客户会话数量。',
  1184. inputSchema: {}
  1185. },
  1186. wrap(qiweiAgentStatus)
  1187. );
  1188. server.registerTool(
  1189. 'qiwei_agent_list_conversations',
  1190. {
  1191. title: '列出企微 Agent 客户会话',
  1192. description: '列出白名单客户会话、最新消息、会话模式和待审核草稿。每个客户由独立 Claude Code Session 处理。',
  1193. inputSchema: {}
  1194. },
  1195. wrap(qiweiAgentListConversations)
  1196. );
  1197. server.registerTool(
  1198. 'qiwei_agent_session_guide',
  1199. {
  1200. title: '查看客户 Claude Code 会话入口',
  1201. description: '当用户问某个企微客户由哪个 Claude Code Session 处理、会话在哪里或怎么打开时调用。返回客户可识别会话名和 Fmode Studio 安全打开命令,不暴露原始 Session ID。',
  1202. inputSchema: {
  1203. conversationId: z.string().optional(),
  1204. customerName: z.string().optional()
  1205. }
  1206. },
  1207. wrap(qiweiAgentSessionGuide)
  1208. );
  1209. server.registerTool(
  1210. 'qiwei_agent_customer_intelligence',
  1211. {
  1212. title: '查看客户画像、待办与预警',
  1213. description: '读取监听和 Agent 持续沉淀的客户画像、内部跟进待办和风险/高意向预警。用户问客户需求进展、下一步该做什么或有哪些风险时调用。',
  1214. inputSchema: {
  1215. conversationId: z.string().optional(),
  1216. customerName: z.string().optional()
  1217. }
  1218. },
  1219. wrap(qiweiAgentCustomerIntelligence)
  1220. );
  1221. server.registerTool(
  1222. 'qiwei_agent_inbox',
  1223. {
  1224. title: '读取项目主控事件箱',
  1225. description: '读取企微监听、Agent 决策、人工审核和发送动作的审计事件,供项目主控 Claude Code 会话掌握前端与客户会话状态。',
  1226. inputSchema: { limit: z.number().int().min(1).max(200).optional() }
  1227. },
  1228. wrap(qiweiAgentInbox)
  1229. );
  1230. server.registerTool(
  1231. 'qiwei_agent_listener',
  1232. {
  1233. title: '启动或停止企微监听',
  1234. description: '控制真实企微消息监听。running=true 启动,false 停止。启动监听不等于自动发送,发送仍受全局策略、会话模式和白名单约束。',
  1235. inputSchema: { running: z.boolean() }
  1236. },
  1237. wrap(qiweiAgentListener)
  1238. );
  1239. server.registerTool(
  1240. 'qiwei_agent_set_global',
  1241. {
  1242. title: '设置企微 Agent 全局策略',
  1243. description: '设置 paused(全局暂停)、review(生成待审核草稿)、auto(仅高置信且无需人工时自动发送)或 human(人工模式)。',
  1244. inputSchema: { mode: z.enum(['paused', 'review', 'auto', 'human']) }
  1245. },
  1246. wrap(qiweiAgentSetGlobal)
  1247. );
  1248. server.registerTool(
  1249. 'qiwei_agent_generate_draft',
  1250. {
  1251. title: '让 Claude Code 生成企微回复草稿',
  1252. description: '对指定客户会话的最新入站消息运行专用 Claude Code/Fmode Session,只生成待审核草稿,不发送给客户。',
  1253. inputSchema: { conversationId: z.string() }
  1254. },
  1255. wrap(qiweiAgentGenerateDraft)
  1256. );
  1257. return server;
  1258. }
  1259. async function main() {
  1260. const server = createServer();
  1261. const transport = new StdioServerTransport();
  1262. await server.connect(transport);
  1263. }
  1264. main().catch(error => {
  1265. console.error('enterprise-wechat-assistant mcp server failed:', error);
  1266. process.exit(1);
  1267. });