server.js 54 KB

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