基于MCP协议构建智能Discord机器人:架构设计与工程实践
1. 项目概述当Discord遇上MCP一个聊天机器人的“大脑”升级如果你在Discord社区里待过无论是游戏公会、技术社群还是兴趣小组大概率都见过那些功能各异的机器人。它们能帮你查攻略、放音乐、管理成员甚至陪你聊天。但不知道你有没有遇到过这样的尴尬机器人A能查天气机器人B能翻译机器人C能生成图片你需要它们时得分别去艾特不同的机器人或者记住一堆不同的命令前缀。整个体验是割裂的就像你手机里装了几十个功能单一的App而不是一个智能助手。最近在GitHub上看到一个名为SaseQ/discord-mcp的项目它让我眼前一亮。这个项目本质上是一个“适配器”或者说“桥梁”它把Discord这个庞大的聊天平台和一套名为MCPModel Context Protocol的协议连接了起来。简单来说它让Discord机器人不再仅仅是执行预设命令的“脚本”而是能像ChatGPT那样拥有一个可以动态调用各种工具和服务的“大脑”。这个“大脑”就是MCP。你可以把它想象成一个标准化的“工具插槽”协议。任何符合MCP标准的工具——无论是查询数据库、调用API、操作文件还是连接其他服务——都可以被轻松地“插”到这个协议上。而discord-mcp项目就是把这个强大的“工具插槽”系统接到了Discord的聊天界面里。这意味着你只需要在Discord里和一个机器人对话它就能根据你的自然语言指令自动选择并调用背后数十甚至上百种不同的工具来为你服务。这不仅仅是功能的堆砌而是一次体验的范式转移从“人适应机器”的命令式交互转向“机器理解人”的意图式交互。2. 核心架构与设计思路拆解2.1 为什么是MCP协议层的价值解析要理解discord-mcp的价值必须先搞懂MCP是什么。MCP全称Model Context Protocol是由Anthropic公司提出并开源的一套协议。它的核心目标是为大语言模型LLM提供一个标准化、安全、可扩展的方式来访问外部工具、数据和功能。在没有MCP之前如果你想给一个聊天机器人比如基于Discord.py的机器人增加新功能流程通常是这样的1找到或编写一个功能的Python代码2将这个功能封装成一个机器人命令如!weather 北京3修改机器人主程序注册这个命令4部署更新。整个过程是紧耦合的功能与机器人主程序深度绑定添加、移除或更新功能都相当麻烦。MCP引入了一种“松耦合”的架构。它将工具Tools的定义、调用和结果返回标准化了。工具提供者Server只需要按照MCP协议暴露出一系列工具的描述名称、参数、说明而工具消费者Client通常是一个LLM应用则通过标准的JSON-RPC over STDIO/SSE与Server通信来列出、调用这些工具。对于discord-mcp项目而言它扮演的角色就是一个MCP Client。它的设计思路可以拆解为以下几个关键点协议抽象层项目核心是实现了与MCP Server通信的客户端逻辑。它需要处理连接建立、工具列表获取、工具调用请求的封装、执行结果的解析与回调这一整套流程。这层抽象使得机器人后端的“智能核心”与具体的工具实现完全分离。Discord事件适配层这一层负责监听Discord平台上的事件主要是消息事件。当用户在频道或私聊中发送消息时该层需要捕获消息内容、用户上下文、频道信息等并将其格式化为MCP Client能够处理的“用户查询”User Query。LLM集成与意图理解层这是项目的“大脑”。它需要集成一个LLM例如OpenAI的GPT系列、Anthropic的Claude或开源的Llama等。当收到用户查询后LLM的任务是理解用户的自然语言意图并参考当前可用的工具列表来自MCP Server决定是否需要调用工具、调用哪个工具、传入什么参数。这一步是“命令式”到“意图式”转变的关键。响应渲染与交互层当工具调用返回结果后可能是纯文本、JSON、图片URL甚至是复杂的结构化数据。这一层需要将结果以适合Discord展示的形式嵌入消息、代码块、文件附件等渲染出来并发送回对应的频道或用户。它可能还需要处理多轮对话的上下文管理。这种架构带来的最大优势是“动态能力扩展”。你不需要为了给机器人增加一个“股票查询”功能而去修改discord-mcp的代码。你只需要启动一个提供了“查询股票”工具的MCP Server并在配置中告诉discord-mcp这个Server的地址。下次用户问“苹果公司股价多少”LLM就能自动发现并使用这个新工具。机器人的能力边界从此由背后连接的MCP Server集群决定。2.2 项目核心组件与数据流让我们更具体地看看一次完整的用户交互数据是如何在这个系统中流动的用户触发用户在Discord频道输入“帮我把‘https://example.com/data.csv’这个文件里的销售额前三名总结一下。”事件捕获discord-mcp的Discord客户端基于discord.py或类似库捕获到这条消息。它会进行初步过滤如忽略其他机器人的消息、检查触发前缀或是否了机器人。上下文构建项目将当前消息内容、可能的对话历史最近几条消息、用户身份、频道信息等打包构建成一个包含上下文Context的提示词Prompt准备发送给LLM。意图决策LLM收到提示词和当前可用的工具列表例如read_file,query_database,search_web,data_summarize。LLM分析用户意图判断需要调用read_file读取URL文件和data_summarize数据总结这两个工具并生成结构化的工具调用请求包括工具名和参数url: “https://example.com/data.csv”,metric: “sales”,top_n: 3。工具调用discord-mcp作为MCP Client将LLM生成的工具调用请求通过MCP协议发送给对应的MCP Server。例如read_file可能由一个“文件操作Server”提供data_summarize由另一个“数据分析Server”提供。结果聚合MCP Server执行工具返回结果如CSV文件内容、总结后的文本。discord-mcp接收这些结果。响应生成项目可能将原始结果再次喂给LLM让其生成一段面向用户的、自然友好的回复比如“已从您提供的链接读取数据销售额前三名分别是张三120万、李四98万、王五85万。需要我进一步分析趋势吗”消息发送最终生成的回复被发送回Discord原始频道完成交互。整个过程中discord-mcp项目本身不关心文件怎么读、数据怎么分析它只关心如何正确地协调LLM、MCP协议和Discord平台。这种关注点分离Separation of Concerns的设计使得每个部分都可以独立演进、优化和替换。注意在实际部署中LLM的API调用如OpenAI和多个MCP Server的通信可能会引入显著的延迟。项目设计必须考虑异步操作、超时处理以及向用户发送“正在思考”之类的中间状态消息以提升体验。3. 核心细节解析与实操要点3.1 MCP Server的选择与集成策略discord-mcp的强大与否几乎完全取决于它背后连接的MCP Server。目前MCP生态虽然年轻但已经有不少现成的Server实现和工具集。1. 官方与社区Server文件系统modelcontextprotocol/server-filesystem提供读写本地文件的能力。这是最基础也最强大的工具之一可以让机器人访问服务器上的文档、配置文件、日志等。安全警告必须严格控制其可访问的目录范围否则将造成严重的安全风险。网络搜索modelcontextprotocol/server-duckduckgo集成DuckDuckGo搜索让机器人能获取实时信息。相比传统搜索引擎API通过MCP集成更标准化。Gitmodelcontextprotocol/server-git提供克隆仓库、查看提交历史、读取文件等Git操作。对于技术社区这可以让机器人帮忙查看项目代码、检查最新提交。SQLite数据库modelcontextprotocol/server-sqlite直接对SQLite数据库执行查询。可以用于查询社区数据、用户信息等。2. 自建MCP Server当现有工具无法满足需求时你需要自己编写MCP Server。这通常是项目落地中最关键的一环。编写一个MCP Server并不复杂核心是实现几个标准的JSON-RPC方法tools/list,tools/call并用标准格式描述你的工具。工具设计原则工具应该粒度适中、功能单一。例如不要做一个“用户管理系统”工具而是拆分成get_user_info、update_user_role、list_online_users等多个工具。清晰的描述description和参数定义inputSchema至关重要这直接决定了LLM能否正确理解和使用它。安全与权限在Server内部实现严格的权限校验。例如一个“发送社区公告”的工具应该检查调用者最终是Discord用户是否具备管理员角色。权限信息可以通过MCP调用链从Discord传递过来但需要在Server逻辑中处理。3. 集成策略本地进程模式最常见的模式。将MCP Server作为子进程启动通过标准输入输出STDIO与discord-mcp通信。优点是部署简单、延迟低。适合文件操作、数据库查询等对延迟敏感的工具。HTTP/SSE模式MCP Server作为一个HTTP服务运行通过Server-Sent EventsSSE进行通信。这更适合需要常驻、服务多个客户端的工具或者当你希望将工具服务部署在独立服务器时。混合模式一个discord-mcp实例可以同时连接多个MCP Server有的以子进程运行有的以远程HTTP服务连接。在项目配置文件中你需要清晰地定义每个Server的URI如stdio:///path/to/server-script或sse://http://tool-server:8080/sse。实操心得在项目初期建议从1-2个核心的MCP Server开始比如文件系统和网络搜索。先跑通整个流程验证LLM调用工具的准确性。然后再逐步添加更复杂的Server如连接内部数据库或API的Server。每次新增工具后都需要用丰富的测试用例去“教育”LLM让它学会在什么场景下使用新工具。你可以通过精心设计系统提示词System Prompt来提供工具使用范例。3.2 LLM的提示词工程与上下文管理LLM是discord-mcp的决策中枢。它的表现直接决定了机器人的智能程度和用户体验。这里有两个核心挑战一是如何让LLM准确理解用户意图并选择正确工具二是如何在Discord的多人、多频道、异步聊天环境中管理好上下文。1. 系统提示词System Prompt设计这是“调教”机器人的关键。你的提示词需要明确告诉LLM身份与目标“你是一个部署在Discord上的智能助手可以通过调用各种工具来帮助用户解决问题。”能力范围“这是你可以使用的工具列表[工具列表]。每个工具都有描述和参数格式。”行为规范“你必须根据用户问题决定是否使用工具。如果用户问题需要实时信息或具体操作就使用工具如果是普通聊天则直接回复。”“调用工具时必须严格遵循工具定义的参数格式JSON Schema。”“如果工具返回了结果你需要对结果进行解释、总结并以友好、易懂的方式回复用户。”“如果用户的问题模糊你需要主动询问澄清。”Discord特定规则“在Discord中你可以使用Markdown简化格式。提及用户时使用用户ID。避免输出过长的文本必要时可以分多条消息发送。”一个常见的技巧是在提示词中加入“少样本学习Few-shot Learning”的例子示例1 用户今天旧金山的天气怎么样 助手思考用户需要实时天气信息我需要使用 search_web 工具。 助手调用工具{name: search_web, arguments: {query: 旧金山 天气 今天}} 等待工具返回天气信息... 助手回复根据查询旧金山今天晴天气温15-22摄氏度。 示例2 用户我们仓库里README文件最近谁修改过 助手思考这需要查询Git历史使用 git_log 工具。 助手调用工具{name: git_log, arguments: {filepath: ./README.md, limit: 5}}2. 上下文管理策略Discord聊天可能是混乱的多人穿插发言、话题快速切换。简单的“保存最近N条消息”策略会很快失效。基于会话Session的上下文为每个用户 频道对或每个线程维护一个独立的对话会话。一个会话内的消息才构成连贯的上下文。当用户开启一个新话题或长时间无互动后可以重置会话。关键信息摘要对于长对话可以在后台运行一个轻量级LLM定期对之前的对话内容进行摘要然后将摘要而非原始历史消息放入上下文。这能有效节省Token并保持核心信息。工具调用历史的纳入将本次对话中已发生过的工具调用及其结果以精简的形式加入后续对话的上下文避免LLM忘记已经做过什么。实操心得LLM的每次调用都消耗Token和金钱。优化上下文就是优化成本。对于工具描述可以采用动态加载的方式在初始提示词中只包含工具的核心名称和一句话描述当LLM表现出对某个工具的兴趣时再通过后续消息“注入”该工具的详细参数定义。这需要更复杂的工程实现但能显著降低初始提示词的篇幅和成本。4. 实操过程与核心环节实现4.1 环境搭建与基础配置假设我们从一个相对干净的Linux服务器或开发环境开始。以下是基于Node.js生态的典型搭建步骤因为目前许多MCP Server是用TypeScript/JavaScript编写的。步骤1项目获取与依赖安装# 1. 克隆 discord-mcp 项目仓库假设项目是公开的 git clone https://github.com/SaseQ/discord-mcp.git cd discord-mcp # 2. 安装项目依赖假设是Node.js项目 npm install # 3. 安装你计划使用的MCP Servers。例如安装文件系统和DuckDuckGo搜索服务器。 # 这些通常也是npm包可以全局安装或在项目内安装。 npm install -g modelcontextprotocol/server-filesystem modelcontextprotocol/server-duckduckgo # 或者作为开发依赖安装 npm install --save-dev modelcontextprotocol/server-filesystem步骤2配置文件详解discord-mcp的核心是一个配置文件可能是config.json,config.yaml或.env文件。你需要仔细配置以下部分{ “discord”: { “token”: “YOUR_DISCORD_BOT_TOKEN_HERE”, // 从Discord开发者门户获取 “clientId”: “YOUR_CLIENT_ID_HERE”, // 用于邀请机器人 “intents”: [“Guilds”, “GuildMessages”, “MessageContent”] // 必需的网关意图 }, “llm”: { “provider”: “openai”, // 或 “anthropic”, “ollama” (本地) “apiKey”: “YOUR_OPENAI_API_KEY_HERE”, “model”: “gpt-4-turbo-preview”, // 根据成本和性能选择 “temperature”: 0.1, // 较低的温度使工具调用更稳定 “maxTokens”: 1000 }, “mcpServers”: [ { “name”: “filesystem”, “command”: “npx”, // 启动命令 “args”: [“-y”, “modelcontextprotocol/server-filesystem”, “--directory”, “/safe/path/for/bot”], // 参数限制访问目录 “type”: “stdio” // 通信类型 }, { “name”: “websearch”, “command”: “npx”, “args”: [“-y”, “modelcontextprotocol/server-duckduckgo”], “type”: “stdio” } // 可以添加更多Server ], “systemPrompt”: “你是一个智能Discord助手...此处填入你精心设计的提示词” }关键配置解析Discord Token这是机器人的身份凭证。绝对不要泄露或提交到代码仓库。务必使用环境变量或安全的密钥管理服务。MessageContent Intent这是2022年后Discord的新要求机器人要读取消息内容必须在开发者门户和代码中都申请此意图。LLM Model选择gpt-3.5-turbo成本低但工具调用准确性可能不足。gpt-4或claude-3系列在复杂工具调用场景下表现好得多但成本高。对于内部测试使用本地部署的Ollama运行llama3等模型是零成本的好选择。MCP Server目录限制文件系统Server的--directory参数至关重要必须将其限制在一个仅包含机器人所需文件的沙箱目录内绝不能是/或/home。步骤3运行与测试# 1. 设置环境变量如果配置从环境变量读取 export DISCORD_TOKEN‘your_token’ export OPENAI_API_KEY‘your_key’ # 2. 启动 discord-mcp npm start # 或 node index.js # 3. 观察日志。成功连接后你应该能看到“Logged in as 机器人名!”以及“Connected to MCP servers: filesystem, websearch”之类的信息。将机器人邀请到你的测试Discord服务器尝试发送消息。如果一切正常机器人应该会回复。你可以从简单的问题开始测试比如“你能做什么”测试基础对话然后测试工具调用如“搜索一下最新的Node.js版本”测试网络搜索。4.2 开发自定义MCP Server实战当内置工具不够用时你需要开发自己的MCP Server。我们以一个简单的“社区待办事项Todo管理器”为例展示开发流程。目标创建一个MCP Server提供两个工具add_todo添加待办和list_todos列出待办。步骤1初始化项目mkdir mcp-server-todo cd mcp-server-todo npm init -y npm install modelcontextprotocol/sdk步骤2编写Server核心代码 (server.js)const { Server } require(‘modelcontextprotocol/sdk/server/index.js’); const { StdioServerTransport } require(‘modelcontextprotocol/sdk/server/stdio.js’); // 1. 创建Server实例 const server new Server( { name: “todo-list-server”, version: “1.0.0”, }, { capabilities: { tools: {}, // 声明本Server提供工具 }, } ); // 2. 一个简单的内存存储生产环境需用数据库 let todoItems []; // 3. 定义 ‘add_todo’ 工具 server.setRequestHandler(‘tools/call’, async (request) { const { name, arguments } request.params; if (name ‘add_todo’) { const { task, assignee } arguments; if (!task) { throw new Error(“Task description is required.”); } const newTodo { id: Date.now().toString(), task, assignee: assignee || ‘Anyone’, createdAt: new Date().toISOString(), completed: false }; todoItems.push(newTodo); return { content: [ { type: “text”, text: ✅ Todo added: “${task}” (assigned to ${newTodo.assignee}). ID: ${newTodo.id}, }, ], }; } // 4. 定义 ‘list_todos’ 工具 if (name ‘list_todos’) { const { filter } arguments || {}; let list todoItems; if (filter ‘completed’) { list todoItems.filter(item item.completed); } else if (filter ‘pending’) { list todoItems.filter(item !item.completed); } const listText list.map(item - [${item.completed ? ‘x’ : ‘ ’}] ${item.task} (ID: ${item.id}, Assignee: ${item.assignee}) ).join(‘\n’) || ‘No todo items found.’; return { content: [ { type: “text”, text: **Todo List** (${filter || ‘all’}):\n${listText}, }, ], }; } throw new Error(Unknown tool: ${name}); }); // 5. 定义 ‘tools/list’ 处理器向客户端宣告本Server提供的工具 server.setRequestHandler(‘tools/list’, async () ({ tools: [ { name: “add_todo”, description: “Add a new todo item to the community list.”, inputSchema: { type: “object”, properties: { task: { type: “string”, description: “The description of the todo task.” }, assignee: { type: “string”, description: “(Optional) The person assigned to this task. Defaults to ‘Anyone’.” } }, required: [“task”] } }, { name: “list_todos”, description: “List all todo items, optionally filtered by status.”, inputSchema: { type: “object”, properties: { filter: { type: “string”, enum: [“all”, “completed”, “pending”], description: “Filter the list: ‘all’, ‘completed’, or ‘pending’. Defaults to ‘all’.” } } } } ], })); // 6. 启动Server使用标准输入输出 async function run() { const transport new StdioServerTransport(); await server.connect(transport); console.error(“Todo MCP Server running on stdio…”); } run().catch((error) { console.error(“Server error:”, error); process.exit(1); });步骤3在discord-mcp配置中集成修改discord-mcp的配置文件在mcpServers数组中添加一项{ “name”: “todo”, “command”: “node”, “args”: [“/absolute/path/to/your/mcp-server-todo/server.js”], “type”: “stdio” }步骤4测试重启discord-mcp。现在当用户在Discord中说“添加一个待办购买服务器分配给Alice”LLM应该能理解并调用add_todo工具。用户说“看看有哪些待办”则会调用list_todos。实操心得在定义工具的inputSchema时description字段至关重要。LLM完全依赖这些描述来理解工具的用途和参数含义。描述要清晰、具体最好包含示例。例如task的描述是“待办任务的描述”这就比简单的“任务”要好。对于枚举型参数如filter使用enum字段可以极大地提高LLM调用的准确性。5. 常见问题与排查技巧实录在实际部署和运行discord-mcp这类项目时你会遇到各种各样的问题。以下是我在实践过程中总结的一些典型问题及其排查思路。5.1 机器人无响应或报错问题现象机器人上线后在Discord中发送消息机器人完全不回复或控制台出现错误日志。排查步骤检查基础连接确认Discord Token是否正确是否有权限Message Content Intent是否在开发者门户和代码中都已开启。查看控制台启动日志是否成功登录Ready! Logged in as ...。检查机器人是否已被邀请到服务器并拥有发送消息的频道权限。检查LLM配置API Key是否正确是否有余额或调用额度。模型名称是否拼写正确例如gpt-3.5-turbo与gpt-3.5-turbo-0125是不同的。网络是否能正常访问LLM供应商的API如OpenAI被墙需确保服务器网络通畅或配置代理此处仅指技术上的网络可达性不涉及任何违规内容。检查MCP Server连接查看日志中是否成功连接了所有配置的MCP Server。常见的错误是Server启动命令路径错误或Server本身有bug导致崩溃。手动在命令行运行MCP Server的启动命令看是否能独立运行。例如运行npx -y modelcontextprotocol/server-filesystem --directory /tmp观察是否有输出。检查消息处理逻辑确认代码中是否正确监听了messageCreate事件。是否有消息过滤逻辑如忽略机器人自身消息、忽略无前缀消息过于严格导致你的消息被误过滤。5.2 LLM不调用工具或调用错误问题现象机器人能回复但总是用LLM的固有知识回答而不去调用工具。或者调用工具时参数错误。排查与解决提示词问题这是最常见的原因。检查你的systemPrompt。是否清晰说明了工具的存在提示词中必须明确告知LLM“你拥有以下工具”并以清晰格式列出工具列表。是否提供了调用范例加入几个用户提问 - 助手思考 - 调用工具的完整示例效果立竿见影。是否限制了“瞎编”在提示词中强调“如果你不知道或需要最新信息请使用工具不要编造答案。”工具描述问题检查MCP Server中tools/list返回的工具描述。描述是否足够清晰description字段要一句话讲清工具用途。A tool to get weather.不如Fetch current weather conditions and forecast for a given city.参数定义是否明确inputSchema中的每个参数都要有description。LLM靠这个来理解“city”是城市名“days”是预报天数。LLM温度Temperature设置过高温度参数控制输出的随机性。对于工具调用这种需要精确性的任务建议将temperature设置为较低值如0.1或0.2。过高的温度会导致LLM“自由发挥”不按套路出牌。上下文污染如果对话历史很长且包含了许多与工具调用无关的闲聊可能会干扰LLM的判断。尝试缩短上下文长度或在每次工具调用决策时使用一个只包含最近几条消息和工具列表的“干净”提示词。5.3 性能与成本优化问题现象机器人响应慢或者LLM API调用费用快速增长。优化策略异步与流式响应不要让用户等待所有工具调用和LLM生成完成。在收到用户消息后可以先发送一个“正在思考...”的提示消息。然后异步执行后续流程完成后编辑原消息或发送新消息。这能极大提升用户体验。工具调用并行化如果LLM决定需要调用多个独立的工具例如同时查询天气和新闻可以尝试并行调用而不是串行等待。LLM缓存对于重复或相似的问题例如不同用户频繁询问“如何邀请机器人”可以使用一个简单的内存缓存如Redis存储LLM的回复。下次遇到相同或高度相似的问题时直接返回缓存结果避免调用API。注意设置合理的缓存过期时间。选择性价比更高的模型对于简单的工具调用和回复生成gpt-3.5-turbo通常足够成本远低于GPT-4。可以将复杂的、需要深度推理的任务路由到GPT-4简单任务路由到GPT-3.5。精简上下文这是降低Token消耗最有效的方法。动态工具描述如前所述不要一次性把所有工具的详细JSON Schema都塞进提示词。只放名称和简短描述必要时再补充细节。对话摘要实现一个后台任务定期将长对话历史总结成一段简短的摘要用摘要替代原始历史。忽略无关历史在构建提示词时可以智能地筛选出与当前问题相关的历史消息而不是无脑地塞入所有消息。5.4 安全与权限管控问题隐患一个拥有文件访问和网络搜索能力的机器人如果被恶意使用或权限控制不当后果严重。防护措施最小权限原则MCP Server层面文件系统Server必须限制在沙箱目录。数据库Server必须使用只读或低权限账户。工具设计层面像“删除文件”、“执行系统命令”这类高危工具要么不提供要么在其内部实现严格的权限校验如检查调用者Discord ID是否在白名单的管理员列表中。用户输入净化与校验永远不要相信来自用户的输入直接用于工具参数。LLM生成的参数也应被视为“用户输入”。路径遍历如果工具参数包含文件路径必须检查是否包含..等字符防止跳出沙箱。SQL注入如果工具直接拼接SQL必须使用参数化查询或ORM。命令注入如果工具会执行系统命令绝对禁止将用户输入直接拼接到命令中。操作审计与日志记录所有工具调用日志包括时间、用户、调用的工具、参数敏感参数可脱敏。这便于事后追溯和问题排查。速率限制在Discord机器人层面和关键工具层面实施速率限制防止用户滥用导致资源耗尽或API费用暴涨。一个真实的踩坑记录我曾配置了一个可以读取服务器日志文件的MCP Server用于调试。由于疏忽没有限制目录。结果有用户在Discord里开玩笑说“看看secret.txt里有什么”LLM真的调用了文件读取工具试图去寻找一个根本不存在的文件。虽然没造成数据泄露但暴露了工具能力。教训是任何工具的能力暴露前都必须经过“最小权限”和“输入校验”的双重审视。后来我修改了配置将该Server的访问目录严格限制在/var/log/myapp-safe子目录下并移除了所有不必要的工具。