第 10 章:实战 — 工具生态
前两章我们造了 MiniClaw 的"骨架":认证、配置、引擎、记忆、命令行界面。 但说白了,它现在就是一个"能聊天的终端"——你说一句,它回一句,仅此而已。
这一章,我们给 MiniClaw 装技能。让它不只是聊天,还能做事。
前提条件
- 已完成前面章节的学习,了解
@tool、create_sdk_mcp_server的用法- 已安装 SDK:
uv add claude-agent-sdk
1. 本章目标
想象一下你手机刚买来的时候——只能打电话发短信。装上微信就能聊天,装上高德就能导航, 装上支付宝就能付款。工具就是 MiniClaw 的"App Store"。
本章结束后,MiniClaw 将拥有 5 个内置技能:
| 技能 | 说明 | 举例 |
|---|---|---|
current_time |
查看当前时间 | "现在几点了?" |
system_info |
查看系统信息 | "我的电脑配置怎么样?" |
calculator |
做数学计算 | "帮我算 sin(45) + log(100)" |
note_save |
保存笔记 | "帮我记一下:明天下午开会" |
note_list |
查看所有笔记 | "我之前记了哪些笔记?" |
更重要的是,我们会建一套工具注册机制——以后想加新技能, 写个函数、注册一下就完事了,不用改引擎代码。
2. 整体架构
加完工具后,MiniClaw 的文件结构:
miniclaw/
__init__.py # 包初始化
__main__.py # 入口:python -m miniclaw
auth.py # 认证管理
config.py # 配置管理
engine.py # 对话引擎(本章更新)
memory.py # 对话记忆
cli.py # 命令行界面
tools/ # 新增:工具系统
__init__.py # 工具包入口
registry.py # 工具注册中心
builtin.py # 5 个内置工具
工作流程一目了然:
启动 MiniClaw
↓
创建 ToolRegistry(注册中心)
↓
register_builtin_tools() ← 把 5 个内置工具注册进去
↓
registry.create_server() ← 打包成 SDK MCP 服务器
↓
注入 engine.py ← 引擎带着工具启动
↓
Claude 自动使用工具 ← 用户问"现在几点",Claude 调用 current_time
3. 工具注册中心 (tools/registry.py)
为什么需要注册中心?
你可能会想:我直接在 engine.py 里写死工具列表不行吗?
行,但不好。原因有三:
- 解耦 — 引擎不应该知道具体有哪些工具,它只需要知道"有工具可用"
- 可扩展 — 未来加新工具,只要往注册中心注册就行,不用改引擎
- 白名单管理 — 注册中心自动生成
allowed_tools,不用手动拼字符串
核心代码
完整代码见
miniclaw/tools/registry.py
ToolRegistry 只有 4 个方法,每个都很直白:
class ToolRegistry:
def __init__(self, server_name: str = "miniclaw"):
self.server_name = server_name
self._tools: list[SdkMcpTool] = []
def register(self, tool_def: SdkMcpTool) -> None:
"""注册一个工具。"""
self._tools.append(tool_def)
def create_server(self) -> McpSdkServerConfig:
"""打包成 SDK MCP 服务器。"""
return create_sdk_mcp_server(name=self.server_name, tools=self._tools)
def get_allowed_tools(self) -> list[str]:
"""获取工具白名单,格式为 mcp__<服务器名>__<工具名>。"""
return [f"mcp__{self.server_name}__{t.name}" for t in self._tools]
说白了就是一个列表的封装。它的价值不在于代码复杂度,而在于建立了一个清晰的边界:
工具的定义、注册、打包都在 tools/ 包里搞定,引擎只管用。
4. 内置工具集 (tools/builtin.py)
完整代码见
miniclaw/tools/builtin.py
5 个工具,逐个讲解核心设计思路。
工具 1:current_time — 查看当前时间
@tool(
"current_time",
"获取当前日期和时间,包含年月日、时分秒、星期几",
{"timezone": str},
annotations=ToolAnnotations(readOnlyHint=True),
)
async def current_time(args: dict) -> dict:
...
设计要点:
- readOnlyHint=True:查时间是纯读操作,不改任何东西
- 参数 timezone 是字符串,Claude 会根据用户的问法自动传合适的值
- 用标准库 timedelta 做时区偏移,不依赖 pytz
工具 2:system_info — 查看系统信息
@tool(
"system_info",
"获取当前系统信息,包括操作系统、CPU、内存、磁盘使用情况",
{"detail_level": str},
annotations=ToolAnnotations(readOnlyHint=True),
)
设计要点:
- 同样 readOnlyHint=True——只读不写
- detail_level 参数让 Claude 选择 "basic" 或 "full" 模式
- 只用 platform、os、shutil 标准库,零额外依赖
工具 3:calculator — 数学计算器
@tool(
"calculator",
"计算数学表达式。支持加减乘除、幂运算、三角函数、对数等",
{"expression": str},
)
设计要点:
- 没标注 readOnlyHint——有意不加,让你对比"有注解"和"没注解"的区别
- 安全措施:eval 的 __builtins__ 设为空字典,只暴露白名单里的数学函数。
即使表达式里写了 os.system("rm -rf /"),也会报错而不是真的执行
- 用 try/except 兜住所有异常,不让工具崩掉
工具 4:note_save — 保存笔记
@tool(
"note_save",
"保存一条笔记到本地。笔记会存储在 ~/.miniclaw/notes/ 目录下",
{"title": str, "content": str},
)
设计要点:
- 这个工具会写文件,所以没标 readOnlyHint
- 笔记存到 ~/.miniclaw/notes/,自动建目录
- 文件名用"时间戳 + 标题",不会覆盖旧笔记
- 标题做了特殊字符清理,避免文件名问题
工具 5:note_list — 列出所有笔记
@tool(
"note_list",
"列出所有已保存的笔记。可以按关键词筛选",
{"keyword": str},
annotations=ToolAnnotations(readOnlyHint=True),
)
设计要点:
- readOnlyHint=True——只读不写
- keyword 参数支持按关键词筛选
- 笔记按时间倒序排列(最新的在前面)
ToolAnnotations 小结
5 个工具中,3 个标了 readOnlyHint=True,2 个没标。规律很简单:
| 工具 | 是否只读 | 有无注解 |
|---|---|---|
current_time |
只读 | readOnlyHint=True |
system_info |
只读 | readOnlyHint=True |
calculator |
只读(但我们故意不标) | 无 |
note_save |
会写文件 | 无 |
note_list |
只读 | readOnlyHint=True |
建议: 只读工具都标上 readOnlyHint=True,这是个好习惯。
注册函数
一个 register_builtin_tools() 函数,一键把 5 个工具注册进去:
def register_builtin_tools(registry: ToolRegistry) -> None:
registry.register(current_time)
registry.register(system_info)
registry.register(calculator)
registry.register(note_save)
registry.register(note_list)
调用方只需要两行:
registry = ToolRegistry()
register_builtin_tools(registry)
# 5 个工具就绑好了
5. 集成到引擎
engine.py 的变化
之前创建 ClaudeAgentOptions 时没有工具,现在加两行:
options = ClaudeAgentOptions(
system_prompt=system_prompt,
permission_mode="bypassPermissions",
max_turns=10,
# ---- 新增 ----
mcp_servers=mcp_servers, # 工具服务器
allowed_tools=allowed_tools, # 工具白名单
)
引擎通过构造函数接收这两个参数,完全不需要知道具体有哪些工具。
main.py 的变化
入口文件负责"组装":
from miniclaw.tools import ToolRegistry, register_builtin_tools
registry = ToolRegistry()
register_builtin_tools(registry)
engine = ChatEngine(
config=config,
memory=memory,
mcp_servers={"miniclaw": registry.create_server()},
allowed_tools=registry.get_allowed_tools(),
)
注意 mcp_servers 的 key 必须和 ToolRegistry 的 server_name 一致(默认都是 "miniclaw")。
不一致的话 allowed_tools 里的名字就对不上,工具就用不了。
6. 用户自定义工具(扩展机制)
想给 MiniClaw 加新技能?比如查天气。两步搞定:
Step 1:写工具函数
# miniclaw/tools/weather.py
@tool("get_weather", "查询指定城市的天气信息", {"city": str},
annotations=ToolAnnotations(readOnlyHint=True))
async def get_weather(args: dict) -> dict:
city = args["city"]
# 这里调用真正的天气 API
return {"content": [{"type": "text", "text": f"{city}:晴,25°C"}]}
Step 2:注册一下
from miniclaw.tools.weather import get_weather
registry.register(get_weather) # 加这一行
不用改引擎,不用改 CLI,不用改任何其他文件。
get_allowed_tools() 自动包含新工具。这就是注册中心的价值。
7. 运行效果
python -m miniclaw
查时间:
你: 现在几点了?
MiniClaw: 当前时间是 2026-02-25 14:32:18,星期三,时区 Asia/Shanghai (UTC+8)。
查系统:
你: 看看我电脑的配置
MiniClaw: 操作系统 Darwin 25.3.0,架构 arm64,Python 3.12.1,CPU 10 核心。
算数学:
你: 帮我算 sin(pi/4) + log10(1000)
MiniClaw: sin(pi/4) + log10(1000) = 0.7071 + 3 = 3.7071
记笔记 + 查笔记:
你: 帮我记一下,明天下午三点和老王开会
MiniClaw: 笔记已保存到 ~/.miniclaw/notes/20260225_143518_明天下午三点和老王开会.md
你: 我之前记了哪些笔记?
MiniClaw: 共 3 条笔记:
- 明天下午三点和老王开会
- 买菜清单
- 项目 TODO
组合使用(最酷的部分):
你: 现在几点了?顺便帮我记一下,明天要交报告
MiniClaw: 现在是 14:35:22。已帮你记下"明天要交报告",保存到
~/.miniclaw/notes/20260225_143522_明天要交报告.md。
Claude 自动先调 current_time,再调 note_save,把结果整合成一句话。
你不需要告诉它"先查时间再记笔记"——它自己判断该用什么工具、什么顺序。
8. 小结
这一章做了三件事:
- 建了工具注册中心 —
ToolRegistry统一管理注册、打包、白名单 - 写了 5 个内置工具 — 时间、系统信息、计算器、保存笔记、查看笔记
- 集成到引擎 — 在
ClaudeAgentOptions中注入mcp_servers和allowed_tools
核心收获:
@tool装饰器把 Python 函数变成 Claude 可调用的技能ToolAnnotations给工具标上"只读"等标签,帮助 Claude 安全决策ToolRegistry让扩展新能力的成本极低——写个函数、注册一下、完事了- Claude 自动判断用什么工具、什么顺序,你不需要手动编排
下一章,我们将为 MiniClaw 添加钩子系统——在 Claude 调用工具前后插入安全检查。
本章文件清单
10-实战-工具生态/
README.md # 你正在读的这个文件
miniclaw/
__init__.py # 包初始化
__main__.py # 入口文件(集成工具系统)
auth.py # 认证管理
config.py # 配置管理
engine.py # 对话引擎(已更新,支持工具注入)
memory.py # 对话记忆
cli.py # 命令行界面
tools/
__init__.py # 工具包入口
registry.py # 工具注册中心
builtin.py # 5 个内置工具