Claude-Code-操作模型-三-
Claude Code 操作模型(三)
原文:The Claude Code Operating Model
译者:飞龙
对于每个问题,请明确指出文件名和行号。
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP Server started"); // Note: logs go to stderr, not stdout
}
main().catch(console.error);
有几个关键的设计细节值得特别关注。
-
工具描述至关重要:
server.tool()的第二个参数(描述字符串)是 Claude 判断何时调用工具的核心依据。描述越清晰准确,Claude 就越能精准识别意图并调用合适的工具。 -
类型安全与校验:输入参数通过 Zod schema 定义,不仅提供了严格的类型安全,还能在运行时自动校验参数,防止错误数据传入。
-
日志输出规范:调试日志必须通过
console.error输出到stderr,而不是stdout。这是因为stdout通道专门用于传输 JSON-RPC 消息;混入普通日志会导致协议解析失败。这一机制与 Hooks 脚本的调试输出原则一致。
Python 实现同样简洁优雅。
from mcp.server import Server
from mcp.server.stdio import stdio_server
# Initializing the Server and In-Memory Storage
server = Server("todo-server")
todos = []
@server.tool("todo_add")
async def add_todo(text: str) -> str:
"""Add a new to-do item"""
todo_id = generate_id() # Assume the generate_id function is
# already defined
todos.append({"id": todo_id, "text": text, "done": False})
return f"Added: {todo_id} - {text}"
@server.tool("todo_list")
async def list_todos() -> str:
"""List all to-do items"""
if not todos:
# No todos found.
return "No todos found."
return "\n".join(
return "No todos found."
return "\n".join(
f"[{'x' if t['done'] else ' '}] {t['id']}: {t['text']}"
for t in todos
)
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream)
if __name__ == "__main__":
import asyncio
asyncio.run(main())
完成开发后,需要在配置文件中注册你的自定义服务器。以下示例演示了如何启动一个编译好的 MCP 服务(Python 项目请将命令调整为 python)。
{
"mcpServers": {
"my-todo": {
"command": "node",
"args": ["./mcp-server/build/index.js"]
}
}
}
常见的 MCP 服务器生态
Anthropic 和 MCP 社区维护了大量开箱即用的 MCP 服务器。本节列出了最常用的几类。
官方服务器
Anthropic 官方服务器的概览如表 6.2所示。
| 服务器名称 | 主要用途 | 安装/启动命令 |
| --- | --- | --- |
| server-filesystem | 文件系统读写操作 | npx @modelcontextprotocol/server‑filesystem <path> |
| server-fetch | 发送 HTTP 请求 | npx @modelcontextprotocol/server‑fetch |
| server-postgres | PostgreSQL 数据库查询 | npx @modelcontextprotocol/server‑postgres |
| server-memory | 跨会话持久化记忆存储 | npx @modelcontextprotocol/server‑memory |
| server-git | Git 版本控制操作 | npx @modelcontextprotocol/server‑git |
表 6.2:官方 MCP 服务器概览
注意:启动 server‑filesystem 时,请将 path 参数替换为你实际想要授权的目录——单个项目使用 ./src,整个工作区使用 ~/projects/my‑app。服务器无法读取该目录之外的任何内容。
热门第三方服务器
热门第三方服务器的概览如表 6.3所示。
| 服务器名称 | 主要用途 | 传输方式 |
| --- | --- | --- |
| GitHub MCP | GitHub 协作管理(PR、issue、代码搜索) | HTTP |
| Notion MCP | Notion 文档读写 | HTTP |
| Sentry MCP | 错误跟踪与日志分析 | HTTP |
| Context7 | 获取最新技术文档 | stdio |
| Bytebase DBHub | 多种数据库的统一访问与管理 | stdio |
表 6.3:热门第三方 MCP 服务器概览
实用配置组合
整合一个文件系统服务器(限定于你授权的目录)、一个用于 HTTP 请求的 fetch 服务器、一个用于 issue 和 pull request 的 GitHub 服务器,以及一个如 Postgres 的数据库服务器。
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
},
"fetch": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-fetch"]
},
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/",
"headers": {
"Authorization": "Bearer ${GITHUB_TOKEN}"
}
},
"database": {
"command": "npx",
"args": ["-y", "@bytebase/dbhub", "--dsn", "${DATABASE_URL}"]
}
}
}
${GITHUB_TOKEN}和${DATABASE_URL}在配置中是环境变量占位符。请确保在使用前已在终端环境中正确设置它们。
使用此配置,开发者可以在单个 Claude 会话中无缝完成复杂的工作流,而之前这些工作流需要在多个工具之间切换。
-
数据查询:"帮我计算数据库中过去 7 天的订单总金额" → 自动调用数据库服务器执行 SQL 查询
-
任务管理:"整理上述 bug 描述并创建一个新的 GitHub Issue" → 自动调用 GitHub 服务器通过 API 提交工单
-
文档检索:"获取 React 19 中 Server Components 的最新官方文档" → 自动调用 fetch 服务器实时检索网页内容
安全机制:信任的边界
MCP 极大地扩展了 Claude 的能力边界,使其能够直接操作外部系统。这一特性既是其核心优势,也是重大的安全风险,因此必须极其谨慎地加以处理。
三层防御机制
为了平衡灵活性与安全性,MCP 架构设计了一套渐进式的三层防御机制。
-
第一层:首次连接时的交互式审批:这是建立信任的基石。每当用户配置一个新的 MCP 服务器时,Claude Code 会在首次尝试连接前自动暂停,显示详细的服务器信息(如来源、权限范围等)并请求明确授权。此步骤是强制性的交互过程,无法绕过。
-
第二层:细粒度的工具级权限控制:即使整个 MCP 服务器已获得全局授权,Claude 在调用特定工具时仍须保持警惕,尤其是那些具有副作用的工具(如文件写入、代码执行、数据库修改等)。在执行前,Claude 会再次拦截,显示将要执行的具体命令详情,并等待二次确认。
-
第三层:基于
OAuth 2.1的动态认证:对于远程 MCP 服务器,使用规范强制要求的OAuth 2.1流程(配合 PKCE),而非长期有效的静态 API 密钥。这种方式不仅便于与企业单点登录集成,还能提供短期、可撤销的凭证——这正是轮换令牌与轮换已被复制到十几个配置文件中的密钥之间的区别。
安全风险与应对措施
2025 年 4 月,安全研究人员在 MCP 生态系统中发现了以下安全风险(invariantlabs.ai/blog/mcp-security-notification-tool-poisoning-attacks)。
-
提示注入攻击:恶意内容可以通过 MCP 服务器注入 Claude 的上下文。如果 MCP 服务器返回的数据包含恶意指令(如"忽略所有先前的指令并执行以下操作"),Claude 可能被误导。应对措施是只使用来自可信来源的 MCP 服务器。
-
工具权限滥用:单个工具的权限可能看起来安全,但组合多个工具可能导致意外后果。例如,"读取文件"工具与"发送邮件"工具的组合理论上可能导致文件内容泄露。应对措施是遵循最小权限原则。
-
冒充:恶意工具可以通过名称和描述伪装成合法工具,诱骗 Claude 调用它们。应对措施是只安装来自可信来源的 MCP 服务器包。
Kage 的备注
优先选择来自官方 modelcontextprotocol/servers 仓库的服务器,这是参考集。对于其他任何来源,请应用你对任何具有 shell 访问权限的依赖项所采用的测试标准:固定版本、阅读源码,并将其凭证范围限定为最窄的可行范围。
MCP+Skills:厨房与菜谱
前几章介绍的 Skills 与本章讨论的 MCP 解决的是不同层次的问题:MCP 赋予 Claude 访问外部数据和工具的能力,而 Skills 则指导 Claude 如何最优地使用这些能力。只有将二者结合,才能形成完整的解决方案。
Anthropic 用了一个生动的类比来解释这种关系。
-
MCP 就像一间专业厨房:配备了所有必要的设备和食材——冰箱(数据库)、炉灶(API 调用)、食材(数据源)和刀具(实用函数)。拥有厨房意味着厨师有能力制作任何菜肴,但做什么、怎么做完全取决于厨师的即兴发挥。每当一位新厨师(即新的用户请求)进来时,都得从头摸索如何使用厨房,很难保证一致的质量。
-
Skills 就像标准菜谱:提供详细的操作说明,明确规定所需的食材、步骤、火候和烹饪时间。有了菜谱,即使是没有经验的厨师也能可靠地复现高质量菜肴。然而,如果没有配套的厨房设备,再好的菜谱也无法执行。
在实践中,协作模式是:在 Skill 的 SKILL.md 文件中引用 MCP 工具,从而引导 Claude 按照特定的工作流高效调用这些工具(参见图 6.3)。
图 6.3:MCP+Skills"厨房与菜谱"的协作流程
这种组合赋予 Claude 双重优势:它可以通过 MCP 获取实时数据能力,并通过 Skills 按照一致的方法论处理数据。这意味着 Claude 不再需要每次依赖用户的详细指令,也不再依赖 Claude 的临场判断,从而实现稳定高效的自动化执行。
企业部署
对于拥有数十甚至数百名工程师的团队来说,依赖个人自行配置 MCP 服务器并非可扩展的方案。为此,Claude Code 提供了企业级集中管理机制。
管理员可以使用 managed‑mcp.json 配置文件(或 Claude for Enterprise 管理控制台)为组织内的所有用户预先配置 MCP 服务器。这些服务器将自动对所有成员生效,无需个人操作。此外,管理员可以预先授权这些服务器,从而免除每位用户首次使用时的审批弹窗流程。
这种集中管理模式实现了双赢:对开发者而言,一旦公司的 Jira、Linear、内部知识库和数据仓库工具通过 MCP 集成,整个工程团队都能从 AI 驱动的工作流中受益,实现"零配置、开箱即用";对安全团队而言,所有 MCP 访问都通过经过严格审查的服务器路由,与分散的个人配置相比,集中审计和控制显著增强了安全性。
调试与故障排除
调试 MCP 服务器通常比调试 Hooks 脚本更具挑战性,因为它涉及进程间通信、网络连接和复杂的认证流程等多个方面。
常见调试方法
以下是一些常见的调试方法:
# 列出所有已配置的服务器及其当前状态
claude mcp list
# 专门测试某个特定服务器的连通性
claude mcp test my-server
# 启用 MCP 调试模式
claude --mcp-debug
MCP 工具输出的 Token 管理
为解决 MCP 工具可能返回海量数据的情况(例如,一个没有 LIMIT 子句的数据库查询可能返回数万条记录),Claude Code 内置了专门的 Token 管理机制。
-
警告阈值:当输出超过 10,000 个 Token 时,系统会显示警告提示
-
截断阈值:当输出超过 25,000 个 Token 时,系统会自动截断内容,以防止上下文溢出
-
自定义配置:用户可以通过设置环境变量
MAX_MCP_OUTPUT_TOKENS来调整这一输出限制,以适应不同的业务场景
常见问题
常见的 MCP 问题及解决方案如表 6.4所示,可以帮助用户快速定位并解决开发过程中遇到的典型问题。
| 问题现象 | 可能原因 | 解决方案 |
| --- | --- | --- |
| 服务器无法启动 | 命令或路径配置错误 | 检查配置文件中的 command 和 args 参数;尝试在终端中手动运行命令以验证其能否正常启动 |
| 连接超时 | 网络不稳定或服务器响应缓慢 | 设置环境变量 MCP_TIMEOUT(单位为毫秒)以延长等待时间 |
| 认证失败 | Token 错误、缺失或已过期 | 确保相关环境变量(如 API_KEY)已正确设置;检查 Token 的有效性并及时刷新 |
| 输出被截断 | 返回数据超过 Token 限制 | 提高 MAX_MCP_OUTPUT_TOKENS 的上限;优化查询逻辑(如添加 LIMIT),使服务器返回更精简的数据 |
| JSON 解析错误 | 服务器标准输出(stdout)被日志污染 | 确保服务器将调试日志和错误信息写入标准错误(stderr),使 stdout 仅保留纯 JSON 数据 |
表 6.4:常见 MCP 问题及解决方案
小结
MCP 解决了 AI 工具集成中的一个根本性问题:通过一个统一的开放协议,将各种 AI 客户端与多样化的数据源无缝连接,无需为每种组合单独开发适配层。该协议基于 JSON-RPC 2.0 进行通信,其中 stdio 适用于本地进程通信,HTTP 适用于远程服务调用(SSE 支持已被弃用);同时,得益于环境变量替换机制,配置文件可以安全地纳入版本控制系统。
从工程设计的角度来看,MCP 的核心价值不仅在于其连接能力,更在于这种连接能力的标准化。如今,一个集成了 Claude 的 MCP 服务器,理论上可以无缝兼容任何遵循该标准的 AI 客户端;反过来,随着该标准被更广泛地采纳,Claude 可访问的 MCP 服务器生态也将持续扩大。从 2024 年 11 月发布到 2025 年 12 月捐赠给 Linux 基金会,MCP 在短短 13 个月内从一个单一公司的项目转变为由联盟管理的行业标准。这一快速演进本身就有力地证明了 AI 工具生态对标准化的迫切需求。
与第 5 章中讨论的 Hooks 以及前面介绍的 Skills 相比,MCP 的定位有着本质的不同:Hooks 旨在“定义 Claude 行为的边界”,Skills 侧重于“引导 Claude 的执行策略”,而 MCP 则致力于“拓展 Claude 获取数据和服务的边界”。三者相辅相成——MCP 提供连接能力,Skills 管理调用逻辑,Hooks 建立安全屏障——共同构成了一个完整的 AI 工程体系。
在第 7 章中,我们将超越交互式使用场景,探索 Claude Code 如何在无人值守环境中运行,重点介绍其 Headless 模式以及与 CI/CD 流水线的集成实践。
讨论问题
-
审视你当前的工作流程:哪些数据源或工具如果能让 Claude 实时访问,会显著提升效率?尝试为这些工具设计一个
.mcp.json配置文件。 -
除了便捷性之外,使用 MCP 与直接将数据粘贴到对话中(例如将导出的 Jira CSV 文件发送给 Claude)之间有什么本质区别?在哪些具体场景下,直接粘贴数据实际上可能是更好的选择?
-
在“厨房与食谱”的协作架构中(MCP 作为厨房,Skills 作为食谱),如果你需要为公司内部工具设计一套完整的集成方案,你会如何规划?(提示:在 MCP 服务器层,应暴露哪些核心工具以最大化效用?在 Skills 引导层,相应的 Skill 应如何指导 Claude 在调用这些工具时遵循特定的业务流程?)
第六章:Headless 模式与 CI/CD 集成
“如果你没有找到一种在睡觉时也能赚钱的方法,你将工作到死。”——沃伦·巴菲特
公司的技术团队只有三名资深工程师,却每天需要审查二十多个 PR。按照惯例,每个 PR 必须至少由一名资深工程师批准才能合并。后果显而易见:PR 堆积如山,发布频率被迫下降,开发人员不得不在等待审查期间切换到其他任务,导致频繁的上下文切换和效率降低。更严重的是,在审查高峰期,人工审查的质量开始下降;疲惫的审查者容易忽略安全漏洞或性能问题,而一旦这些问题进入生产环境,修复成本将成倍增加。
经过一周的调研,小雪提出了一个方案:引入 Claude 作为 PR 的“初审员”,自动执行安全检查、代码质量分析和测试覆盖率评估,并生成结构化的审查报告。人工审查者则基于这些报告做出最终决策,将注意力集中在业务逻辑和架构设计等核心领域。这种方式并非取代人工审查,而是将工程师从重复性的机械检查中解放出来,让他们能够专注于只有人类才能做出的更高层次的判断。
“Claude Code 能在无人值守的环境中自动运行吗?”小冰问道。
“这正是 Headless 模式的核心所在,”Kage 回答道。“在前六章中,我们一直以‘交互模式’使用 Claude:你输入命令,它响应,如此循环往复。但从本章开始,Claude Code 将摆脱人工实时操作,能够在后台、在 CI/CD 流水线中,甚至在凌晨三点无人值守时独立工作。这一转变的意义不亚于 Docker 将应用从‘绑定到特定机器’转变为‘可在任何环境中运行’。”
从交互到无人值守:一次关键的架构演进
在交互模式下,Claude Code 充当“现场对话伙伴”的角色:用户输入命令,Claude 立即响应,用户确认结果后继续下一轮交互。这种模式非常适合探索性工作,例如代码调试、架构头脑风暴或增量式模块重构。然而,在自动化场景中,键盘前没有人,没有人检查中间步骤,整个流程必须能够从头到尾独立运行,零人工干预。
Headless 模式正是为此而生。通过使用 -p(或 --print)参数,用户可以明确告诉 Claude Code:“这是一次非交互式调用;请直接输出结果,任务完成后即退出,无需等待任何用户输入。”
最常见的 Headless 模式调用方式
claude -p "Analyze security vulnerabilities in the src/ directory"
在将其粘贴到流水线之前有一个注意事项:单独使用 -p 并不能使运行无人值守。没有权限标志时,打印模式仍然会在 Claude 首次想要读取文件或运行命令时停下来请求批准——这在 CI 中意味着作业会一直挂起直到超时。本章后面每个示例都会添加 --allowedTools,正是出于这个原因。
等价完整形式
claude --print "Analyze security vulnerabilities in the src/ directory"
然而,-p 只是入口点。真正将 Claude Code 转变为“可编程组件”的,是其全面的参数系统。要完全理解这个系统,首先有必要澄清交互模式与 Headless 模式之间的核心差异(见 表 7.1)。
| 输入方式 || 多轮实时对话 || Headless 模式 |
| --- | --- | --- |
| 输出方式 || 终端 TUI,实时渲染 || |
| 权限确认 || 多轮实时对话 || 无界面,仅标准输出(stdout) |
| 会话持久化 || 流式 Markdown 渲染 || 纯文本 / JSON / 流式 JSON |
| 成本控制 || 弹出交互式提示,需要手动确认 || 根据白名单自动跳过或执行 |
| 典型场景 || 自动保存,支持可恢复传输(resume) || 默认不保存(可按需指定) |
| 交互模式 || 依赖人工观察和中断 || 通过 --max-turns/--max-budget-usd 进行硬限制 |
| 终端 TUI,实时渲染 || 开发调试、架构探索 || CI/CD 流水线、定时任务、批处理 |
表 7.1:交互模式与 Headless 模式的核心差异
这种演进的意义远不止“仅仅移除一个界面”。在交互模式中,人类实际上扮演了三个关键角色:权限批准者(实时决定是否允许执行特定命令)、质量检查者(立即判断输出是否符合预期)以及流程控制者(根据中间结果动态调整下一条指令)。当这三个角色都不存在时,系统必须在执行前将所有规则“硬编码”到参数中:授权调用哪些工具?允许的最大对话轮数是多少?成本预算上限是多少?下游程序必须遵循什么输出格式才能解析结果?这就是 Headless 模式庞大参数系统的核心使命:将过去依赖人类直觉和实时判断的流程,转变为一套确定性、可预测且可自动执行的工程规范。
核心参数系统:四个维度的控制
Kage 在白板上将参数系统画成四个控制区域,而不是一个矩阵。输出格式、成本、安全性和执行控制是四个相互独立的类别,而不是单一空间的四个象限(图 7.1)。
图 7.1:Headless 模式参数系统的四个维度
“看,”Kage 指着白板说,“Headless 模式的参数并不是随机或混乱的,而是可以归类为这四个维度。任何生产级的无人值守调用都必须在所有四个方面做出清晰而果断的选择。”
输出格式控制
--output-format 参数充当“协议转换器”,将 Claude Code 与下游自动化流水线连接起来。它决定输出数据的结构,并直接影响后续脚本或系统如何消费这些结果。
该参数包含三种核心格式:text、json 和 stream-json。
text 格式
最简单的纯文本输出,保留语言的自然流动,适合直接阅读或简单脚本处理。
claude -p "List the main issues" --output-format text
返回的是纯散文,没有任何包装:
主要问题集中在 src/auth/session.ts:
1. 会话令牌使用 == 进行比较,而不是常量时间比较(第 47 行)。
2. 刷新路径从不会使旧令牌失效,因此被盗令牌会一直有效(第 92 行)。
3. 登录端点没有速率限制(第 15 行)。
json 格式
输出一个完整、严格结构化的 JSON 对象。它不仅包含任务结果,还封装了丰富的执行元数据,是构建健壮 CI/CD 流水线的基石。
claude -p "Review PR" --output-format json
使用 json 格式时,返回对象包含审计和监控所需的全面信息。
{
"type": "result",
"subtype": "success",
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"is_error": false,
"duration_ms": 12345,
"duration_api_ms": 10000,
"num_turns": 5,
"total_cost_usd": 0.0342,
"usage": {
"input_tokens": 5000,
"output_tokens": 1500,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 3000
},
"result": "Found 2 Critical issues:\n1. SQL injection risk...",
"structured_output": null
}
在自动化运维和成本优化中,以下字段至关重要。
-
total_cost_usd可用于精确的成本跟踪。在 CI/CD 流水线中,用户可以将每次运行的成本记录到数据库中,以生成诸如“每个 PR 的 AI 审查成本”之类的报告,甚至可以自动告警或阻止超出预算的部门。 -
num_turns可用于监控执行轮数,反映模型交互的实际深度(与配置的--max-turns限制相比)。 -
usage.cache_read_input_tokens反映提示缓存的有效性。在频繁调用相同 System Prompt 或基础代码库的场景中,较高的cache_read_input_tokens值意味着跳过了更多计算,从而成本更低、速度更快。
stream-json 格式
逐行输出 JSON 事件(即 NDJSON 格式),专为实时监控长时间运行任务而设计。示例:
claude-p "Analyze code" --output-format stream-json
在输出中,每一行都是一个独立的 JSON 对象,并严格按时间排序。
{"type":"system","subtype":"init","session_id":"...",
"model":"claude-sonnet-4-6","tools":["Read","Grep","Glob"]}
{"type":"assistant","message":{"role":"assistant",
"content":[{"type":"text","text":"Analyzing..."}]}}
{"type":"user","message":{"role":"user","content":
[{"type":"tool_result","tool_use_id":"...","content":"..."}]}}
{"type":"assistant","message":{"role":"assistant",
"content":[{"type":"text","text":"Analysis complete."}]}}
{"type":"result","subtype":"success","total_cost_usd":0.03,
"result":"Final result"}
请特别注意第一行的 system/init 事件,它会报告当前会话的模型版本、可用工具列表以及 MCP 服务器状态等初始化细节。在调试 CI 配置问题时,这一特性尤其有用。
“三种格式的选择其实很简单,”Kage 总结道。“供人阅读时使用纯文本格式,供机器解析时使用 JSON 格式,而 stream-json 格式则用于实时监控。”
成本护栏
在交互模式中,你可以随时按 Ctrl + C 中断 Claude 的执行。然而,在 Headless 模式中,由于没有“人工断路器”机制,你必须通过参数来强制执行控制。参见以下代码示例:
限制执行轮数(防止过度分析)
claude -p "Review code" --max-turns 5
设置硬性成本上限(USD)
claude -p "Analyze logs" --max-budget-usd 0.50
双重保护:同时限制轮数和成本
claude -p "Analyze the entire codebase" --max-turns 10 --max-budget-usd 2.00
--max-turns 和 --max-budget-usd 从不同维度进行约束。
-
--max-turns:限制的是智能体轮次的数量——即助手在循环中自身的轮次——而非工具调用次数。Claude 可以在一个轮次内发起多次工具调用,因此轮次与工具调用并非一一对应;如果你对num_turns进行监测,会发现它的读数低于工具调用次数。 -
--max-budget-usd:限制 API 调用的总花费。即使轮次很少(例如只有 3 轮),如果每一轮涉及大量 token(比如读取一个庞大的日志文件),累计成本仍然可能很高。在这种情况下,该参数提供了关键的保护。
这两个参数触发的是根本不同的行为。
-
当达到
--max-turns时,运行会以错误状态终止,并在 JSON 输出中以子类型error_max_turns报告。可能仍会返回部分文本,但它不是一个完整的答案:请检查退出状态和结果子类型,否则脚本会将一次被截断的运行视为成功的运行。 -
当触发
--max-budget-usd时,执行过程会立即被强制停止,并且不再输出任何后续结论。在 JSON 输出中,此类事件会以子类型error_max_budget_usd标记。
“在生产环境中,务必同时配置这两个参数,”Kage 强调说。“--max-turns 防止 Claude 在处理复杂任务时陷入无限递归,而 --max-budget-usd 则防止因意外的大规模分析而耗尽你的 API 预算。这两道防线缺一不可。”
安全边界:工具权限
在 Headless 模式下,由于工具调用没有人工确认步骤,你必须预先定义 Claude 的能力边界。这是 CI/CD 场景中最关键的安全设置。参见以下代码示例:
# Whitelist mode: only allow read-only tools (safest review configuration)
claude -p "Review code changes" --allowedTools "Read,Grep,Glob"
# Blacklist mode: prohibit specific tools
claude -p "Generate documentation" --disallowedTools "Bash"
# Fine-grained mode: allow specific command patterns
claude -p "Check Git history" --allowedTools "Read,Grep,Glob,Bash(git log *),Bash(git diff *)"
--allowedTools(白名单)和 --disallowedTools(黑名单)具有根本不同的安全语义。
-
白名单基于 “封闭世界假设”:只有明确列出的工具才被允许;其他所有工具都被拒绝
-
黑名单基于 “开放世界假设”:除明确禁止的工具外,所有工具都被允许
在自动化场景中,白名单始终比黑名单更安全。这是因为使用白名单时,你无需预想每一个潜在危险的工具,只需列出你实际需要的工具即可。
请注意,--allowedTools 支持模式匹配,并且模式中的空白字符是有意义的。Bash(git log *)——星号前有一个空格——只允许 Bash 执行以“git log”开头并后跟一个空格的命令。去掉那个空格,Bash(git log*) 也会匹配 git log-something;同样的陷阱会把 Bash(git diff*) 变成一个能匹配 git diff-index 的模式。在白名单中,这是一个安全漏洞,而不是笔误。请写上那个空格。
此外,还有一个更激进的参数 --dangerously-skip-permissions,它会跳过所有权限检查。不要忽视其名称中 dangerously 这个词;它并非装饰。在 CI 环境中,除非你在严格隔离的容器中运行,并且确信 Claude 的行为不会影响容器之外的任何事物,否则绝对不要使用这个参数。
执行控制:模型、提示词、结构化输出
我们可以通过模型选择、System Prompt 定制、结构化输出等参数来控制 Claude 的“思考过程”。
模型选择
不同的任务需要不同的模型,你需要根据自己的需求进行权衡。参见以下代码示例:
# Simple format check (fast and low cost)
claude -p "Check code formatting" --model claude-haiku-4-5
# In-depth security review (using the most powerful model)
claude -p "Security vulnerability analysis" --model claude-sonnet-4-6
# Specify fallback model (automatically downgrade if the primary model is overloaded)
claude -p "Code review" --model claude-sonnet-4-6 --fallback-model haiku
--fallback-model 是一个在生产中容易被忽视但至关重要的参数。在 API 高峰期,如果主模型不可用并阻塞了 CI 流水线,所有 PR 都会陷入停滞。设置一个回退模型可以确保流水线继续运行,虽然质量会略有牺牲,但能避免工作流中断。
System Prompt 定制
通过定制 System Prompt 来定义 Claude 的角色和行为边界。参见以下代码示例。
# Completely replace the System Prompt (this will make Claude forget its default identity as a code assistant and only follow the new instructions)
claude -p "Analyze code" --system-prompt "You are an application security expert specializing in the OWASP Top 10."
# Append to the System Prompt (recommended; retains Claude's core capabilities while adding specific constraints)
claude -p "Review PR" --append-system-prompt "Be sure to check for hardcoded sensitive information."
# Load appended prompt from a file (suitable for large projects with complex review standards)
claude -p "Review code" --append-system-prompt-file ./review-guidelines.txt
--system-prompt 和 --append-system-prompt 的关键区别在于它们如何处理 Claude 的默认行为。
-
--system-prompt:完全覆盖 Claude 的默认 System Prompt。Claude 将不再认为自己是代码助手,也不理解如何正确使用Read、Grep或Bash等内置工具。它只会机械地遵循你提供的新指令,这很容易导致工具调用失败或行为失控。 -
--append-system-prompt:将你的自定义内容追加到 Claude 默认 System Prompt 之后。Claude 保留所有核心能力,同时严格遵循你的附加指令。这适用于绝大多数 CI/CD 自动化场景。
结构化输出
强制 Claude 输出符合特定 JSON Schema 的数据,实现无缝的程序化集成。参见以下代码示例。
claude -p "Extract all security issues from the code" \
--output-format json \
--json-schema '{
"type": "object",
"properties": {
"issues": {
"type": "array",
"items": {
"type": "object",
"properties": {
"severity": {"type": "string",
"enum": ["critical", "high", "medium", "low"]},
"file": {"type": "string"},
"line": {"type": "number"},
"description": {"type": "string"}
},
"required": ["severity", "file", "description"]
}
}
},
"required": ["issues"]
}'
使用 `--json-schema` 时,输出会根据该 schema 进行校验。若不匹配,Claude 会被重新提示,但仅限固定的重试次数——并非无限重试。如果它仍然无法生成符合要求的内容,本次运行会返回错误结果而非有效数据,因此请编写好失败路径。当校验成功时,数据会出现在 JSON 输出的 `structured_output` 字段中。
Unix 管道:将 Claude 集成到命令行工作流中
“我注意到一个有趣的设计,”小月说。“看起来 Claude Code 支持标准输入(stdin)?”
“没错,”Kage 回答道。“这是 Headless 模式最优雅的功能之一:Claude Code 可以无缝集成到 Unix 管道中,成为你工具链中一个灵活的组件。”
Unix 哲学的核心是“每个工具做好一件事,并通过管道组合起来”。Claude Code 充分体现了这一设计原则。请看以下代码示例。
# 分析日志文件
cat server.log | claude -p "找出所有 500 错误并总结根本原因"
# 分析 git 变更
git diff HEAD~1 | claude -p "总结本次提交的变更,并按 Conventional Commits 规范格式化"
# 解析 API 响应
curl -s https://api.example.com/health | claude -p "判断服务是否健康"
更有趣的是多阶段管道组合;Claude 既能接收上游数据,也能将处理后的结果传递给下游工具。请看以下代码示例。
# Claude 分析 → jq 提取 → 发送通知
claude -p "检查安全漏洞" --output-format json | \
jq -r '.result' | \
mail -s "Security Scan Report" security@company.com
# Grep 预过滤 → Claude 深度分析
grep -r "TODO" src/ | claude -p "按优先级对这些 TODO 项进行分类"
# Claude 生成代码 → 直接写入文件
claude -p "生成一个 Express 健康检查路由" --output-format text > routes/health.js
在批处理中,这种管道能力将 Claude 变成了一个语义过滤器。经典文本工具——grep、awk、sed——非常擅长精确的模式匹配,但完全不理解含义;Claude 理解文本,但并不是为了将数 GB 的数据流经自身而构建的。将前者通过管道传给后者,你就能同时获得两者的优势。
实践示例 1:使用 GitHub Actions 实现自动化代码审查
理论讨论已经足够充分;接下来,让我们用一个完整的工程示例来演示。
Anthropic 提供了两种 GitHub Actions 集成选项:第一种是使用官方 Action(anthropics/claude-code-action@v1),封装程度高,易于配置;第二种是直接调用 CLI,灵活性更高,控制更精细。
使用官方 Action
Claude Code 自带一个便捷的安装命令:在交互模式下,运行 /install-github-app 即可引导用户完成安装 GitHub App、配置 Secrets 和创建 Workflow 文件的整个过程。
如果你更喜欢手动配置,步骤也很简单。首先,进入你的 GitHub 仓库,然后点击 Settings | Secrets | Actions,添加 ANTHROPIC_API_KEY,然后创建以下 Workflow 文件。
# .github/workflows/claude-review.yml
name: Claude PR Review
on:
pull_request:
types: [opened, synchronize, reopened]
issue_comment:
types: [created]
# Automatically cancel running old jobs for new commits on the same PR to save resources
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
review:
runs-on: ubuntu-latest
# Only run when PR-related events are triggered, or when the comment contains "@claude"
if: |
github.event_name == 'pull_request' ||
contains(github.event.comment.body, '@claude')
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch the full history for diff analysis
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Please review the code changes in this PR, focusing on the following aspects.
1. Security vulnerabilities (such as SQL injection, XSS attacks, hardcoded sensitive information, etc.).
2. Unhandled exceptions and edge cases.
3. Performance risks (such as N+1 queries, memory leak risks).
Please output the report in the following format.
## Review Report
### Critical (Must Fix)
### Warning (Recommended Fix)
### Suggestion (Optional Optimization)
claude_args: >-
--allowedTools "Read,Grep,Glob"
--max-turns 10
--model claude-sonnet-4-6
这个配置中有三点值得特别注意。
-
双触发模式:官方 Action v1 版本能够自动检测触发模式。
-
代理模式:当配置中提供了 prompt 参数时触发。每次创建或更新 PR 时,Claude 都会自动执行预设的审查任务。
-
标记模式:当用户在 PR 评论中提及
@claude时触发。在这种情况下,Claude 会像团队成员一样交互式地回应具体问题。
这两种模式可以在同一个 Workflow 文件中共存,互不冲突。
-
-
参数传递机制:官方 Action v1 版本极大地简化了配置结构。所有 Claude Code CLI 参数现在都统一通过
claude_args字段传递。Beta 版本中此前独立的max_turns和allowed_tools等字段已被弃用,不再支持。 -
并发控制策略:通过设置
cancel-in-progress: true,可以实现高效的并发控制。当连续向同一个 PR 推送多个提交时,系统会自动取消任何正在运行的旧任务,只保留由最新提交触发的审查流程。这种机制有效避免了冗余执行,并节省了 API 调用成本。
直接调用 CLI
当你需要实现更复杂的逻辑时,例如根据审查结果决定是否阻止 PR 合并、将审查报告发布为正式的 PR 评论而不是普通评论,或针对不同类型的文件采用差异化审查策略,直接调用 Claude CLI 将提供更大的灵活性。
# .github/workflows/advanced-review.yml
name: Advanced AI Review
on:
pull_request:
types: [opened, synchronize]
paths:
- 'src/**' # Trigger only when something under src/ changes
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install Claude Code
run: npm install -g @anthropic-ai/claude-code
- name: Get changed files
id: changed
run: |
FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
echo "files=$(echo "$FILES" | tr '\n' ' ')" >> $GITHUB_OUTPUT
echo "count=$(echo "$FILES" | wc -l)" >> $GITHUB_OUTPUT
- name: Run Claude Review
id: review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
CHANGED_FILES: ${{ steps.changed.outputs.files }}
run: |
claude -p "Review the following changed files: $CHANGED_FILES
Focus on security vulnerabilities, logic errors,
and performance problems.
Give the file name and the line number for every issue.
End your reply with exactly one verdict line:
<VERDICT>request_changes</VERDICT> or
<VERDICT>approved</VERDICT>" \
--output-format json \
--max-turns 10 \
--max-budget-usd 0.50 \
--model claude-sonnet-4-6 \
--allowedTools "Read,Grep,Glob" > review.json
# Write the review to a file; do not interpolate it
# into a shell or JS string
jq -r '.result' review.json > review.md
echo "cost=$(jq -r '.total_cost_usd' review.json)" >>
$GITHUB_OUTPUT
echo "is_error=$(jq -r '.is_error' review.json)" >>
$GITHUB_OUTPUT
- name: Post Review Comment
uses: actions/github-script@v7
env:
COST: ${{ steps.review.outputs.cost }}
with:
script: |
const fs = require('fs');
const result = fs.readFileSync('review.md', 'utf8');
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## Claude Code Review\n\n${result}\n\n---\n*Cost:
$${process.env.COST} | Automated by Claude Code*`
});
- name: Gate on critical issues
run: |
# Match an explicit sentinel, never a bare grep for "critical" —
# the word appears in almost every review ("no critical issues",
#"non-critical").
if grep -qF "<VERDICT>request_changes</VERDICT>" review.md; then
echo "::error::Claude requested changes"
exit 1
fi
尽管上述配置比官方 Action 更为复杂,但它为用户提供了三项关键能力:成本透明化(每次审查的具体费用直接显示在评论中)、质量门禁(当检测到 Critical 级别的严重问题时,Workflow 会自动失败并阻止 PR 合并)以及路径过滤(通过 paths 参数,仅在 src/ 目录发生变更时才触发审查)。
此外,还需要关注 CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC 环境变量。这是 CI/CD 流水线中的标准最佳实践,用于在自动化场景中一次性禁用非必要功能,例如自动更新检查、错误上报以及遥测数据传输。
实战示例 2:多阶段 CI 流水线
实战示例 1中介绍的是一个单阶段审查的案例。在实际项目中,你可能需要 Claude 参与多个阶段,例如先审查代码质量,再扫描安全漏洞,最后检查测试覆盖率。每个阶段往往有不同的关注重点、工具权限限制和成本预算。
以下是一个多阶段 AI 流水线的示例配置。
name: Multi-Stage AI Pipeline
on:
pull_request:
types: [opened, synchronize]
jobs:
# Stage 1: Quick Format Check (using a low-cost model)
lint-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Format Check
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
run: |
npx @anthropic-ai/claude-code -p "Check if the code under src/ complies with project coding standards" \
--model claude-haiku-4-5 \
--max-turns 3 \
--max-budget-usd 0.05 \
--allowedTools "Read,Grep,Glob" \
--output-format text
# Stage 2: In-Depth Security Review (using a high-performance model)
security-scan:
runs-on: ubuntu-latest
needs: lint-check # Only runs after format check passes
steps:
- uses: actions/checkout@v4
- name: Security Scan
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
run: |
npx @anthropic-ai/claude-code -p "Conduct an in-depth security scan, focusing on OWASP Top 10" \
--model claude-sonnet-4-6 \
--append-system-prompt "You are an application security expert. Please focus only on security vulnerabilities." \
--max-turns 10 \
--max-budget-usd 1.00 \
--allowedTools "Read,Grep,Glob" \
--output-format json \
--json-schema '{"type":"object","properties":{"vulnerabilities":{"type":"array","items":{"type":"object","properties":{"severity":{"type":"string"},"description":{"type":"string"},"file":{"type":"string"}},"required":["severity","description"]}}},"required":["vulnerabilities"]}' \
> security-report.json
# Stage 3: Test Coverage Analysis
coverage-check:
runs-on: ubuntu-latest
needs: lint-check # Depends on format check, but runs in parallel with security check
steps:
- uses: actions/checkout@v4
- name: Coverage Analysis
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
run: |
npx @anthropic-ai/claude-code -p "Analyze which code paths lack test coverage" \
--model claude-sonnet-4-6 \
--max-turns 8 \
--max-budget-usd 0.50 \
--allowedTools "Read,Grep,Glob" \
--output-format text
上述配置涉及三个关键设计要点。
-
模型分层:将模型与工作相匹配。机械且定义明确的检查(格式化、lint 分类)无法从更深入的推理中获益,因此更小、更便宜的模型(如 Haiku)就足够了;而需要处理大量上下文并进行推理的工作——安全审查、覆盖率分析——则是更大模型(如 Sonnet)能够体现其成本价值的地方。具体的模型名称会变化,但方法不会变:在两个层级上对十个真实 PR 运行同样的任务,将发现结果与人工审查的基线进行对比,让捕获率的差异来决定更大的模型是否值得其数倍的成本。
-
预算分层:为每次运行设置
‑‑max‑budget‑usd上限。合适的上限并不是一个可以从书上照抄的数字——它取决于你的提示词、你的模型,以及你的仓库迫使智能体读取多少上下文。先建立基线:运行任务十次,从 JSON 输出中读取total_cost_usd,并将上限设置到观察到的最大值之上。这里使用的数字(简单检查 $0.05,深度检查 $1)是我们测量得到的数量级,并非建议值。 -
并行执行:
security‑scan和coverage‑check都设置了needs: lint‑check,意味着它们必须等待第一阶段完成后才能开始。由于这两个阶段之间没有依赖关系,GitHub Actions 会自动并行运行它们。
流式输出:实时监控长时间运行的任务
对于长时间运行的分析任务,stream‑json 格式允许用户实时观察 Claude 的执行进度,而不必等到整个任务完成后才能查看最终结果。参见以下代码示例。
claude -p "Analyze the architecture problems across the whole src/ directory" \
--output-format stream-json \
--allowedTools "Read,Grep,Glob" | while read -r line; do
TYPE=$(echo "$line" | jq -r '.type // ""')
case "$TYPE" in
"system")
# Initialization: report which model is running
echo "[INIT] Model: $(echo "$line" | jq -r '.model // "unknown"')"
;;
"assistant")
# Pull out the text Claude produced
TEXT=$(echo "$line" | jq -r '.message.content[0].text // ""')
[ -n "$TEXT" ] && echo "[CLAUDE] $TEXT"
;;
"result")
# Task finished: report the turn count and the cost
COST=$(echo "$line" | jq -r '.total_cost_usd // 0')
TURNS=$(echo "$line" | jq -r '.num_turns // 0')
echo "[DONE] Finished in $TURNS turns. Cost: \$$COST"
;;
esac
done
stream-json 定义了一套完整的事件协议。除了上一个示例中提到的 system(初始化)、assistant(助手响应)、user(用户输入)和 result(结果摘要)事件之外,还有两种其他特殊事件类型值得注意。
-
stream_event(需要--include-partial-messages参数):提供来自 Claude 的增量数据输出,可精细到每个 token 级别的文本片段。这适用于希望实现"打字机效果"的用户界面,但通常在 CI 自动化工作流中不需要启用。 -
system/compact_boundary:上下文压缩边界事件。当对话历史接近模型的上下文窗口限制时,Claude 会自动触发一种机制来压缩较早的内容以释放空间。在长时间运行的分析任务中,如果检测到该事件,说明当前任务已消耗了大量上下文资源,你应当意识到可能存在上下文丢失的风险。
会话管理:跨步骤保持上下文
在 Headless 模式下,系统默认不会持久化会话状态,每次调用都被视为一个全新的上下文。然而,在涉及多轮交互的复杂场景中,往往需要在多次调用之间保持上下文的连续性。参见以下代码示例。
# Step 1: Perform initial analysis
# Call Claude to analyze the code structure of src/ and capture the complete JSON output
RESULT=$(claude -p "Analyze module dependencies in src/" \
--output-format json \
--allowedTools "Read,Grep,Glob")
# Each stage returns a JSON envelope. The fields that matter downstream:
#
# {
# "type": "result",
# "subtype": "success",
# "session_id": "f3c1a9e2-...", <- pass this to --resume to continue the same session
# "result": "Found 3 issues in src/auth/ ...",
# "total_cost_usd": 0.0412,
# "num_turns": 6,
# "is_error": false
# }
#
# A later headless run does not inherit any of this implicitly. Either feed result
# into the next prompt, or pass session_id to --resume so the model keeps its context.
# Step 2: Extract session identifier
# Parse session_id from the returned result for subsequent session restoration
SESSION_ID=$(echo "$RESULT" | jq -r '.session_id')
# Step 3: Continue the conversation based on context
# Use the --resume parameter to load the previous session state and enable continuous reasoning
claude -p "Based on the previous analysis, identify circular dependency issues" \
--resume "$SESSION_ID" \
--output-format text
--resume 参数的核心价值在于允许 Claude 完整"继承"上一次对话的上下文状态,包括已读取的文件、已执行的分析步骤以及已得出的中间结论。与在新提示中手动重述上下文信息相比,这种方式不仅显著提升了交互效率,还确保了模型推理的准确性和连贯性。
除了基本的会话恢复之外,CLI 工具还提供了一系列高级参数来处理复杂的交互场景。参见以下代码示例。
# Auto-continue: resume the most recent session in the current directory
# No need to manually look up the session_id; the system automatically matches and updates it
claude -p "Continue the previous analysis" --continue
# Session branching: create a new branch based on an existing session
# Preserve the original session unchanged, and derive a new exploration path from the specified node
claude -p "What if we switch to a microservices architecture?" --resume "$SESSION_ID" --fork-session
# Disable persistence: enforce stateless mode
# Suitable for fully isolated CI/CD pipelines, ensuring no session history is left behind
claude -p "One-time analysis" --no-session-persistence
--fork-session 是一个非常实用的功能,其逻辑类似于 Git 等版本控制系统中的分支机制。它从指定会话的某个特定节点"分叉"出一个全新的会话实例。原会话(主干)保持不变、不受影响;新会话(分支)从分叉点开始独立演进。该功能非常适合"假设性分析"需求。例如,开发者在完成对同一代码库的基线分析后,可以创建多个分支来探索不同的重构方向。
CI 环境配置:生产级检查清单
到目前为止,所有内容都是关于让运行能够正常工作。接下来的内容则是关于让它能够经受住真实流水线的考验——这些环节容易被忽略,但一旦忽略代价高昂。除了 -p 和 --allowedTools 之外,生产部署还需要有意识地设置环境变量、界定权限范围,并让失败变得可见。
必需的环境变量
必需的环境变量如下:
# Authentication
ANTHROPIC_API_KEY=sk-ant-... # API key
# Feature disabling
# Option 1: Use an aggregate variable (recommended, can disable multiple non-essential features at once)
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 # Disable auto-updates, telemetry, and error reporting
# Option 2: Disable individually (for finer-grained control)
DISABLE_AUTOUPDATER=1 # Disable auto-updates (essential in CI environments)
DISABLE_TELEMETRY=1 # Disable telemetry
DISABLE_ERROR_REPORTING=1 # Disable error reporting
DISABLE_AUTOUPDATER 是 CI/CD 环境中的一个关键环境变量。如果未设置该变量,Claude Code 可能会在执行过程中尝试自动更新,这可能导致版本不一致或意外的服务中断。相比之下,CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC 是一个便捷的"总开关",可以一次性禁用所有非必要的后台流量(包括自动更新、遥测和错误报告),从而简化配置过程。
性能调优参数
性能调优参数如下:
# Timeout control
# Default timeout for Bash commands (in milliseconds)
BASH_DEFAULT_TIMEOUT_MS=120000 # Equivalent to 2 min
# Maximum allowed timeout for Bash commands (in milliseconds)
BASH_MAX_TIMEOUT_MS=600000 # Equivalent to 10 min
# Output control
CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000 # Maximum tokens per model response (default 32,000, upper limit 64,000)
MAX_MCP_OUTPUT_TOKENS=25000 # Maximum tokens per MCP tool response
# Context management
CLAUDE_CODE_AUTOCOMPACT_PCT_OVERRIDE=50 # Auto-compaction trigger threshold (range 1~100, represents context usage percentage; default 50%)
GitHub Actions 的完整配置模板
将上述各部分整合在一起:以下模板与之前的审查任务相同,但应用了本节中的环境、权限和成本控制。这是值得直接复制使用的版本。
- name: Run Claude Analysis
env:
# Authentication key (retrieved from repository Secrets)
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
# Performance and environment control
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1" # Disable non-essential background traffic
BASH_DEFAULT_TIMEOUT_MS: "300000" # Default Bash command timeout: 5 min
run: |
npx @anthropic-ai/claude-code -p "$PROMPT" \
--output-format json \
--max-turns 10 \
--max-budget-usd 1.00 \
--allowedTools "Read,Grep,Glob" \
--model claude-sonnet-4-6 \
--fallback-model claude-haiku-4-5 \
--append-system-prompt "Follow the review guidelines in the CLAUDE.md file at the project root."
CI 中的 MCP 服务器配置
如果 CI 任务需要利用 MCP 服务器能力(例如数据库查询、外部 API 调用等),您可以通过 --mcp-config 参数加载专用配置文件,并使用 --strict-mcp-config 确保环境隔离。请参阅以下代码示例。
npx @anthropic-ai/claude-code \
-p "Analyze optimization opportunities in the database schema" \
--mcp-config ./ci-mcp-config.json \
--strict-mcp-config \
--allowedTools "Read,Grep,Glob,mcp__database__query" \
--max-turns 10
--strict-mcp-config 参数启用严格模式,强制 Claude 仅使用 --mcp-config 指定的配置文件。它会忽略用户主目录或项目根目录中的任何其他 MCP 配置。这在 CI 环境中非常重要,因为它可以防止开发人员无意中加载残留的本地 MCP 配置。
跨平台 CI/CD 集成
尽管 GitHub Actions 有官方 Action 支持,但 Claude Code 的无头模式本质上与平台无关。只要运行时环境支持 Node.js,它就可以部署在任何 CI/CD 平台上。
GitLab CI/CD 配置
以下配置演示了如何在 GitLab Runner 中执行代码审查任务并以结构化格式输出结果。
# .gitlab-ci.yml
claude-review:
image: node:20
variables:
ANTHROPIC_API_KEY: $ANTHROPIC_API_KEY
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
script:
- npm install -g @anthropic-ai/claude-code
- |
claude -p "Review the code changes in this MR" \
--output-format json \
--max-turns 10 \
--max-budget-usd 0.50 \
--allowedTools "Read,Grep,Glob" > review.json
- cat review.json | jq -r '.result'
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
关于 GitLab 的特别说明:由于上述配置是通用 CI,Anthropic 和 GitLab 一直在深化集成:GitLab 现在提供官方 AI 功能(测试版),并带有专用变量如 AI_FLOW_INPUT、AI_FLOW_CONTEXT 和 AI_FLOW_EVENT,因此提示上下文不再需要手动组装,并且 gitlab-mcp-server 通过 MCP(mcp__gitlab 工具)向模型暴露 GitLab API。Anthropic 的 GitLab CI/CD 指南记录了当前设置——请查阅该指南而非本页,因为变量名称仍在变动。
Jenkins 流水线集成配置
在 Jenkins 中,可以使用声明式流水线轻松集成 Claude Code。以下配置演示了如何安全地管理凭据、执行无头审查并解析结构化输出。
pipeline {
agent any
environment {
ANTHROPIC_API_KEY = credentials('anthropic-api-key')
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = '1'
}
stages {
stage('AI Code Review') {
steps {
sh 'npm install -g @anthropic-ai/claude-code'
sh '''
claude -p "Review the most recent code changes, focusing on logical errors and code standards" \
--output-format json \
--max-turns 10 \
--max-budget-usd 0.50 \
--allowedTools "Read,Grep,Glob" > review.json
'''
script {
def review = readJSON file: 'review.json'
echo "Review result: ${review.result}"
echo "Cost: \$${review.total_cost_usd}"
}
}
}
}
}
本地自动化脚本
除了 CI/CD 平台之外,Claude Code 的无头模式也非常适合打包为本地脚本,使团队成员能够在提交代码前进行自检,或作为本地自动化工具链的一部分。
以下是 review.sh 脚本的增强版本,具备环境检查、动态文件过滤、结构化报告生成和错误处理能力。
#!/bin/bash
# review.sh , A Local Code Review Script for Team Sharing
set -e
TARGET=${1:-.}
# Pre-checks
[ -z "$ANTHROPIC_API_KEY" ] && echo "Error: ANTHROPIC_API_KEY not set" && exit 1
command -v claude &> /dev/null || { echo "Error: Claude Code not installed"; exit 1; }
echo "Starting review for: $TARGET"
# Build file list
if [ -d "$TARGET" ]; then
FILES=$(find "$TARGET" -type f \( -name "*.ts" -o -name "*.js" -o -name "*.py" \) | head -20)
else
FILES="$TARGET"
fi
# Run the review. Note: RESULT=$(claude -p ...) would hide a failure even under
# `set -e`, because command substitution masks the inner command's exit code.
if ! claude -p "Review the code quality and security issues in the following files:
$FILES
Output format: a Markdown review report. Give the file name and line number for every issue." \
--output-format text \
--max-turns 15 \
--max-budget-usd 0.50 \
--allowedTools Read,Grep,Glob > /tmp/review-out.txt 2> /tmp/review-err.txt; then
cat /tmp/review-err.txt >&2
exit 1
fi
RESULT=$(cat /tmp/review-out.txt)
# Save the report
REPORT="review-$(date +%Y%m%d-%H%M%S).md"
{
echo "# Code Review Report"
echo
echo "**Date**: $(date)"
echo "**Target**: $TARGET"
echo
echo "---"
echo
echo "$RESULT"
} > "$REPORT"
echo "Report saved: $REPORT"
安全最佳实践:从 RBAC 到容器隔离
“在 CI/CD 中运行 AI Agent 是一件必须认真对待的安全事项。”Kage 的语气变得更加严肃。
这不是危言耸听。一个配置不当的无头任务可能导致以下严重风险:
-
机密泄露:Agent 读取
.env、config.json或硬编码的凭据文件并将其打印出来。这不是假设性场景,也不需要恶意提示——一个被要求“检查配置”的审查 Agent 会很乐意照做。最终出现在 CI 日志中的内容看起来是这样的:$ claude -p "Review the configuration for problems" --allowedTools "Read,Grep" Reading .env ... The configuration has a few problems. First, the production database credentials are committed to the repository: DATABASE_URL=postgres://admin:S3cr3t!prod@db.internal:5432/orders STRIPE_SECRET_KEY=sk_live_51H8xQ2... These should be moved to a secret store. -
破坏性操作:执行恶意 shell 命令,破坏构建环境或覆盖代码。
-
成本失控:陷入无限循环或被诱导生成大量无意义内容,导致 API 调用费用过高
-
内容污染:在 PR/MR 评论中输出幻觉内容、不当言论,甚至注入攻击载荷
防御策略必须在以下四个层面构建纵深防御体系
最小权限原则
在实际场景中,可以采取以下措施。
# Code review tasks: read-only
--allowedTools "Read,Grep,Glob"
# Format fixing tasks: read and write
--allowedTools "Read,Grep,Glob,Edit,Write"
# Git operation tasks: fine-grained Bash whitelist
--allowedTools "Read,Grep,Glob,Bash(git diff *),Bash(git log *)"
绝不要授予 Claude 不受限制的 Bash 执行权限。即使在需要 shell 能力的场景中,也必须通过模式匹配严格限制可执行命令的范围。
机密管理
对于机密管理,以下对比了正确和错误的做法。
# Incorrect practice: hardcoding keys (the key will appear in plain text in the Actions run log)
env:
ANTHROPIC_API_KEY: "sk-ant-xxx"
# Correct practice: referencing repository secrets
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
安全防御体系
GitHub Actions 密钥掩码机制
GitHub Actions 会自动掩盖日志中引用的密钥值(显示为 ***)。
尽管 GitHub 拥有日志掩码机制,但如果你在提示词中指示模型 "list all environment variables" 或 "print configuration details",模型可能会在其输出文本中重复密钥的具体值。此类模型生成的文本无法被自动掩码机制捕获,从而导致密钥泄露。
切勿在提示词中指示模型访问、读取或输出任何环境变量或密钥值。
容器隔离
以下展示了一个 GitHub Actions 工作流片段,用于构建一个高安全性的 "沙箱" 环境以运行代码审查任务。
jobs:
review:
runs-on: ubuntu-latest
container:
image: node:20
options: --read-only --tmpfs /tmp --network none
-
--read-only:将文件系统设置为只读以防止写操作(配合--tmpfs /tmp为临时文件提供可写空间)。 -
--network none:完全切断网络连接,适用于不需要外部访问的审查任务。在此模式下,Claude 只能读取仓库内的文件,无法与外界通信。
成本保护
以下展示了 GitHub Actions 中用于优化资源使用和降低运营成本的工作流配置片段。
# 路径过滤:仅当 src 目录下的文件变更时触发
on:
pull_request:
paths:
- 'src/**'
- '!src/**/*.test.*' # 排除测试文件
# 并发限制:仅保留同一 PR 的最新运行,自动取消中间版本
concurrency:
group: claude-${{ github.event.pull_request.number }}
cancel-in-progress: true
路径过滤和并发控制是成本优化的第一道防线。与限制单次运行的资源消耗相比,从源头阻止不必要的触发是降低总成本更有效的方式。
审计日志
以下展示了执行 AI 任务并持久化保存包含完整审计信息的运行日志的配置片段。
# JSON 输出格式天然包含完整审计信息
claude -p "task" --output-format json | tee -a /var/log/claude-audit.jsonl
每次无头模式调用的 JSON 输出自动包含 session_id、total_cost_usd、num_turns 和 usage 等关键审计字段。在有严格合规要求的环境中,建议实时将这些日志推送到中心化日志系统(如 ELK、Datadog 等)进行长期归档和深度分析。
Kage's notes
这五层防御不是 "选修题",而是生产环境中的 "必修题":最小权限原则防止 Claude "做不该做的事",密钥管理严防凭证泄露,容器隔离阻断逃逸风险,成本保护避免账单失控,审计日志保障全链路可追溯。缺失任何一层都意味着安全防线上的致命缺口。
从 CLI 到 Agent SDK:无头模式的编程接口
前面的示例均通过 CLI 调用 Claude Code,但在实际工程实践中,Claude Code 还提供了 Agent SDK(支持 TypeScript 和 Python),允许开发者直接使用编程语言控制无头执行流程,而非依赖命令行参数。
以下是 TypeScript Agent SDK 和 Python Agent SDK 的示例。
以下代码演示了如何使用 Agent SDK 构建一个进程内 MCP 服务器,将两个特定业务功能(数据库查询和发送通知)封装为安全工具供 AI Agent 调用。
// TypeScript Agent SDK Example
import { query } from "@anthropic-ai/claude-agent-sdk";
async function reviewPR(changedFiles: string[]) {
let sessionId: string | undefined;
let result: string = "";
for await (const message of query({
prompt: `Review the code changes in the following files:
${changedFiles.join(", ")}`,
options: {
allowedTools: ["Read", "Grep", "Glob"],
maxTurns: 10,
maxBudgetUsd: 0.50,
model: "claude-sonnet-4-6",
outputFormat: { type: "json_schema", schema: reviewSchema },
},
})) {
if (message.type === "system" && message.subtype === "init") {
sessionId = message.session_id;
}
if (message.type === "result" && message.structured_output) {
result = message.structured_output;
}
}
return { sessionId, result };
}
# Python Agent SDK Example
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def review_pr(changed_files: list[str]):
options = ClaudeAgentOptions(
allowed_tools=["Read", "Grep", "Glob"],
max_turns=10,
max_budget_usd=0.50,
model="claude-sonnet-4-6",
)
async for message in query(
prompt=f"Review the following files: {', '.join(changed_files)}",
options=options,
):
if isinstance(message, ResultMessage):
return message.result
CLI 和 Agent SDK 的关系类似于 curl 与 HTTP 客户端库的区别:CLI 以轻量便捷著称,适用于简单自动化脚本和 CI/CD 流水线;而 Agent SDK 专为复杂逻辑场景设计。无论是根据上一轮评审结果动态调整后续提示词、跨越多次 Claude 调用编排复杂工作流,还是将 Claude 深度集成到大型应用系统中,Agent SDK 都能提供所需的编程灵活性。在 Chapter 8 中,我们将深入探讨 Agent SDK 的具体用法。
渐进式实施策略
"After all these technical details," Xiaobing asked, "if our team wants to introduce Headless mode for PR reviews, how should we proceed step by step?"
Kage 建议了四阶段渐进式实施路径。
Phase One: Observer Mode(观察者模式)
Claude 仅执行只读分析,输出评审报告但不阻塞任何流程。评审报告以评论形式发布在 PR 上,供团队成员参考或忽略。该阶段核心目标是建立信任,让团队直观体验 Claude 评审质量,同时识别其局限性(如易误报场景),并据此迭代优化提示词与参数配置。
Phase Two: Advisor Mode(顾问模式)
积累足够正向反馈后,将 Claude 评审结果纳入 CI 状态监控。检测到关键问题时,CI 状态标记为 "warning"(黄色),但不阻塞合并。人工评审者需结合 Claude 报告做最终裁决。该阶段核心目标是人机协作。
Phase Three: Gatekeeper Mode(守门人模式)
Claude 评审成为代码合并的必要条件。一旦检测到关键安全问题,CI 状态即刻转红并阻断合并。但此阶段必须提供 "escape hatch"(逃生通道),允许特定权限人员手动覆盖 Claude 的阻断决策(类似跳过不稳定测试用例)。
Phase Four: Proactive Repair Mode(主动修复模式)
Claude 不仅发现问题,还能自动修复格式错误、补充缺失异常处理逻辑、生成单元测试。该阶段需开启 Write tool permissions,但所有修复必须作为新提交提交,严禁直接修改 PR author's original code(PR 作者原始代码)。
分阶段推进的原因非关耐心箴言,而是每个阶段都会产出下一阶段所需的证据。在拥有数周评审输出前,你无法区分真实发现与误报,也无依据决定 Agent 应被允许阻断什么。请让每个阶段运行足够久以积累证据——通常需数周——并让 dismissal rate(驳回率)而非日历决定何时推进。
Summary
Headless mode(无头模式) 推动了 Claude Code 角色的转型:从仅限终端交互的智能助手,进化为可无缝嵌入各类工程工作流的可编程组件。这一转型由四个维度的参数定义。
-
‑p(Entry Point):将 Claude Code 运行模式从交互式会话切换为非交互式执行,奠定自动化基础 -
‑‑output‑format(Data Interface):标准化输出结构,保障下游系统能高效解析、消费评审结果 -
‑‑allowedTools(Security Boundary):严格限制工具权限,确保自动化环境下的操作合规可控 -
‑‑max‑turns与‑‑max‑budget‑usd(Cost Guardrails):设定执行轮次与预算上限,防止无人值守场景下的资源失控
在工程架构层面,Headless mode 最大价值在于赋予 Claude Code "first-class citizen in the CI/CD ecosystem"(CI/CD 生态系统中的一等公民) 地位。正如单元测试、静态分析、容器镜像构建,AI 代码评审正式成为流水线标准步骤:它需要明确的输入定义、确定性的输出格式、可量化的成本预算、以及可纳入版本控制的配置文件。
实施策略上,建议从 "Observer Mode" 起步,逐步演进至 "Gatekeeper Mode"。这一渐进过程旨在给团队留出充足时间建立信任、积累实战数据、优化配置。切记勿急躁:AI 辅助评审的核心价值不在于追求 "perfect replacement of humans(完美替代人类)",而在于以智能筛选让人工评审者将宝贵精力聚焦于最关键的决策环节。
在 Chapter 8 中,我们将从命令行参数迈入编程接口(Agent SDK)的世界。你将学习如何用 TypeScript 或 Python 代码替代繁琐的 CLI parameters,灵活控制 Claude 行为逻辑,从而构建真正定制化、高度集成的 AI 应用系统。
Discussion Questions
-
Your team wants to add Claude Code PR review to a pipeline that already has required status checks. Should the review be advisory or blocking, and how would you decide when to promote it? What should Claude own that a linter cannot, and what do you do about false positives?
-
Under what circumstances would
‑‑max‑turns 5and‑‑max‑budget‑usd 0.50be triggered first? When designing Headless tasks for production environments, which metric should you prioritize? Please explain your reasoning. -
Take the CI you already run — and if you have not chosen one yet, take the one your code is already hosted next to; a review job is not worth a platform migration. Write the workflow file: the trigger, the permissions, the tool whitelist, and the two ceilings (
‑‑max‑turnsand‑‑max‑budget‑usd). Then say which of those two you expect to trip first, and why.
第七章:以 Claude 为引擎构建:Agent SDK 智能体开发工具包
"Give a man a fish and you feed him for a day; teach a man to fish and you feed him for a lifetime." — Proverb
自小雪将 Claude Code 接入公司 CI/CD 流水线、实现 PR 自动评审与报告生成后,团队反响热烈。产品经理据此提出更前瞻性构想:
"Can we develop an internal tool that allows non-technical colleagues to use Claude Code to analyze code? For example, a product manager only needs to enter a user story, and the system can automatically locate the relevant code modules, assess implementation complexity, and list the files to be modified."
小冰沉吟片刻。Headless mode 虽能部分满足需求,但本质上是命令行工具,对不熟悉终端操作的产品经理门槛过高。他们真正需要的是带表单输入、进度反馈、历史记录的 Web 界面。这意味着不再是简单的 "using a tool(使用工具)",而是 "building a product(构建产品)"。
"Headless mode drives Claude through parameters, while the Agent SDK orchestrates Claude through code," Kage 补充道。"The difference is like 'using a calculator' versus 'programming a calculator': the former relies only on preset function buttons, while the latter allows you to customize its behavior logic."
Positioning of the Agent SDK: From Tool to Component(Agent SDK 定位:从工具到组件)
前文介绍的机制(含 CLAUDE.md、sub-agents、Skills、Hooks、MCP、Headless mode)均聚焦 "using Claude Code"。该范式下,开发者作为用户,在既定框架内通过配置拓展 Claude Code 行为边界。
Agent SDK 则基于完全不同前提出发。它假定开发者是 application builder(应用构建者),旨在将 Claude Code 核心能力深度嵌入自有应用。何时启动 Claude、传递何参数、如何编排中间执行步骤、如何管理多轮对话状态——这一切完全由开发者代码掌控。此时你不再是 "using" 现成产品,而是以 Claude 为智能引擎 "building" 全新应用。
这种演进路径如 Figure 8.1 所示,清晰展示了控制粒度从 "configuration(配置)" 向 "code(代码)" 的转移。
Figure 8.1: Evolution of control granularity from CLAUDE.md to Agent SDK
每深入一层,控制粒度愈细,定制化程度随之提升。Agent SDK 处于演进链末端:在赋予开发者最大灵活性的同时,也带来最高工程复杂度。
Agent SDK 提供 Python 与 TypeScript/JavaScript 两个版本,功能完全等价。值得注意的是,截至 2025 年底,其包名已正式从 claude‑code‑sdk 变更为 claude‑agent‑sdk,以更精准反映其定位。安装说明如下。
Python
pip install claude-agent-sdk
TypeScript/Node.js
npm install @anthropic-ai/claude-agent-sdk
核心 API:query 函数
Agent SDK 的入口点是 query 函数。如果无头模式的入口点是 claude -p "prompt",那么 query 函数就是其编程等价物——不过,它不返回最终结果,而是返回一个异步消息流。
SDK 实际上提供了两个入口点,值得弄清你要用哪一个。query() 是无状态的:每次调用都会开启一个新会话,这正是一次性任务以及本章所有示例所需要的。ClaudeSDKClient 是有状态的:它在多轮对话中保持会话,因此适用于构建需要记住三轮前所说内容的交互式进程内助手。两者都支持自定义工具和钩子;区别在于会话生命周期,而非能力。此处的示例均使用 query(),因为每个示例都是自包含的。
Python 版本
以下代码演示如何使用 Agent SDK 通过 Python 以编程方式调用 AI 代理,以执行分析代码库架构的任务。
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
async def analyze_code():
options = ClaudeAgentOptions(
max_turns=5,
allowed_tools=["Read", "Grep", "Glob"],
system_prompt="You are a code architecture analyst",
)
async for message in query(
prompt="Analyze the implementation architecture of the src/auth/ directory",
options=options
):
if isinstance(message, AssistantMessage):
for block in message.content:
if hasattr(block, 'text'):
print(block.text, end="", flush=True)
elif isinstance(message, ResultMessage):
print(f"\n\nCompleted. Cost: ${message.total_cost_usd:.4f}")
asyncio.run(analyze_code())
TypeScript 版本
以下代码演示如何使用 Agent SDK 通过 TypeScript 以编程方式调用 AI 代理,以执行分析代码库架构的任务。
import { query, ClaudeAgentOptions } from '@anthropic-ai/claude-agent-sdk';
const options: ClaudeAgentOptions = {
maxTurns: 5,
allowedTools: ['Read', 'Grep', 'Glob'],
systemPrompt: 'You are a code architecture analyst.',
};
async function analyzeCode() {
for await (const message of query({
prompt: 'Analyze the implementation architecture of the src/auth/ directory',
options,
})) {
if (message.type === 'assistant') {
for (const block of message.content) {
if ('text' in block) {
process.stdout.write(block.text);
}
}
} else if (message.type === 'result') {
console.log(`\n\nCompleted. Cost: $${message.totalCostUsd}`);
}
}
}
analyzeCode();
query 函数是一个异步生成器。它不需要等待 Claude 完成整个任务才返回;相反,它会在 Claude 生成每条消息时实时产出。在 Web 应用中,这意味着你可以将 Claude 的思考过程流式传输到前端,重现类似 Claude.ai 的打字机效果。在 CI/CD 场景中,它允许你在 Claude 仍在运行时就开始处理生成的输出。
消息类型:解读 Claude 的输出流
“理解消息类型是使用 Agent SDK 的基础,”Kage 强调道。“正如使用 HTTP API 的开发者必须掌握状态码一样,Agent SDK 开发者需要彻底掌握每种消息的含义。”
query 函数生成的消息流是一个有序序列,其中每条消息通过 type 字段标识其确切类型。
system/init — 会话初始化
以下代码展示了一个用于会话初始化的系统消息示例。
{
"type": "system",
"subtype": "init",
"session_id": "550e8400-...",
"model": "claude-sonnet-4-6",
"tools": ["Read", "Grep", "Glob"],
"mcp_servers": []
}
消息流中的第一条消息报告当前会话的配置详情,包括模型版本、可用工具集和 MCP 服务器状态。这在调试时尤为关键:如果你发现 Claude 没有按预期调用某个特定工具,首先应检查该工具是否定义在 init 消息中。
assistant — Claude 的响应
以下代码展示了 Claude 每轮响应的消息示例。
{
"type": "assistant",
"message": {
"role": "assistant",
"content": [
{"type": "text", "text": "Let me first take a look at the directory structure..."},
{"type": "tool_use", "id": "toolu_xxx", "name": "Glob", "input": {"pattern": "src/auth/**/*"}}
]
}
}
在上述代码中,content 字段是一个数组,可包含文本块(即 Claude 的“思考”过程或最终输出)和工具调用块(即 Claude 决定执行的动作)的混合内容。值得注意的是,单条 assistant 消息可能包含多个工具调用,这意味着 Claude 可以并发请求读取多个文件或执行其他并行操作。
user — 工具执行结果
以下代码展示了工具执行完成后的结果示例。
{
"type": "user",
"message": {
"role": "user",
"content": [
{"type": "tool_result", "tool_use_id": "toolu_xxx", "content": "src/auth/\n├── login.ts\n├── session.ts\n└── middleware.ts"}
]
}
}
请注意,tool_use_id 必须与 assistant 消息中的工具调用 ID 严格对应。这种映射关系由 Agent SDK 自动维护,开发者通常无需手动处理。
result — 任务完成
以下代码展示了任务完成时的终止消息示例。
{
"type": "result",
"subtype": "success",
"session_id": "550e8400-...",
"num_turns": 5,
"total_cost_usd": 0.0342,
"duration_ms": 12345,
"duration_api_ms": 10000,
"usage": {
"input_tokens": 5000,
"output_tokens": 1500,
"cache_read_input_tokens": 3000
},
"result": "Architecture analysis: src/auth/ adopts a layered design...",
"structured_output": null
}
在上述代码中,subtype 是判断任务完成状态的关键字段(详见表 8.1)。
| subtype | 含义 |
| :--- | :--- |
| success | 任务正常完成 |
| error_max_turns | 达到最大轮次限制 (--max-turns) |
| error_max_budget_usd | 达到成本限制 |
| error_during_execution | 执行过程中发生错误 |
| error_max_structured_output_retries | 结构化输出验证失败(超过最大重试次数) |
表 8.1:result 消息中的 subtype 类型及含义
以下代码演示了一个实用的消息处理模式,用于在异步消息流中收集文本输出、记录工具调用详情并存储会话元数据。
async def process_query(prompt: str, options: ClaudeAgentOptions) -> dict:
# Initialize result container
result = {"text": [], "tools": [], "metadata": {}, "error": None}
# Asynchronously iterate over the message stream
async for message in query(prompt=prompt, options=options):
if isinstance(message, AssistantMessage):
# Process content blocks in the assistant's response
for block in message.content:
if hasattr(block, 'text'):
# Collect text output (thoughts or answers)
result["text"].append(block.text)
elif hasattr(block, "name"):
记录工具调用(名称和输入参数)
result["tools"].append({
"tool": block.name,
"input": block.input,
})
elif isinstance(message, ResultMessage):
从终止消息中提取元数据
result["metadata"] = {
"session_id": message.session_id,
"cost": message.total_cost_usd,
"turns": message.num_turns,
"duration_ms": message.duration_ms,
}
检查终止是否由于错误
if message.is_error:
result["error"] = message.subtype
return result
# ClaudeAgentOptions: 细粒度行为控制
`ClaudeAgentOptions` 是控制 `ClaudeAgent` 行为的核心配置对象(此类名已从上一版本的 `ClaudeCodeOptions` 更新)。它将 Headless CLI 的命令行参数转换为编程接口,实现动态配置和细粒度控制。
以下代码演示了该配置对象的结构化解析。
```python
from claude_agent_sdk import ClaudeAgentOptions
options = ClaudeAgentOptions(
# === 模型与执行控制 ===
model="claude-sonnet-4-6", # 指定使用的模型版本
max_turns=10, # 限制最大交互轮数,防止无限循环
max_budget_usd=1.0, # 软性上限:在轮次间检查,
# 因此进行中的一轮仍会完成
# === 工具权限管理 ===
# 白名单:仅允许使用列出的工具
allowed_tools=["Read", "Grep", "Glob", "Write"],
# 黑名单:显式禁止使用的工具(通常
# 优先于白名单或作为补充)
disallowed_tools=["Bash"],
# === 权限模式 ===
# default: 标准模式,危险操作需用户确认
# acceptEdits: 自动接受文件编辑
# plan: 仅规划,不执行
# bypassPermissions: 跳过所有确认(高风险,
# 仅适用于可信环境)
permission_mode="default",
# === 提示工程 ===
system_prompt={"type": "preset", "preset": "claude_code", "append":
"Be sure to check for SQL injection vulnerabilities"},
# Claude Code 预设 + 追加指令
# === 工作环境配置 ===
cwd="/path/to/project", # 设置 Agent 的工作目录
env={"PROJECT_NAME": "MyApp"}, # 注入自定义环境
# 变量
# === 会话管理 ===
resume="session-id-to-resume", # 从指定 session_id 恢复上下文
# === 结构化输出 ===
# 强制模型输出符合特定 JSON Schema 格式的数据
output_format={"type": "json_schema", "schema": my_schema}, #
# 结构化输出
# === MCP 集成 ===
# 动态启动并连接到外部 MCP 服务器
mcp_servers={
"db": {"command": "python", "args": ["./db_server.py"]},
},
)
权限模式详解
权限模式是 Agent SDK 应用中的核心安全防线。它决定了 Claude 在遇到潜在风险操作(如写入文件或执行命令)时的行为逻辑(见表 8.2)。
| 模式 | 行为逻辑 | 适用场景 |
|---|---|---|
default |
标准交互:遇到危险操作(如写入文件或运行 Shell)时,等待外部确认(通常通过回调或人工干预) | 面向用户的 Web 应用,交互式 CLI 工具 |
acceptEdits |
半自动:自动接受文件编辑操作,但仍会对命令执行 (Bash)、网络请求等进行提示 |
自动化修复脚本,代码格式化流水线 |
plan |
只读/规划模式:禁止所有修改。模型只能读取文件、分析代码并输出计划;无法执行写入操作或命令。 | 代码审查,架构分析,漏洞扫描 |
bypassPermissions |
完全信任:跳过所有权限检查,自动执行所有操作(包括 rm -rf、curl 等高风险命令) |
严格隔离的 CI 容器,沙箱环境 |
表 8.2:Agent SDK 权限模式及适用场景
"在 CI/CD 流水线中," Kage 说,"
bypassPermissions看似很方便——毕竟它不需要人工确认,但请务必记住,这意味着授予Claude执行任何命令的权限,包括删除文件、修改配置,甚至发起网络请求。因此,仅当您确定执行环境完全隔离时(如容器化部署、只读文件系统、无网络连接),才启用此选项。*"
工具权限的模式匹配
allowed_tools 支持与 Headless CLI 相同的模式匹配语法。请参见以下代码示例。
options = ClaudeAgentOptions(
allowed_tools=[
"Read",
"Grep",
"Glob",
"Bash(git diff *)", # 仅允许执行 git
# diff 命令
"Bash(npm test *)", # 仅允许执行
# npm test 命令
"mcp__database__query", # 仅允许使用
# MCP 数据库查询工具
]
)
会话管理:跨调用的上下文连续性
query 函数适用于单任务场景。然而,许多实际应用需要多轮交互:用户首先指示 Claude 分析问题,然后在收到结果后指定修复方向,最后在修复完成后要求 Claude 进行验证。
基于 session_id 的会话连续性
以下代码演示了如何使用支持会话连续性的异步 AI Agent 接口,分两步完成一个复杂的安全分析任务。
# 第一次调用:分析问题
session_id = None
async for message in query(prompt="Analyze the security issues
in src/auth", options=options):
if isinstance(message, SystemMessage) and message.subtype == "init":
session_id = message.session_id # [关键] 获取会话 ID
if isinstance(message, ResultMessage):
print(message.result)
# 第二次调用:基于上一轮上下文深入探究
resume_options = ClaudeAgentOptions(**options.__dict__, resume=session_id)
async for message in query(
prompt="Focus on analyzing the first SQL injection risk
you identified",
options=resume_options
):
if isinstance(message, ResultMessage):
print(message.result)
resume 参数使 Claude 能够“记住”上一轮对话的完整上下文(包括已读取的文件、执行的分析步骤和得出的结论)。与在第二个提示词中重复描述上下文相比,这种方式不仅更高效,还能确保分析的准确性和连贯性。
对话分叉
以下代码演示了如何使用对话分叉技术,基于相同的初始分析结果探索多个不同的解决方案或假设场景,同时确保每个分支保持独立。
# 基于相同的分析结果探索不同方向
# 创建分叉配置:复用原始会话状态,
但要开始一个新的分支
options_fork = ClaudeAgentOptions(**options.__dict__, resume=session_id, fork_session=True)
方向 A:探索微服务重构方案
async for message in query(
prompt="如果重构为微服务架构,需要修改哪些组件?",
options=options_fork
):
...
方向 B:探索现有架构的安全加固方案
基于相同的 session_id 再次分叉,以确保两个方向保持完全独立
async for message in query(
prompt="如果维持现有架构,应如何加强安全性?",
options=options_fork
)
设置 fork_session=True 将从指定会话的特定点位“分叉”出一个全新的会话副本,而原会话的状态保持不变。这种机制在需要进行“假设性分析”或对比多个技术方案的场景中极具价值。
自定义工具:扩展 Claude 的能力边界
Claude 原生自带 Read、Write、Bash 和 Grep 等基础工具。然而,在实际应用场景中,开发者往往需要集成领域特定的能力,例如查询数据库、调用内部 API、发送通知或执行复杂的业务逻辑。
Agent SDK 中的自定义工具本质上是进程内 MCP 服务器。与 第 6 章 中介绍的独立部署的 MCP 服务器不同,这些工具不需要启动单独的进程,而是直接在应用程序进程中运行。这种架构消除了进程间通信的开销,从而显著提高了工具调用的效率和响应速度。
使用 @tool 装饰器定义工具
以下代码演示了如何使用 Agent SDK 构建一个进程内 MCP 服务器,将两个具体的业务功能(数据库查询和发送通知)封装为供 AI Agent 调用的安全工具。
from claude_agent_sdk import tool, create_sdk_mcp_server
import json
# 工具 1:数据库查询(只读)
@tool(
name="query_database",
description="在应用数据库上执行只读 SQL 查询",
input_schema={
"query": str,
"limit": int
}
)
async def query_database(args):
sql = args["query"]
limit = args.get("limit", 100)
# [安全关键] 强制验证:仅允许 SELECT
if not sql.strip().upper().startswith("SELECT"):
return {
"content": [{"type": "text", "text": "Error: Only SELECT queries allowed"}],
"isError": True
}
results = await db.execute(f"{sql} LIMIT {limit}")
return {
"content": [{"type": "text", "text": json.dumps(results, indent=2)}]
}
# 工具 2:发送通知
@tool(
name="send_notification",
description="向团队 Slack 频道发送通知",
input_schema={"channel": str, "message": str}
)
async def send_notification(args):
await slack.post_message(args["channel"], args["message"])
return {
"content": [{"type": "text", "text": f"Notification sent to #{args['channel']}"}]
}
# 创建一个 MCP 服务器来托管这些工具
tools_server = create_sdk_mcp_server(
name="app-tools",
version="1.0.0",
tools=[query_database, send_notification]
)
在 Agent 配置中注册并启用自定义工具
以下代码演示了如何在 Agent 配置中注册并启用自定义工具。
options = ClaudeAgentOptions(
mcp_servers={
"app-tools": tools_server
},
allowed_tools=[
"Read", "Grep", "Glob", # 内置工具
"mcp__app-tools__query_database", # 自定义工具
"mcp__app-tools__send_notification",
]
)
自定义工具的名称必须严格遵循 mcp__{server_name}__{tool_name} 格式。这种带前缀的命名约定不仅避免了与内置工具(如 Read 和 Bash)的命名冲突,还为日志审计和监控仪表板提供了天然的分类标签。
使用 Pydantic 模型进行参数验证
对于超出简单字符串集合的场景,请将参数定义为 Pydantic 模型,而不是裸字典。其优势体现在字典无法表达的情况:嵌套对象、带默认值的可选字段、将值限定在固定集合内的枚举,以及数值范围。下面的模型涵盖了这四种情况 —— 其中 where 默认为 None,limit 通过 ge 和 le 进行边界约束 —— 每一项约束都会成为 Claude 所见 schema 的一部分,因此格式错误的调用会在你的函数运行前被拒绝。
from pydantic import BaseModel, Field
from claude_agent_sdk import tool
# 定义结构化参数模型
class DatabaseQueryParams(BaseModel):
table: str = Field(
...,
description="表名"
)
columns: list[str] = Field(
default=["*"],
description="要选择的列"
)
where: str | None = Field(
default=None,
description="SQL WHERE 子句条件(可选)"
)
limit: int = Field(
default=100,
ge=1,
le=1000,
description="返回的最大行数"
)
# 直接将模型定义为工具参数
@tool(
name="safe_query",
description="执行安全的、参数化的数据库查询",
input_schema=DatabaseQueryParams
)
async def safe_query(args: DatabaseQueryParams):
# 参数已由 Pydantic 模型验证,确保类型安全
...
Kage 的备注
自定义工具的设计必须以极高的谨慎态度对待。Claude 可能会以意想不到的方式调用工具,例如传入极端的参数值、在短时间内连续发起数十次调用,或将一个工具的输出作为另一个工具的输入。因此,请遵循以下三项原则:单一职责(确保每个工具仅专注于特定任务)、清晰描述(description 字段充当给 Claude 的“使用指南”,必须明确界定“何时可以使用”以及“何时应禁用”)和 零信任验证(绝不信任 Claude 传入的任何参数;所有输入必须在工具内部经过严格验证)。
具体而言,需要权限控制而非仅靠良好描述的工具,是指其效果无法通过重复运行来撤销的工具:任何执行删除操作、写入生产环境、发送邮件或 Slack 消息,或涉及资金支出的工具。
Agent SDK 中的钩子:程序化拦截
第 5 章 中介绍的钩子机制依赖于 JSON 配置文件。在 SDK 中,你可以将相同的拦截点注册为代码内回调,这消除了此处展示的用例中配置文件的往返开销。在规划之前,有一个值得注意的注意事项:在 Python SDK 中,SessionStart 和 SessionEnd 不可作为代码内回调使用 —— 其 HookEvent 类型省略了它们,在 Python 中你只能通过在设置文件中声明的 Shell 命令钩子来获取它们。它们可以在 TypeScript 中注册为 SDK 回调。下面的示例所用的 PreToolUse 和 PostToolUse 在两者中均可用。
PreToolUse:执行前拦截
以下代码演示了一个细粒度的运行时安全拦截系统。该系统利用钩子机制,在 AI Agent 实际执行工具(如运行 Bash 命令或修改文件)之前,对操作进行深度检查和过滤。如果检测到任何危险行为,系统将立即拒绝执行,从而有效防止 AI 造成危害。
from claude_agent_sdk import ClaudeAgentOptions, HookMatcher
async def block_dangerous_bash(input_data, tool_use_id, context):
"""拦截危险的 Bash 命令"""
# 仅处理 Bash 工具调用
if input_data["tool_name"] != "Bash":
return {}
command = input_data["tool_input"].get("command", "")
# 定义危险命令特征列表
dangerous = ["rm -rf", "sudo", "chmod 777", "> /dev/",
"mkfs", "dd if="]
for pattern in dangerous:
if pattern in command:
return {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": f"Blocked: {pattern}"
}
}
return {}
async def protect_config_files(input_data, tool_use_id, context):
"""保护关键配置文件不被修改"""
tool_name = input_data["tool_name"]
# 仅处理文件写入工具
if tool_name not in ["Write", "Edit"]:
return {}
file_path = input_data["tool_input"].get("file_path", "")
# 定义受保护的路径或文件名特征
protected = [".env", "secrets", "production.yaml",
"database/migrations"]
for p in protected:
if p in file_path:
return {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": f"Protected file: {file_path}"
}
}
return {}
# 配置 Agent 选项并注册安全拦截钩子
options = ClaudeAgentOptions(
hooks={
"PreToolUse": [
HookMatcher(matcher="Bash", hooks=[block_dangerous_bash]),
HookMatcher(matcher="Write", hooks=[protect_config_files]),
HookMatcher(matcher="Edit", hooks=[protect_config_files]),
]
}
)
PostToolUse:执行后处理
以下代码演示如何使用 PostToolUse(工具使用后)钩子来实现自动化工作流增强和端到端审计。
import subprocess
import json
from datetime import datetime
from claude_agent_sdk import ClaudeAgentOptions, HookMatcher
async def auto_format_on_write(input_data, tool_use_id, context):
"""写入文件后自动触发代码格式化"""
# 仅处理文件写入工具
if input_data["tool_name"] not in ["Write", "Edit"]:
return {}
file_path = input_data["tool_input"].get("file_path", "")
# 根据文件扩展名选择相应的格式化工具
if file_path.endswith(".py"):
# 使用 Black 格式化 Python 代码
subprocess.run(["black", file_path], capture_output=True)
elif file_path.endswith((".ts", ".js", ".tsx", ".jsx")):
# 使用 Prettier 格式化前端代码
subprocess.run(["prettier", "--write", file_path],
capture_output=True)
return {}
async def audit_all_tools(input_data, tool_use_id, context):
"""记录所有工具调用的审计日志"""
audit_entry = {
"timestamp": datetime.now().isoformat(),
"tool": input_data["tool_name"],
"input": input_data["tool_input"],
"tool_use_id": tool_use_id,
}
# 将审计日志追加写入 JSONL 文件
with open("agent-audit.jsonl", "a") as f:
f.write(json.dumps(audit_entry) + "\n")
return {}
# 配置 Agent 选项并注册执行后钩子
options = ClaudeAgentOptions(
hooks={
"PreToolUse": [...], # 可在此连接前面定义的
# 安全拦截钩子
"PostToolUse": [
HookMatcher(matcher="Write", hooks=[auto_format_on_write]),
HookMatcher(matcher="Edit", hooks=[auto_format_on_write]),
HookMatcher(matcher="*", hooks=[audit_all_tools]),
]
}
)
Agent SDK Hooks 与 Shell Hooks 的关系
表 8.3 对比了 Agent SDK Hooks 与 Shell Hooks 的核心差异,清晰地展示了从可配置脚本到原生编程接口的演进。
| Hook 机制 | Shell Hooks | Agent SDK Hooks |
|---|---|---|
| 定义方式 | settings.json 配置 + Shell 脚本文件 |
原生 Python/TypeScript 函数 |
| 执行环境 | 独立子进程 | 当前进程内 |
| 数据交换 | 通过 stdin/stdout 传递 JSON 字符串 |
直接通过函数参数和返回值对象 |
| 异步支持 | 不支持(同步阻塞) | 原生支持 async/await |
| 调试体验 | 困难(依赖 echo 打印或写日志文件) | 高效(支持 IDE 断点、单步调试) |
| 适用场景 | Claude Code 交互模式/Headless CLI | 基于 Agent SDK 构建的嵌入式应用 |
表 8.3:Shell Hooks 与 Agent SDK Hooks 对比
如果您希望直接使用 Claude Code(无论是终端交互模式还是 Headless CLI 模式),建议使用 Shell Hooks;如果您正在开发自定义应用、集成服务或使用 Agent SDK 构建复杂的自动化工作流,建议使用 Agent SDK Hooks。
四层安全防御体系
Agent SDK 建立了由四层安全防御组成的纵深防御体系,如图 8.2 所示。
权限模式设定默认姿态,工具白名单限定可用工具范围,canUseTool 回调凭借完整的参数上下文充当逐次调用的守门人,而 PreToolUse Hooks 是执行前最后的确定性检查点;在典型应用中,白名单和权限模式承担了大部分防护重任。
图 8.2 Agent SDK 的纵深防御体系
运行时权限回调是 SDK 独有的机制。请注意拼写差异:Python 中参数名为 can_use_tool,TypeScript 中为 canUseTool。本章正文在泛指该机制时采用 TypeScript 形式,但 Python 代码示例中注册的是 can_use_tool——编写代码时请以代码为准,而非正文。
以下代码演示了如何构建一个智能“守门人”,在工具执行前根据具体内容动态决定是否授予访问权限。
async def can_use_tool(tool_name: str, tool_input: dict) -> dict:
"""运行时权限检查回调"""
# 场景 1:文件写入保护
# 防止敏感配置文件被篡改
if tool_name in ["Write", "Edit"]:
file_path = tool_input.get("file_path", "")
if ".env" in file_path or "secrets" in file_path:
return {
"allowed": False,

浙公网安备 33010602011771号