Skip to content

Tool Development Overview MCP ​

ChatAI Plugin implements its tool system based on MCP (Model Context Protocol) standard, supporting three tool sources.

Tool Sources ​

Three Tool Sources

Choose the appropriate development method based on your needs, from simple to complex: Custom JS → Built-in Tools → External MCP

SourceLocationDescriptionHot Reload
Built-in Toolssrc/mcp/tools/Core functionality, 25 categorized modules✅
Custom JSdata/tools/User scripts, no source code modification needed✅
External MCPdata/mcp-servers.jsonnpm packages or remote servers❌

Skills File System ​

In addition to the three tool sources above, the system also supports loading skill documents from the data/skills directory:

File FormatDescriptionAuto Activation
SKILL.mdMarkdown frontmatter + instruction bodyPer autoActivate config
*.skill.yamlYAML skill definitionPer autoActivate config
*.skill.jsonJSON skill definitionPer autoActivate config

Skill Loading Mechanism ​

  1. Default exposure: all skill files are scanned at startup, exposing only the skill list (name + description)
  2. Explicit loading: the model requests the full skill content through the load_skill tool
  3. Auto injection: skills with autoActivate: true are injected automatically at conversation start
  4. Context reload: already-loaded skills are re-injected after context compression

Scan Paths ​

The following directories are scanned by default:

  • data/skills/ - primary skill directory
  • .cursor/skills/ - IDE skill directory
  • .claude/skills/ - Claude Code skill directory
  • .codex/skills/ - Codex skill directory

Skills Config File ​

The full Skills module configuration lives in data/skills.yaml (loaded and validated by src/services/skills/SkillsConfig.js), decoupled from the MCP server config. The file is auto-created with defaults when missing and supports hot reload (hasChanged() detects mtime changes, then reload()). All configuration sits under the top-level skills key.

Top-level Structure ​

yaml
skills:
  enabled: true          # Whether the Skills module is enabled
  mode: 'hybrid'         # Working mode: hybrid / skills-only / mcp-only
  sources: {...}         # Tool source config
  groups: [...]          # Tool group config
  execution: {...}       # Tool execution config
  dispatch: {...}        # Dispatch config (lightweight model pre-selects tool groups)
  documents: {...}       # SKILL.md document skill config
  security: {...}        # Dangerous tool security config

Working Mode mode ​

ValueDescription
hybridDefault. Loads both Skills-configured tools (built-in + custom JS) and MCP server tools
skills-onlyLoads only Skills-configured tools and ignores MCP servers (isMcpEnabled() returns false)
mcp-onlyLoads only MCP server tools and ignores built-in and custom JS tools (legacy behavior)

Invalid value fallback

mode accepts only the three values above; any other value triggers a validation-time warning and falls back to hybrid.

Tool Sources sources ​

The three tool sources can be enabled/disabled independently, each keeping its own disabled-tool list:

yaml
sources:
  builtin:                 # Built-in tools
    enabled: true
    categories: []         # Enabled category list (empty array = enable all)
    disabledTools: []      # Disabled tools (takes precedence over categories)
  custom:                  # Custom JS tools
    enabled: true
    path: 'data/tools'     # JS tool directory (relative to the plugin root)
    autoReload: true       # Auto reload on file changes
  mcp:                     # External MCP server tools
    enabled: true
    servers: []            # Enabled server list
    disabledServers: []    # Disabled server list

disabledTools merge

getDisabledTools() deduplicates and merges the disabledTools of the builtin, custom, and mcp sources; a tool disabled by any source is disabled globally.

Tool Groups groups ​

groups divides tools into logical groups for dispatch and permission management. User-configured groups completely replace the defaults (no merging).

yaml
groups:
  - index: 0                    # Group index (unique)
    name: 'basic'               # Group name (unique)
    description: 'Basic tools: time, date, lunar date, festivals, system environment info, etc.'
    tools: ['get_current_time', 'get_lunar_date', ...]  # Tool name list
    enabled: true               # Whether this group is enabled
  - index: 6
    name: 'admin'
    description: 'Group admin: mute, kick, set group card/title, send announcements, etc.'
    tools: ['mute_member', 'kick_member', ...]
    enabled: true
    requiredPermission: 'admin' # Permission required for this group (optional)
  - index: 17
    name: 'shell'
    description: 'System commands (dangerous tools)'
    tools: ['execute_command', 'get_system_info', ...]
    enabled: false              # Dangerous group disabled by default
    requiredPermission: 'master'
FieldTypeDescription
indexnumberGroup index, retrievable via getGroupByIndex()
namestringGroup name, retrievable via getGroupByName(); warns when missing
descriptionstringGroup description
toolsstring[]Tool name list of the group; reset to [] when not an array
enabledbooleanWhether enabled; getEnabledGroups() filters enabled !== false
requiredPermissionstringPermission required for this group (e.g. admin, master), optional

Document Skills documents ​

Controls scanning and injection behavior of document skills like SKILL.md (see Skills File System):

yaml
documents:
  enabled: true
  mode: 'auto'                  # auto / all / explicit
  paths:                        # Directory scan list
    - 'data/skills'
    - '.cursor/skills'
    - '.claude/skills'
    - '.codex/skills'
  maxDepth: 6                   # Max directory recursion depth
  maxFileBytes: 65536           # Max bytes per skill file (reset to 1024 when < 1024)
  maxPromptChars: 20000         # Max chars injected into the system prompt (reset to 1000 when < 1000)
modeMatching Behavior
autoDefault. Matches skills by name/description/relativePath/triggers against the context text
allInjects all skills when none are explicitly specified
explicitInjects only the skills explicitly selected in selectedNames

Invalid value fallback

When mode is not auto/all/explicit, the default is restored; non-array paths and maxDepth < 0 also restore defaults.

Execution Config execution ​

yaml
execution:
  timeout: 30000        # Per-tool timeout (milliseconds); reset to 1000 when < 1000
  maxParallel: 5        # Max parallelism; reset to 1 when < 1, to 20 when > 20
  retryOnError: false   # Whether to retry on error
  maxRetries: 2         # Retry count
  cacheResults: true    # Whether to cache results
  cacheTTL: 60000       # Cache TTL (milliseconds)

Dispatch Config dispatch ​

When enabled, a lightweight model first decides which tool groups are needed, reducing token cost:

yaml
dispatch:
  enabled: false        # Disabled by default; can be enabled in the admin panel
  useSummary: true      # Use tool group summaries in the decision
  maxGroups: 3          # Max tool groups to select

Security Config security ​

yaml
security:
  dangerousTools:               # Dangerous tool list
    - kick_member
    - mute_member
    - recall_message
    - write_file
    - delete_file
    - execute_command
  allowDangerous: false         # Whether dangerous tools may execute
  dangerousRequiredPermission: 'master'  # Permission required for dangerous tools

Relationship to tool approval

security.dangerousTools affects the risk classification of tool approval: tools matching the list are rated high risk. Executing dangerous tools must additionally satisfy the allowDangerous and dangerousRequiredPermission constraints.

Tool Definition Format ​

All tools follow the MCP standard unified definition format:

javascript
{
  // Tool name (unique identifier, snake_case format)
  name: 'my_tool',
  
  // Tool description (visible to AI, clear description helps AI call correctly)
  description: 'Tool function description, explain when to use and parameter meanings',
  
  // Parameter definition (JSON Schema format)
  inputSchema: {
    type: 'object',
    properties: {
      param1: {
        type: 'string',
        description: 'Parameter description'
      }
    },
    required: ['param1']
  },
  
  // Handler function (async)
  handler: async (args) => {
    // Implementation logic
    return { result: '...' }
  }
}

Context Access ​

Tools access runtime context through the second argument context of the handler function. Platform differences, target ID types, send-result validation, and fallbacks for unsupported capabilities are all handled by the standard interface.

ToolContext API

context is a request-scoped object; tools created by the model must not import runtime singletons or read protocol-side objects directly.

javascript
handler: async (args, context) => {
  const api = context.getApi()
  const message = context.message
  const event = context.getEvent()
  const userId = event?.user_id
  const groupId = event?.group_id

  // Check whether the sender is the master
  const isMaster = context.isMaster()

  // Send/query through the standard interface; do not branch on QQBot, ICQQ, or OneBot inside tools
  const permission = await api.getBotPermission(groupId)
  // Returns: { role: 'owner'|'admin'|'member', isAdmin: boolean, isOwner: boolean, inGroup: boolean }
}

Return Format ​

Tool return values are automatically serialized and returned to AI:

javascript
return { text: 'Result text' }
javascript
return { 
  success: true,
  data: { key: 'value' }
}
javascript
return {
  text: 'Current time: 14:30',
  datetime: '2024-12-15T06:30:00.000Z',
  timestamp: 1702622400000
}
javascript
// Method 1: Throw error
throw new Error('Operation failed: Permission denied')

// Method 2: Return error object
return { error: true, message: 'Operation failed' }

Return Value Notes

  • Return values should be concise, avoid returning large amounts of irrelevant data
  • text field will be shown directly to AI, should be human-readable format
  • Structured data is suitable for scenarios requiring further processing

Knowledge Graph Tools ​

src/mcp/tools/knowledgeGraph.js provides 12 kg_* tools (category knowledgeGraph) backed by the kg_entities / kg_relationships tables, covering entity CRUD, version history, relationships, subgraph exploration and scope stats. When scope_id is omitted it is derived from the event context (group:<gid>:user:<uid> / group:<gid> / user:<uid> / global). See Built-in Tools for the full list.

Development Workflow ​

StepDescriptionKey Points
1. Determine TypeChoose built-in/custom/MCPUse custom JS for simple features, built-in for complex
2. Define InterfaceName, description, parametersClear description, use JSON Schema for params
3. Implement LogicWrite handler functionHandle exceptions and check permissions
4. Test & VerifyAPI test or conversation testUse #工具日志 to view call details
5. Deploy & EnableConfigure permissions and enableManage tool enable status via Web panel

Detailed Documentation ​

📚 Choose the Right Development Method

DocumentUse CaseDifficulty
Built-in ToolsDeep integration, access internal APIs⭐⭐⭐
Custom JS ToolsQuick development, no source modification⭐⭐
Advanced DevelopmentAdvanced techniques and best practices⭐⭐⭐
MCP ServerExternal services, reuse existing MCP⭐⭐
SecurityUnderstand tool security mechanisms⭐⭐

Released under the MIT License