Skip to content

工具开发概述 MCP

ChatAI Plugin 基于 MCP (Model Context Protocol) 标准实现工具系统,支持三种工具来源。

工具来源

三种工具来源

根据需求选择合适的工具开发方式,从简单到复杂依次为:自定义 JS → 内置工具 → 外部 MCP

来源位置说明热重载
内置工具src/mcp/tools/核心功能,24个类别模块化组织
自定义 JSdata/tools/用户脚本,无需修改源码
外部 MCPdata/mcp-servers.jsonnpm 包或远程服务器

Skills 文件系统

除了上述三种工具来源,系统还支持从 data/skills 目录加载技能文档:

文件格式说明自动激活
SKILL.mdMarkdown frontmatter + 指令正文按 autoActivate 配置
*.skill.yamlYAML 格式技能定义按 autoActivate 配置
*.skill.jsonJSON 格式技能定义按 autoActivate 配置

技能加载机制

  1. 默认暴露: 系统启动时扫描所有技能文件,只暴露技能列表(名称+描述)
  2. 显式加载: 模型通过 load_skill 工具请求加载完整技能内容
  3. 自动注入: autoActivate: true 的技能在对话开始时自动注入
  4. 上下文重载: 上下文压缩后自动重新注入已加载的技能

扫描路径

默认扫描以下目录:

  • data/skills/ - 主要技能目录
  • .cursor/skills/ - IDE 技能目录
  • .claude/skills/ - Claude Code 技能目录
  • .codex/skills/ - Codex 技能目录

Skills 配置文件

Skills 模块的完整配置集中在 data/skills.yaml(由 src/services/skills/SkillsConfig.js 加载与校验),与 MCP 服务器配置解耦。文件不存在时会以默认值自动创建,且支持热重载(hasChanged() 检测 mtime 变化后 reload())。所有配置均位于顶层 skills 键下。

顶层结构

yaml
skills:
  enabled: true          # 是否启用 Skills 模块
  mode: 'hybrid'         # 工作模式:hybrid / skills-only / mcp-only
  sources: {...}         # 工具源配置
  groups: [...]          # 工具组配置
  execution: {...}       # 工具执行配置
  dispatch: {...}        # 调度配置(轻量模型预筛工具组)
  documents: {...}       # SKILL.md 文档技能配置
  security: {...}        # 危险工具安全配置

工作模式 mode

取值说明
hybrid默认。同时加载 Skills 配置的工具(内置 + 自定义 JS)和 MCP 服务器工具
skills-only仅加载 Skills 配置的工具,忽略 MCP 服务器(isMcpEnabled() 返回 false
mcp-only仅加载 MCP 服务器工具,忽略内置与自定义 JS 工具(兼容旧行为)

无效值回退

mode 仅接受上述三个取值,配置为其他值时会在校验阶段告警并回退为 hybrid

工具源 sources

三种工具来源可独立启用/禁用,并各自维护禁用工具列表:

yaml
sources:
  builtin:                 # 内置工具
    enabled: true
    categories: []         # 启用的类别列表(空数组 = 启用全部)
    disabledTools: []      # 禁用的工具(优先级高于 categories)
  custom:                  # 自定义 JS 工具
    enabled: true
    path: 'data/tools'     # JS 工具目录(相对插件根目录)
    autoReload: true       # 文件变更自动重载
  mcp:                     # 外部 MCP 服务器工具
    enabled: true
    servers: []            # 启用的服务器列表
    disabledServers: []    # 禁用的服务器列表

disabledTools 合并

getDisabledTools() 会去重合并 builtincustommcp 三个来源各自的 disabledTools,任一来源禁用即全局禁用。

工具组 groups

groups 将工具按功能划分为逻辑组,供调度和权限管理使用。用户配置的 groups完全替换默认值(而非合并)。

yaml
groups:
  - index: 0                    # 组索引(唯一)
    name: 'basic'               # 组名称(唯一)
    description: '基础工具:获取时间、日期、农历、节日、系统环境信息等'
    tools: ['get_current_time', 'get_lunar_date', ...]  # 工具名列表
    enabled: true               # 是否启用该组
  - index: 6
    name: 'admin'
    description: '群管理:禁言、踢人、设置群名片/头衔、发送公告等'
    tools: ['mute_member', 'kick_member', ...]
    enabled: true
    requiredPermission: 'admin' # 该组所需权限(可选)
  - index: 17
    name: 'shell'
    description: '系统命令(危险工具)'
    tools: ['execute_command', 'get_system_info', ...]
    enabled: false              # 危险组默认关闭
    requiredPermission: 'master'
字段类型说明
indexnumber组索引,可通过 getGroupByIndex() 检索
namestring组名称,可通过 getGroupByName() 检索;缺失时告警
descriptionstring组说明
toolsstring[]组内工具名列表;非数组时会被重置为 []
enabledboolean是否启用;getEnabledGroups() 过滤 enabled !== false
requiredPermissionstring该组所需权限(如 adminmaster),可选

文档技能 documents

控制 SKILL.md 等文档技能的扫描与注入行为(对应 Skills 文件系统):

yaml
documents:
  enabled: true
  mode: 'auto'                  # auto / all / explicit
  paths:                        # 扫描目录列表
    - 'data/skills'
    - '.cursor/skills'
    - '.claude/skills'
    - '.codex/skills'
  maxDepth: 6                   # 目录递归最大深度
  maxFileBytes: 65536           # 单个技能文件最大字节数(<1024 时重置为 1024)
  maxPromptChars: 20000         # 注入 system prompt 的最大字符数(<1000 时重置为 1000)
mode匹配行为
auto默认。按上下文文本匹配技能的 name/description/relativePath/triggers
all未显式指定时注入全部技能
explicit仅注入 selectedNames 显式选中的技能

无效值回退

modeauto/all/explicit 时回退默认;paths 非数组、maxDepth < 0 时均回退默认值。

执行配置 execution

yaml
execution:
  timeout: 30000        # 单工具超时(毫秒),<1000 时重置为 1000
  maxParallel: 5        # 最大并行数,<1 重置为 1,>20 重置为 20
  retryOnError: false   # 出错是否重试
  maxRetries: 2         # 重试次数
  cacheResults: true    # 是否缓存结果
  cacheTTL: 60000       # 缓存有效期(毫秒)

调度配置 dispatch

启用后先用轻量模型判断需要哪些工具组,减少 token 消耗:

yaml
dispatch:
  enabled: false        # 默认关闭,可在管理面板启用
  useSummary: true      # 使用工具组摘要参与判断
  maxGroups: 3          # 最多选取的工具组数量

安全配置 security

yaml
security:
  dangerousTools:               # 危险工具列表
    - kick_member
    - mute_member
    - recall_message
    - write_file
    - delete_file
    - execute_command
  allowDangerous: false         # 是否允许执行危险工具
  dangerousRequiredPermission: 'master'  # 危险工具所需权限

与工具审批的关系

security.dangerousTools 会影响 工具审批 的风险分级:命中列表的工具会被判定为高风险。危险工具的执行还需满足 allowDangerousdangerousRequiredPermission 约束。

工具定义格式

所有工具遵循 MCP 标准的统一定义格式:

javascript
{
  // 工具名称(唯一标识,snake_case 格式)
  name: 'my_tool',
  
  // 工具描述(AI 可见,描述清晰有助于 AI 正确调用)
  description: '工具功能描述,说明何时使用、参数含义',
  
  // 参数定义(JSON Schema 格式)
  inputSchema: {
    type: 'object',
    properties: {
      param1: {
        type: 'string',
        description: '参数描述'
      }
    },
    required: ['param1']
  },
  
  // 处理函数(异步)
  handler: async (args) => {
    // 实现逻辑
    return { result: '...' }
  }
}

上下文访问

内置工具通过 ToolContext 访问运行时上下文,获取 Bot、事件、权限等信息。

ToolContext API

ToolContext 是工具执行时的核心上下文对象,定义于 src/mcp/BuiltinMcpServer.js

javascript
import { getBuiltinToolContext } from '../../mcp/BuiltinMcpServer.js'

handler: async (args) => {
  const ctx = getBuiltinToolContext()
  
  // 获取 Bot 实例(自动处理多 Bot 环境)
  const bot = ctx.getBot()
  // 支持指定 Bot ID
  const specificBot = ctx.getBot(botId)
  
  // 获取消息事件
  const event = ctx.getEvent()
  const userId = event?.user_id
  const groupId = event?.group_id
  
  // 检查是否为主人
  const isMaster = ctx.isMaster
  
  // 获取适配器信息
  const adapter = ctx.getAdapter()
  // 返回: { adapter: 'icqq'|'napcat'|'onebot', isNT: boolean, canAiVoice: boolean }
  
  // 快捷判断方法
  ctx.isIcqq()    // 是否 ICQQ 适配器
  ctx.isNapCat()  // 是否 NapCat 适配器
  ctx.isNT()      // 是否支持 NT 特性
  
  // 获取 Bot 在群内的权限
  const permission = await ctx.getBotPermission(groupId)
  // 返回: { role: 'owner'|'admin'|'member', isAdmin: boolean, isOwner: boolean, inGroup: boolean }
}

返回值格式

工具返回值会自动序列化并返回给 AI:

javascript
return { text: '结果文本' }
javascript
return { 
  success: true,
  data: { key: 'value' }
}
javascript
return {
  text: '当前时间: 14:30',
  datetime: '2024-12-15T06:30:00.000Z',
  timestamp: 1702622400000
}
javascript
// 方式1:抛出错误
throw new Error('操作失败:权限不足')

// 方式2:返回错误对象
return { error: true, message: '操作失败' }

返回值注意事项

  • 返回值应简洁明了,避免返回大量无关数据
  • text 字段会直接展示给 AI,应为人类可读格式
  • 结构化数据适合需要进一步处理的场景

开发流程

步骤说明要点
1. 确定类型选择内置/自定义/MCP简单功能用自定义 JS,复杂功能用内置工具
2. 定义接口名称、描述、参数描述要清晰,参数用 JSON Schema
3. 实现逻辑编写 handler 函数注意异常处理和权限检查
4. 测试验证API 测试或对话测试使用 #工具日志 查看调用详情
5. 部署启用配置权限后启用通过 Web 面板管理工具启用状态

详细文档

📚 选择适合的开发方式

文档适用场景难度
内置工具需要深度集成、访问内部 API⭐⭐⭐
自定义 JS快速创建自定义工具⭐⭐
高级开发进阶技巧与最佳实践⭐⭐⭐
MCP 服务器接入外部 MCP 服务⭐⭐
安全与权限了解工具安全机制⭐⭐

基于 MIT 许可发布