#!/usr/bin/env node const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js'); const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js'); const { z } = require('zod'); const { redactSecret } = require('./providers/fmode-wecom-gateway'); const { ensureRelayDaemon } = require('./core/relay-daemon'); const { qiweiProductModeStatus, qiweiProductModeSet } = require('./tools/qiwei-product-mode-run'); const { searchQiweiApis, getQiweiApiDoc, callQiweiApi } = require('./tools/qiwei-api-catalog-run'); const { qiweiLoginStatus, qiweiLoginStart, qiweiLoginCheck, qiweiLoginVerify } = require('./tools/qiwei-login-run'); const { qiweiSubscriptionStatus, qiweiSubscribe, qiweiSubscriptionAutoRenew } = require('./tools/qiwei-subscription-run'); const { qiweiOfficialStatus, qiweiOfficialPrepare, qiweiOfficialHelp, qiweiOfficialCall } = require('./tools/wecom-official-cli-run'); const { qiweiBatchAddFriends, qiweiAutoCreateGroup, qiweiCheckFriendStatus, qiweiGetCustomerProfile } = require('./tools/qiwei-customer-ops-run'); const { qiweiSyncExternalGroups, qiweiListExternalGroups, qiweiAnalyzeGroupMembers, qiweiConfirmExternalGroup, qiweiAddExternalGroup, qiweiConfigureGroupKeywords, qiweiRejectExternalGroup } = require('./tools/qiwei-group-management-run'); const { qiweiGroupOpsGetContext, qiweiGroupOpsGeneratePlan, qiweiGroupOpsBatchGeneratePlans, qiweiGroupOpsGenerateCopy, qiweiGroupOpsReviewMessages, qiweiGroupOpsDailyBrief, qiweiGroupOpsManagePlaybook, qiweiGroupOpsExecuteItem, qiweiGroupOpsUpdateFinding, qiweiGroupOpsInsights, qiweiGroupOpsManageTask, qiweiGroupOpsAutomation, qiweiGroupOpsManageAccess } = require('./tools/qiwei-group-operations-run'); const { qiweiBusinessDiagnosis, qiweiBusinessActionCandidate, qiweiBusinessActionFeedback } = require('./tools/qiwei-business-diagnosis-run'); const { qiweiPrepareCustomerPortrait, qiweiUpdateCustomerPortrait, qiweiSaveCustomerPortrait, qiweiBatchUpdateCustomerPortrait, qiweiBatchSaveCustomerPortrait, qiweiExportCustomerPortraits, qiweiAddCustomerTags, qiweiRemoveCustomerTags, qiweiListCustomerTags, qiweiListAllTags, qiweiSyncPersonalLabels, qiweiCreatePersonalLabel, qiweiUpdatePersonalLabel, qiweiDeletePersonalLabel, qiweiApplyPersonalLabels } = require('./tools/qiwei-portrait-tags-run'); const { qiweiDistillBroker, qiweiPrepareBrokerPlaybook, qiweiSaveBrokerPlaybook, qiweiBatchDistillBroker, qiweiBatchSaveBrokerPlaybook, qiweiExportBrokerPlaybooks, qiweiGetBrokerPlaybook } = require('./tools/qiwei-broker-playbook-run'); const { qiweiPreviewTransferPackage, qiweiExecuteTransfer } = require('./tools/qiwei-customer-transfer-run'); const { qiweiGoalCreatePlan, qiweiGoalGet, qiweiGoalUpdateTask, qiweiGoalImportMeetingActions } = require('./tools/qiwei-goal-management-run'); const { qiweiTranscribeVoice, qiweiVoiceProfileStatus, qiweiEnrollVoice, qiweiSendClonedVoice } = require('./tools/qiwei-voice-run'); const { qiweiWebhookStatus, qiweiWebhookDiscover, qiweiWebhookServerStart, qiweiWebhookServerStop, qiweiWebhookAutoSetup, qiweiWebhookSetup, qiweiRelayConfig, qiweiRelaySaveConfig, qiweiRelayRegister, qiweiRelayConnect, qiweiRelayStart } = require('./tools/qiwei-webhook-relay-run'); const { qiweiAgentDashboardStart, qiweiAgentBindController, qiweiAgentStatus, qiweiAgentListConversations, qiweiAgentSessionGuide, qiweiAgentCustomerIntelligence, qiweiAgentInbox, qiweiAgentListener, qiweiAgentSetGlobal, qiweiAgentGenerateDraft } = require('./tools/qiwei-agent-control-run'); const goalTaskSchema = z.object({ title: z.string(), owner: z.string().optional(), deadline: z.string().optional(), priority: z.string().optional(), acceptance: z.string().optional(), status: z.enum(['planned', 'in_progress', 'blocked', 'done', 'cancelled']).optional(), note: z.string().optional(), blockedReason: z.string().optional() }); const goalMilestoneSchema = z.object({ title: z.string(), owner: z.string().optional(), deadline: z.string().optional(), acceptance: z.string().optional(), status: z.enum(['planned', 'in_progress', 'blocked', 'done', 'cancelled']).optional(), tasks: z.array(goalTaskSchema).optional() }); function asToolResult(result) { const normalized = result && typeof result === 'object' ? result : { status: 'ok', assistantMessage: String(result) }; return { content: [ { type: 'text', text: normalized.assistantMessage || JSON.stringify(normalized, null, 2) } ], structuredContent: normalized, isError: normalized.status === 'error' }; } function wrap(handler) { return async (input = {}) => { try { return asToolResult(await handler(input)); } catch (error) { const safeMessage = redactSecret(error && error.message ? error.message : error); return asToolResult({ status: 'error', assistantMessage: `工具执行异常:${safeMessage}`, errors: [{ message: safeMessage }] }); } }; } function createServer() { const server = new McpServer({ name: 'enterprise-wechat-assistant', version: '0.5.2' }); server.registerTool( 'qiwei_product_mode_status', { title: '查看企微个人版或企业版模式', description: '查看当前项目是个人版还是企业版、消息接收和存储方式、企业 Relay 是否就绪,以及企业版升级服务的当前报价占位。', inputSchema: {} }, wrap(qiweiProductModeStatus) ); server.registerTool( 'qiwei_product_mode_set', { title: '切换企微个人版或企业版模式', description: '切换项目运行模式。personal 使用本地主动监听和单设备本地数据;enterprise 使用服务端统一回调、多设备归集和企业隔离。切换后按返回步骤重启相关进程。', inputSchema: { mode: z.enum(['personal', 'enterprise']) } }, wrap(qiweiProductModeSet) ); server.registerTool( 'qiwei_api_search', { title: '检索企业微信开放接口清单', description: [ '检索企业微信能力的全量接口清单(100+ 个接口:登录/设备、消息收发、群管理、联系人、朋友圈、标签、会话、文件等模块)。', '当需要对企业微信做任何操作(发消息、拉群、加好友、同步消息、发朋友圈等)时,先来这里查有哪些接口。', '入参 query(关键词,中英文均可)、module(模块 key:login/client/msg/room/contact/sns/label/session/user/cloud)、tag、limit 均可选;都不传返回全量清单。' ].join(' '), inputSchema: { query: z.string().optional(), module: z.string().optional(), tag: z.string().optional(), limit: z.number().int().min(1).max(200).optional() } }, wrap(searchQiweiApis) ); server.registerTool( 'qiwei_api_doc', { title: '读取企业微信接口的详细参数文档', description: '读取清单中某个企业微信接口的完整参数文档(参数表、必填项、返回字段说明、调用模板)。入参 id(如 msg.sendText,来自 qiwei_api_search)或 method 路径(如 /msg/sendText)。', inputSchema: { id: z.string().optional(), method: z.string().optional() } }, wrap(getQiweiApiDoc) ); server.registerTool( 'qiwei_api_call', { title: '调用企业微信开放接口', description: [ '通过 Fmode 网关转发的企业微信接口执行请求,接口访问凭据和设备上下文由 Fmode 网关管理。', '推荐传 id(清单接口 id)+ params;未登记接口可传 rawMethod(如 /msg/sendText)+ params。', '客户端只需 Fmode 鉴权 token 和 uid,不接触企业微信接口访问凭据。' ].join(' '), inputSchema: { id: z.string().optional(), rawMethod: z.string().optional(), params: z.record(z.any()).optional(), filePath: z.string().optional(), uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(callQiweiApi) ); server.registerTool( 'qiwei_login_status', { title: '查询企业微信登录/配置状态', description: '通过 Fmode 网关转发的企业微信接口查询当前 uid 对应的设备在线状态。技能包安装后、或任何接口报设备/登录错误时先调用。', inputSchema: { uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiLoginStatus) ); server.registerTool( 'qiwei_login_start', { title: '开始企业微信扫码登录', description: [ '登录两步流程入口:默认在本地端口(默认 4310)启动流程页服务并打开浏览器,页面自动推进:未开通订阅时先展示服务端当前套餐价格(飞马余额扣费),开通后自动生成二维码并每 3 秒轮询扫码状态,需要验证码时页面提示输入。', '传 flowUi=false 仅用于已开通订阅后的扫码回退模式;未开通时返回 needs_subscription,不生成包含请求凭据的静态付费页。', '需要有效企微包月订阅和可用席位;无需提供企业微信接口访问凭据、代理或设备参数。' ].join(' '), inputSchema: { uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional(), openBrowser: z.boolean().optional(), flowUi: z.boolean().optional(), flowPort: z.number().int().optional() } }, wrap(qiweiLoginStart) ); server.registerTool( 'qiwei_login_check', { title: '检测扫码登录状态', description: '通过 Fmode 网关转发的企业微信接口轮询扫码登录状态(用户扫码后每 3-5 秒调一次)。状态 10 表示需要 6 位验证码;manual=true 时先尝试免扫码登录。', inputSchema: { manual: z.boolean().optional(), uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiLoginCheck) ); server.registerTool( 'qiwei_login_verify', { title: '提交扫码登录的 6 位验证码', description: '当 qiwei_login_check 返回状态 10(需要验证码)时,向用户索要手机端显示的 6 位验证码并通过本工具提交,随后再调 qiwei_login_check 确认登录成功。', inputSchema: { code: z.string(), uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiLoginVerify) ); server.registerTool( 'qiwei_subscription_status', { title: '查询企微包月订阅与席位状态', description: '查询企微包月订阅、到期时间、自动续费、已购/已用席位和飞马余额。首次登录或收到 402/403 时先调用。', inputSchema: { authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiSubscriptionStatus) ); server.registerTool( 'qiwei_subscribe', { title: '开通、续费或增购企微席位', description: '使用飞马余额一次开通或续费 1-12 个月企微订阅。1 个席位对应 1 个企业微信账号;seats 和 months 不传时均默认 1。', inputSchema: { seats: z.number().int().min(1).optional(), months: z.number().int().min(1).max(12).optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiSubscribe) ); server.registerTool( 'qiwei_subscription_auto_renew', { title: '设置企微订阅自动续费', description: '开启或关闭企微包月订阅到期自动续费。', inputSchema: { autoRenew: z.boolean(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiSubscriptionAutoRenew) ); server.registerTool( 'qiwei_official_status', { title: '检查企业微信官方 CLI 状态', description: '检查固定版本官方 CLI 是否已下载、版本是否正确,以及官方机器人扫码授权是否完成。此工具与 Fmode 网关登录状态完全独立。', inputSchema: {} }, wrap(qiweiOfficialStatus) ); server.registerTool( 'qiwei_official_prepare', { title: '下载企业微信官方 CLI', description: '将固定版本 @wecom/cli 下载到用户级缓存并校验。不会全局安装,不修改官方 CLI,也不会改动 Fmode 网关转发的企业微信接口通道。', inputSchema: { force: z.boolean().optional() } }, wrap(qiweiOfficialPrepare) ); server.registerTool( 'qiwei_official_help', { title: '读取企业微信官方 CLI 帮助', description: '读取官方 CLI 总帮助、category 工具列表或具体 method 的参数 schema。category/method 级帮助需要先完成官方 CLI 初始化。', inputSchema: { category: z.enum(['contact', 'doc', 'meeting', 'msg', 'schedule', 'todo']).optional(), method: z.string().regex(/^[a-z][a-z0-9_]*$/).optional() } }, wrap(qiweiOfficialHelp) ); server.registerTool( 'qiwei_official_call', { title: '调用企业微信官方 CLI 能力', description: '通过固定版本官方 CLI 调用通讯录、文档、会议、消息、日程或待办。参数以结构化 JSON 传递,不经过 shell,也不经过 Fmode 网关。', inputSchema: { category: z.enum(['contact', 'doc', 'meeting', 'msg', 'schedule', 'todo']), method: z.string().regex(/^[a-z][a-z0-9_]*$/), args: z.record(z.any()).optional(), timeoutMs: z.number().int().min(1000).max(300000).optional() } }, wrap(qiweiOfficialCall) ); server.registerTool( 'qiwei_batch_add_friends', { title: '批量搜索并添加企微好友', description: [ '迁移自 Qiwei 项目的批量加好友能力。按手机号搜索联系人,依据 qiwei-endpoints.json 中的 /contact/searchContact、/contact/addSearchWxContact、/contact/addSearchWxWorkContact 调用 Fmode 网关。', '不修改 qiwei_login_* 和 qiwei_api_* 既有流程;需要已登录设备 guid。支持 customers、phones,或传入 .xlsx filePath 读取 Excel。' ].join(' '), inputSchema: { guid: z.string().optional(), customers: z.array(z.object({ phone: z.string(), name: z.string().optional(), greeting: z.string().optional(), verifyText: z.string().optional(), groupName: z.string().optional(), supportMemberIds: z.array(z.string()).optional() })).optional(), phones: z.array(z.string()).optional(), filePath: z.string().optional(), greeting: z.string().optional(), defaultGreeting: z.string().optional(), rateLimitPerMinute: z.number().int().min(1).max(60).optional(), maxAttempts: z.number().int().min(1).max(5).optional(), addToAllowlist: z.boolean().optional().describe('仅添加测试好友时设为 true;成功后自动加入个人消息白名单并启用待审核监听'), uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiBatchAddFriends) ); server.registerTool( 'qiwei_auto_create_group', { title: '自动创建企微服务群', description: [ '迁移自 Qiwei 项目的自动建群能力。基于 qiwei-endpoints.json 中的 /room/createRoom、/room/modifyRoomName、/room/inviteRoomMember、/msg/sendText 执行建群、改群名、邀请协作成员和发送欢迎语。', '不修改目标包已有登录、订阅、通用 API 调用流程;需要已登录设备 guid。' ].join(' '), inputSchema: { guid: z.string().optional(), customerUserId: z.string().optional(), externalUserId: z.string().optional(), memberList: z.array(z.string()).optional(), supportMemberIds: z.array(z.string()).optional(), groupName: z.string().optional(), welcomeText: z.string().optional(), isOuterRoom: z.number().int().optional(), uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiAutoCreateGroup) ); server.registerTool( 'qiwei_check_friend_status', { title: '检查好友申请状态', description: '按手机号批量检查联系人搜索状态,判断对方是否已是好友、已被其他人添加、未添加或未找到。需要已登录设备 guid。', inputSchema: { guid: z.string().optional(), customers: z.array(z.object({ phone: z.string(), name: z.string().optional() })).optional(), phones: z.array(z.string()).optional(), externalUserIds: z.array(z.string()).optional(), uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiCheckFriendStatus) ); server.registerTool( 'qiwei_get_customer_profile', { title: '查询客户档案', description: '按手机号或 externalUserId 查询客户档案:搜索联系人、拉取外部联系人列表、拉取群列表,并读取本地已保存的画像文件。需要已登录设备 guid。', inputSchema: { guid: z.string().optional(), phone: z.string().optional(), externalUserId: z.string().optional(), uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiGetCustomerProfile) ); server.registerTool( 'qiwei_sync_external_groups', { title: '同步外部群列表', description: '从当前账号创建的群(self)、最近会话中的群(session)或两者(all)扫描群目录。支持按可配置关键词自动分类客户群,结果写入 outputs/groups/。', inputSchema: { guid: z.string().optional(), scope: z.enum(['self', 'session', 'all']).optional(), includeInternalGroups: z.boolean().optional(), customerKeywords: z.array(z.string()).optional(), matchMode: z.enum(['any', 'threshold']).optional(), threshold: z.number().int().min(1).optional(), highConfidenceTerms: z.array(z.string()).optional(), autoClassify: z.boolean().optional(), maxPages: z.number().int().min(1).max(300).optional(), uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiSyncExternalGroups) ); server.registerTool( 'qiwei_list_external_groups', { title: '列出外部群', description: '读取最近一次同步的群列表和已确认/已拒绝映射,返回每个群的状态(IMPORTED / SUGGESTED / AUTO_CONFIRMED / CONFIRMED / REJECTED),支持按关键词、状态、来源过滤。', inputSchema: { keyword: z.string().optional(), status: z.enum(['IMPORTED', 'SUGGESTED', 'AUTO_CONFIRMED', 'CONFIRMED', 'REJECTED']).optional(), source: z.enum(['roomList', 'session', 'messages']).optional(), includeRejected: z.boolean().optional(), customerKeywords: z.array(z.string()).optional(), matchMode: z.enum(['any', 'threshold']).optional(), threshold: z.number().int().min(1).optional(), highConfidenceTerms: z.array(z.string()).optional() } }, wrap(qiweiListExternalGroups) ); server.registerTool( 'qiwei_analyze_group_members', { title: '分析群详情', description: '对指定的 roomId 列表调用 /room/batchGetRoomDetail,按群名关键词判断哪些可能是客户群。', inputSchema: { guid: z.string().optional(), roomIds: z.array(z.string()), customerKeywords: z.array(z.string()).optional(), matchMode: z.enum(['any', 'threshold']).optional(), threshold: z.number().int().min(1).optional(), highConfidenceTerms: z.array(z.string()).optional(), autoClassify: z.boolean().optional(), updateSnapshot: z.boolean().optional(), uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiAnalyzeGroupMembers) ); server.registerTool( 'qiwei_confirm_external_group', { title: '确认外部群为客户群', description: '对最近一次同步列表中的某个 roomId 标记为 CONFIRMED,并关联 customerId / externalUserId。映射写入 outputs/groups/confirmed-mapping.json。', inputSchema: { roomId: z.string(), customerId: z.string().optional(), externalUserId: z.string().optional(), customerName: z.string().optional() } }, wrap(qiweiConfirmExternalGroup) ); server.registerTool( 'qiwei_add_external_group', { title: '手动添加外部群', description: '直接按 roomId 添加外部群并标记为 CONFIRMED,关联 customerId / externalUserId。适用于同步列表之外的群。', inputSchema: { roomId: z.string(), roomName: z.string().optional(), customerId: z.string().optional(), externalUserId: z.string().optional(), customerName: z.string().optional() } }, wrap(qiweiAddExternalGroup) ); server.registerTool( 'qiwei_configure_group_keywords', { title: '配置客户群关键词', description: '持久化配置客户群识别关键词、匹配模式(any/threshold)、阈值和高置信度词,写入 outputs/groups/customer-keywords.json。传 reset=true 可恢复默认。', inputSchema: { keywords: z.array(z.string()).optional(), highConfidenceTerms: z.array(z.string()).optional(), matchMode: z.enum(['any', 'threshold']).optional(), threshold: z.number().int().min(1).optional(), reset: z.boolean().optional() } }, wrap(qiweiConfigureGroupKeywords) ); server.registerTool( 'qiwei_reject_external_group', { title: '拒绝外部群', description: '将最近一次同步列表中的某个 roomId 标记为 REJECTED,写入 outputs/groups/rejected-mapping.json。', inputSchema: { roomId: z.string(), reason: z.string().optional() } }, wrap(qiweiRejectExternalGroup) ); server.registerTool('qiwei_group_ops_get_context', { title: '读取社群运营上下文', description: '读取当前账号的群运营总览,或指定群的主档、已发布 SOP、计划和质检风险。用于制定计划、写话术和生成管理简报前获取事实。', inputSchema: { accountKey: z.string().optional(), guid: z.string().optional(), roomId: z.string().optional(), date: z.string().optional(), group: z.record(z.any()).optional() } }, wrap(qiweiGroupOpsGetContext)); server.registerTool('qiwei_group_ops_generate_plan', { title: '生成客户群运营计划', description: '依据已发布 SOP 或显式计划项,为指定客户群生成每日运营计划草稿。结果只进入待审核状态,不自动发送。', 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() } }, wrap(qiweiGroupOpsGeneratePlan)); server.registerTool('qiwei_group_ops_batch_generate_plans', { title: '批量生成客户群运营计划', description: '按门店或显式群列表批量生成指定日期的运营计划。每个群独立幂等,失败项不会影响成功项,全部结果进入待审核。', 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() } }, wrap(qiweiGroupOpsBatchGeneratePlans)); server.registerTool('qiwei_group_ops_generate_copy', { title: '生成并检查群运营话术', description: '替换话术变量并执行缺失变量、敏感承诺等基础规则检查。仅返回可审核草稿。', inputSchema: { content: z.string().optional(), template: z.string().optional(), variables: z.record(z.any()).optional(), riskTerms: z.array(z.string()).optional() } }, wrap(qiweiGroupOpsGenerateCopy)); server.registerTool('qiwei_group_ops_review_messages', { title: '质检客户群消息', description: '对指定客户群消息执行投诉风险和超时未回应检查,每个结论保留原消息证据。', 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() } }, wrap(qiweiGroupOpsReviewMessages)); server.registerTool('qiwei_group_ops_daily_brief', { title: '生成社群运营每日简报', description: '汇总今日应执行、已完成、待审核和高风险事项,并返回可下钻的计划与质检数据。', inputSchema: { accountKey: z.string().optional(), guid: z.string().optional(), date: z.string().optional() } }, wrap(qiweiGroupOpsDailyBrief)); server.registerTool('qiwei_group_ops_manage_playbook', { title: '管理群运营 SOP', description: '列出、创建、创建新版本、发布或回滚群运营 SOP。发布与回滚属于受控写操作,调用前必须向用户确认。', 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() } }, wrap(qiweiGroupOpsManagePlaybook)); server.registerTool('qiwei_group_ops_execute_item', { title: '审核或记录群运营计划项', description: '批准、拒绝、跳过或记录计划项已由人工发送。真实自动发送在接口回执验证前保持关闭。', 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() } }, wrap(qiweiGroupOpsExecuteItem)); server.registerTool('qiwei_group_ops_update_finding', { title: '处理群质检事项', description: '认领、确认、关闭或标记群质检误报,并保留处理审计。', 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() } }, wrap(qiweiGroupOpsUpdateFinding)); server.registerTool('qiwei_group_ops_insights', { title: '分析社群运营与配置质检规则', description: '查看跨门店执行对比、群活跃趋势、生命周期建议、优秀话术和质检反馈;也可更新当前账号的 SLA、风险词和禁止承诺。', 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() } }, wrap(qiweiGroupOpsInsights)); server.registerTool('qiwei_group_ops_manage_task', { title: '管理社群整改任务', description: '创建、查询、推进社群整改任务,并在负责人精确匹配且人工确认后同步为真实企微官方待办。', 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() } }, wrap(qiweiGroupOpsManageTask)); server.registerTool('qiwei_group_ops_automation', { title: '管理社群受控自动化', description: '创建和激活自动化策略,配置白名单、允许类型、时间窗、频控、灰度与熔断,执行 dry-run 或在全部安全条件满足时受控发送。默认暂停且真实发送环境开关关闭。', 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() } }, wrap(qiweiGroupOpsAutomation)); server.registerTool('qiwei_group_ops_manage_access', { title: '管理社群运营角色权限', description: '配置或查看当前企微账号下的总部管理员、区域/门店经理、执行员工和审计者权限。首次初始化必须显式确认。', 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() } }, wrap(qiweiGroupOpsManageAccess)); server.registerTool('qiwei_business_diagnosis', { title: '诊断企微经营问题', description: '只读汇总客户主档、真实会话和社群运营数据,针对经营问题返回结论、证据、数据缺口和可下钻动作。缺少订单或 GMV 数据时明确拒绝生成虚假交易结论。', inputSchema: { question: z.string().min(2), accountKey: z.string().optional(), guid: z.string().optional(), uid: z.string().optional(), date: z.string().optional() } }, wrap(qiweiBusinessDiagnosis)); server.registerTool('qiwei_business_action_candidate', { title: '将经营建议加入行动候选', description: '把经营诊断中的一条建议加入统一任务中心候选池。只创建待人工确认候选,不会生成正式任务、同步企微待办或发送消息。', inputSchema: { question: z.string().min(2), diagnosisType: z.string().optional(), scope: z.record(z.any()).optional(), action: z.object({ id: z.string().optional(), label: z.string().min(1), description: z.string().optional(), route: z.string().optional(), hash: z.string().optional(), priority: z.enum(['low', 'medium', 'high', 'urgent']).optional() }), evidence: z.array(z.union([z.string(), z.record(z.any())])).optional(), dataGaps: z.array(z.record(z.any())).optional() } }, wrap(qiweiBusinessActionCandidate)); server.registerTool('qiwei_business_action_feedback', { title: '记录经营行动效果反馈', description: '为已完成的经营诊断来源内部任务记录有效、部分有效、无效或暂无法判断。反馈只用于内部复盘,不会发送客户消息或同步企微待办。', inputSchema: { taskId: z.string().min(1), outcome: z.enum(['effective', 'partially_effective', 'ineffective', 'unknown']), note: z.string().max(1000).optional(), actor: z.string().max(100).optional() } }, wrap(qiweiBusinessActionFeedback)); server.registerTool( 'qiwei_prepare_customer_portrait', { title: '准备客户画像分析上下文', description: '从 outputs/messages/ 中收集指定 externalUserId 发送的消息,生成画像分析上下文并写入 outputs/portraits/,供 Agent 分析后保存。', inputSchema: { externalUserId: z.string(), roomIds: z.array(z.string()).optional() } }, wrap(qiweiPrepareCustomerPortrait) ); server.registerTool( 'qiwei_update_customer_portrait', { title: '更新客户画像', description: '更新客户画像。updateMode=incremental 仅分析新增消息并合并现有画像;updateMode=full 基于全部消息重新计算。传 aiMode=keyword 时使用关键词模式。', inputSchema: { externalUserId: z.string(), aiMode: z.enum(['keyword']).optional(), updateMode: z.enum(['incremental', 'full']).optional(), roomIds: z.array(z.string()).optional() } }, wrap(qiweiUpdateCustomerPortrait) ); server.registerTool( 'qiwei_save_customer_portrait', { title: '保存客户画像', description: '将 Agent 分析后的画像 JSON 保存到 outputs/portraits/.json。', inputSchema: { externalUserId: z.string(), portrait: z.record(z.any()), source: z.string().optional(), messageCount: z.number().int().optional() } }, wrap(qiweiSaveCustomerPortrait) ); server.registerTool( 'qiwei_batch_update_customer_portrait', { title: '批量更新客户画像', description: '对多个 externalUserId 执行增量或全量画像更新。', inputSchema: { externalUserIds: z.array(z.string()), aiMode: z.enum(['keyword']).optional(), updateMode: z.enum(['incremental', 'full']).optional(), roomIds: z.array(z.string()).optional() } }, wrap(qiweiBatchUpdateCustomerPortrait) ); server.registerTool( 'qiwei_batch_save_customer_portrait', { title: '批量保存客户画像', description: '批量保存多个客户的画像。', inputSchema: { items: z.array(z.object({ externalUserId: z.string(), portrait: z.record(z.any()), source: z.string().optional() })) } }, wrap(qiweiBatchSaveCustomerPortrait) ); server.registerTool( 'qiwei_export_customer_portraits', { title: '导出客户画像', description: '将 outputs/portraits/ 中保存的画像导出为 Excel。未传 externalUserIds 时导出全部。', inputSchema: { externalUserIds: z.array(z.string()).optional() } }, wrap(qiweiExportCustomerPortraits) ); server.registerTool( 'qiwei_add_customer_tags', { title: '添加客户标签', description: '为 externalUserId 添加本地标签,去重保存到 outputs/tags/.json。', inputSchema: { externalUserId: z.string(), tags: z.array(z.string()) } }, wrap(qiweiAddCustomerTags) ); server.registerTool( 'qiwei_remove_customer_tags', { title: '移除客户标签', description: '为 externalUserId 移除本地标签。', inputSchema: { externalUserId: z.string(), tags: z.array(z.string()) } }, wrap(qiweiRemoveCustomerTags) ); server.registerTool( 'qiwei_list_customer_tags', { title: '列出客户标签', description: '读取 outputs/tags/.json 返回标签列表。', inputSchema: { externalUserId: z.string() } }, wrap(qiweiListCustomerTags) ); server.registerTool( 'qiwei_list_all_tags', { title: '列出所有本地标签', description: '汇总所有客户的本地标签并去重返回。', inputSchema: {} }, wrap(qiweiListAllTags) ); server.registerTool( 'qiwei_sync_personal_labels', { title: '同步企微个人标签', description: '调用 /label/syncLabelList 同步企微个人标签列表。', inputSchema: { guid: z.string().optional(), uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiSyncPersonalLabels) ); server.registerTool( 'qiwei_create_personal_label', { title: '创建企微个人标签', description: '调用 /label/editLabel opType=1 创建个人标签。', inputSchema: { guid: z.string().optional(), labelName: z.string(), labelSuperId: z.string().optional(), uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiCreatePersonalLabel) ); server.registerTool( 'qiwei_update_personal_label', { title: '更新企微个人标签', description: '调用 /label/editLabel opType=3 更新个人标签。', inputSchema: { guid: z.string().optional(), labelId: z.string(), labelName: z.string(), labelSuperId: z.string().optional(), uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiUpdatePersonalLabel) ); server.registerTool( 'qiwei_delete_personal_label', { title: '删除企微个人标签', description: '调用 /label/editLabel opType=2 删除个人标签。', inputSchema: { guid: z.string().optional(), labelId: z.string(), uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiDeletePersonalLabel) ); server.registerTool( 'qiwei_apply_personal_labels', { title: '应用企微个人标签到客户', description: '调用 /label/contactEditLabel 为客户添加/移除个人标签。', inputSchema: { guid: z.string().optional(), externalUserId: z.string(), addLabels: z.array(z.string()).optional(), removeLabels: z.array(z.string()).optional(), uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiApplyPersonalLabels) ); server.registerTool( 'qiwei_prepare_broker_playbook', { title: '准备顾问 playbook 分析上下文', description: '从 outputs/messages/ 中收集指定顾问发送的消息,生成 playbook 分析上下文并写入 outputs/broker-playbooks/。', inputSchema: { brokerUserId: z.string(), roomIds: z.array(z.string()).optional() } }, wrap(qiweiPrepareBrokerPlaybook) ); server.registerTool( 'qiwei_distill_broker', { title: '蒸馏顾问 playbook', description: '默认 Agent 驱动:准备上下文并返回给调用方分析。传 aiMode=keyword 时直接基于关键词生成简版 playbook 并保存。', inputSchema: { brokerUserId: z.string(), aiMode: z.enum(['keyword']).optional(), roomIds: z.array(z.string()).optional() } }, wrap(qiweiDistillBroker) ); server.registerTool( 'qiwei_save_broker_playbook', { title: '保存顾问 playbook', description: '将 Agent 分析后的 playbook JSON 保存到 outputs/broker-playbooks/.json。', inputSchema: { brokerUserId: z.string(), playbook: z.record(z.any()), source: z.string().optional(), messageCount: z.number().int().optional() } }, wrap(qiweiSaveBrokerPlaybook) ); server.registerTool( 'qiwei_batch_distill_broker', { title: '批量蒸馏顾问 playbook', description: '对多个 brokerUserId 执行 qiwei_distill_broker。', inputSchema: { brokerUserIds: z.array(z.string()), aiMode: z.enum(['keyword']).optional(), roomIds: z.array(z.string()).optional() } }, wrap(qiweiBatchDistillBroker) ); server.registerTool( 'qiwei_batch_save_broker_playbook', { title: '批量保存顾问 playbook', description: '批量保存多个顾问的 playbook。', inputSchema: { items: z.array(z.object({ brokerUserId: z.string(), playbook: z.record(z.any()), source: z.string().optional() })) } }, wrap(qiweiBatchSaveBrokerPlaybook) ); server.registerTool( 'qiwei_export_broker_playbooks', { title: '导出顾问 playbook', description: '将 outputs/broker-playbooks/ 中保存的 playbook 导出为 Excel。未传 brokerUserIds 时导出全部。', inputSchema: { brokerUserIds: z.array(z.string()).optional() } }, wrap(qiweiExportBrokerPlaybooks) ); server.registerTool( 'qiwei_get_broker_playbook', { title: '读取顾问 playbook', description: '读取 outputs/broker-playbooks/.json 返回 playbook。', inputSchema: { brokerUserId: z.string() } }, wrap(qiweiGetBrokerPlaybook) ); server.registerTool( 'qiwei_preview_transfer_package', { title: '预览客户交接包', description: '根据 fromUserId/toUserId 和 roomIds/externalUserIds 生成交接包预览,写入 outputs/transfers/。', inputSchema: { fromUserId: z.string(), toUserId: z.string(), roomIds: z.array(z.string()).optional(), externalUserIds: z.array(z.string()).optional() } }, wrap(qiweiPreviewTransferPackage) ); server.registerTool( 'qiwei_execute_transfer', { title: '执行客户交接', description: '读取交接包预览,对每个 room 邀请新顾问入群,可选移除旧顾问并发送欢迎语。结果写入 outputs/transfers/。', inputSchema: { guid: z.string().optional(), previewFile: z.string().optional(), removeOldBroker: z.boolean().optional(), welcomeText: z.string().optional(), uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiExecuteTransfer) ); server.registerTool( 'qiwei_transcribe_voice', { title: '语音转写', description: '接收语音 URL、本地文件路径或 base64,保存到 outputs/voice/,可选解码 silk 并调用转写服务。', inputSchema: { voiceUrl: z.string().optional(), filePath: z.string().optional(), base64Audio: z.string().optional(), transcribe: z.boolean().optional(), uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiTranscribeVoice) ); server.registerTool( 'qiwei_voice_profile_status', { title: '查询声音克隆状态', description: '查询当前企微账号是否已配置云端 IndexTTS2,以及是否完成本人声音初始化。', inputSchema: { uid: z.string().optional(), guid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiVoiceProfileStatus) ); server.registerTool( 'qiwei_enroll_voice', { title: '初始化本人声音', description: '保存并校验 5~30 秒本人参考录音,转换为账号隔离的 16kHz 单声道声音档案。', inputSchema: { filePath: z.string().optional(), base64Audio: z.string().optional(), originalName: z.string().optional(), mime: z.string().optional(), uid: z.string().optional(), guid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiEnrollVoice) ); server.registerTool( 'qiwei_send_cloned_voice', { title: '发送克隆企微语音', description: '真实发送操作。合成本人音色、自动选择受控客服语气、编码 24kHz SILK 并发送给指定企微联系人。调用前必须获得人工确认。', inputSchema: { toId: z.string().min(1), text: z.string().min(1).max(600), context: z.string().optional(), tone: z.enum(['auto', 'natural', 'friendly', 'apology', 'empathetic', 'reminder']).optional(), confirmed: z.literal(true), uid: z.string().optional(), guid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() }, annotations: { destructiveHint: true, openWorldHint: true } }, wrap(qiweiSendClonedVoice) ); server.registerTool( 'qiwei_webhook_status', { title: '查询 webhook 状态', description: '查询当前个人版/企业版消息接收方式、本地 webhook、企业 Relay 和事件状态,不返回任何回调或租户密钥。', inputSchema: {} }, wrap(qiweiWebhookStatus) ); server.registerTool( 'qiwei_webhook_discover', { title: '获取个人版直连回调地址', description: '个人版隔离部署专用:返回本地 webhook server 的回调 URL。企业版使用服务端统一回调。', inputSchema: {} }, wrap(qiweiWebhookDiscover) ); server.registerTool( 'qiwei_webhook_server_start', { title: '启动个人版本地 webhook server', description: '个人版隔离部署专用:在本机启动 HTTP server,仍需独立公网入口才能接收企微回调。企业版不能调用。', inputSchema: { port: z.number().int().min(0).max(65535).optional() } }, wrap(qiweiWebhookServerStart) ); server.registerTool( 'qiwei_webhook_server_stop', { title: '停止本地 webhook server', description: '停止正在运行的本地 webhook server。', inputSchema: {} }, wrap(qiweiWebhookServerStop) ); server.registerTool( 'qiwei_webhook_auto_setup', { title: '自动配置企业版 Relay 回调', description: '企业版专用:注册设备到中央 Relay,并调用 Fmode 服务端专用接口连接 Token 级全局回调。个人版使用本地监听,无需调用。', inputSchema: { guid: z.string().optional(), port: z.number().int().min(0).max(65535).optional(), callbackUrl: z.string().optional(), secret: z.string().optional(), uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiWebhookAutoSetup) ); server.registerTool( 'qiwei_webhook_setup', { title: '配置隔离部署的个人回调', description: '仅限个人版独立服务 Token 和独立公网部署;需要显式开启直连回调,企业版不能调用。', inputSchema: { guid: z.string().optional(), callbackUrl: z.string(), secret: z.string().optional(), uid: z.string().optional(), authToken: z.string().optional(), apiBase: z.string().optional() } }, wrap(qiweiWebhookSetup) ); server.registerTool( 'qiwei_relay_config', { title: '读取 relay 配置', description: '读取 outputs/webhook/relay-config.json 中的中央 relay 配置。', inputSchema: {} }, wrap(qiweiRelayConfig) ); server.registerTool( 'qiwei_relay_save_config', { title: '保存 relay 配置', description: '保存中央 relay 配置到 outputs/webhook/relay-config.json。', inputSchema: { relayBaseUrl: z.string(), tenantId: z.string().optional(), publicKey: z.string().optional() } }, wrap(qiweiRelaySaveConfig) ); server.registerTool( 'qiwei_relay_register', { title: '注册 Relay 租户', description: '使用 Fmode token 在中央 Relay 服务端注册租户,自动保存 tenantId/apiKey/apiSecret/privateKey 到 .env.local 和 outputs/webhook/relay-config.json。', inputSchema: { relayBaseUrl: z.string().optional(), description: z.string().optional(), guid: z.string().optional(), force: z.boolean().optional() } }, wrap(qiweiRelayRegister) ); server.registerTool( 'qiwei_relay_connect', { title: '连接中央 relay', description: '企业版专用:检查 Relay 配置,注册设备并调用 Fmode `/relay/connect` 接入服务端统一回调。', inputSchema: { guid: z.string().optional(), force: z.boolean().optional() } }, wrap(qiweiRelayConnect) ); server.registerTool( 'qiwei_agent_dashboard_start', { title: '启动企微智能工作台', description: '启动并预览当前项目的企微 Dashboard(默认 4320 端口),同时返回 Fmode 鉴权、企微席位、账号在线、测试白名单和企业回调状态。Dashboard 仅用于管理,不承担企业版消息接收。', inputSchema: {} }, wrap(qiweiAgentDashboardStart) ); server.registerTool( 'qiwei_goal_create_plan', { title: '建立目标推进计划', description: '把已确认的大目标、里程碑和任务保存为本地推进台账;不会自动创建企微待办。', inputSchema: { title: z.string(), objective: z.string(), owner: z.string().optional(), deadline: z.string().optional(), acceptance: z.string().optional(), status: z.enum(['planned', 'in_progress', 'blocked', 'done', 'cancelled']).optional(), milestones: z.array(goalMilestoneSchema).optional() } }, wrap(qiweiGoalCreatePlan) ); server.registerTool( 'qiwei_goal_get', { title: '查看目标与推进状态', description: '列出目标计划,或读取单个计划的里程碑、任务、完成率、阻塞和逾期情况。', inputSchema: { goalId: z.string().optional() } }, wrap(qiweiGoalGet) ); server.registerTool( 'qiwei_goal_update_task', { title: '更新目标任务进度', description: '更新目标任务的状态、负责人、截止时间、完成说明或阻塞原因。', inputSchema: { goalId: z.string(), taskId: z.string(), status: z.enum(['planned', 'in_progress', 'blocked', 'done', 'cancelled']).optional(), owner: z.string().optional(), deadline: z.string().optional(), note: z.string().optional(), blockedReason: z.string().optional() } }, wrap(qiweiGoalUpdateTask) ); server.registerTool( 'qiwei_goal_import_meeting_actions', { title: '把会议行动项写入目标计划', description: '把已经过用户确认的会议行动项写入目标推进台账,并标记缺失的负责人或截止时间。', inputSchema: { goalId: z.string(), meetingTitle: z.string(), meetingDate: z.string().optional(), sourceUrl: z.string().optional(), actions: z.array(goalTaskSchema).min(1) } }, wrap(qiweiGoalImportMeetingActions) ); server.registerTool( 'qiwei_agent_bind_controller', { title: '绑定企微项目主控会话', description: '把当前或指定 Claude Code Session 绑定为项目主控,并让每个客户专属 Session 记录该主控关联。通常由 qiwei_agent_dashboard_start 自动尝试。', inputSchema: { sessionId: z.string().uuid().optional() } }, wrap(qiweiAgentBindController) ); server.registerTool( 'qiwei_agent_status', { title: '查看企微 Agent 状态', description: '读取账号在线状态、监听状态、全局策略、Claude Code/Fmode 模型和客户会话数量。', inputSchema: {} }, wrap(qiweiAgentStatus) ); server.registerTool( 'qiwei_agent_list_conversations', { title: '列出企微 Agent 客户会话', description: '列出白名单客户会话、最新消息、会话模式和待审核草稿。每个客户由独立 Claude Code Session 处理。', inputSchema: {} }, wrap(qiweiAgentListConversations) ); server.registerTool( 'qiwei_agent_session_guide', { title: '查看客户 Claude Code 会话入口', description: '当用户问某个企微客户由哪个 Claude Code Session 处理、会话在哪里或怎么打开时调用。返回客户可识别会话名和 Fmode Studio 安全打开命令,不暴露原始 Session ID。', inputSchema: { conversationId: z.string().optional(), customerName: z.string().optional() } }, wrap(qiweiAgentSessionGuide) ); server.registerTool( 'qiwei_agent_customer_intelligence', { title: '查看客户画像、待办与预警', description: '读取监听和 Agent 持续沉淀的客户画像、内部跟进待办和风险/高意向预警。用户问客户需求进展、下一步该做什么或有哪些风险时调用。', inputSchema: { conversationId: z.string().optional(), customerName: z.string().optional() } }, wrap(qiweiAgentCustomerIntelligence) ); server.registerTool( 'qiwei_agent_inbox', { title: '读取项目主控事件箱', description: '读取企微监听、Agent 决策、人工审核和发送动作的审计事件,供项目主控 Claude Code 会话掌握前端与客户会话状态。', inputSchema: { limit: z.number().int().min(1).max(200).optional() } }, wrap(qiweiAgentInbox) ); server.registerTool( 'qiwei_agent_listener', { title: '查看或控制企微消息接收', description: '企业版公网回调自动常驻,不需要监听开关;个人版可用 running 控制本地主动监听。停止 Agent 处理请使用全局 paused,消息仍会接收和保存。', inputSchema: { running: z.boolean() } }, wrap(qiweiAgentListener) ); server.registerTool( 'qiwei_agent_set_global', { title: '设置企微 Agent 全局策略', description: '设置 paused(全局暂停)、review(生成待审核草稿)、auto(仅高置信且无需人工时自动发送)、autopilot(全自动接管,非空回复直接发送)或 human(人工模式)。autopilot 必须同时传 confirmation=ENABLE_AUTOPILOT。', inputSchema: { mode: z.enum(['paused', 'review', 'auto', 'autopilot', 'human']), confirmation: z.string().optional(), } }, wrap(qiweiAgentSetGlobal) ); server.registerTool( 'qiwei_agent_generate_draft', { title: '让 Claude Code 生成企微回复草稿', description: '对指定客户会话的最新入站消息运行专用 Claude Code/Fmode Session,只生成待审核草稿,不发送给客户。', inputSchema: { conversationId: z.string() } }, wrap(qiweiAgentGenerateDraft) ); return server; } async function main() { const server = createServer(); const transport = new StdioServerTransport(); await server.connect(transport); ensureRelayDaemon(); void (async () => { const connected = await qiweiRelayConnect({}); if (connected.status !== 'ok') { console.error(`[RelayBootstrap] ${connected.assistantMessage || '企业回调连接失败'}`); return; } const started = await qiweiRelayStart({}); if (started.status !== 'ok') console.error(`[RelayBootstrap] ${started.assistantMessage || 'Relay 消费守护进程启动失败'}`); })().catch(error => console.error('[RelayBootstrap] 企业回调启动异常:', error.message)); } main().catch(error => { console.error('enterprise-wechat-assistant mcp server failed:', error); process.exit(1); });