Documentation Index
Fetch the complete documentation index at: https://docs.qoder.com/llms.txt
Use this file to discover all available pages before exploring further.
工具是模型执行任务时可以调用的能力。Qoder Agent SDK 支持两类工具:
- 内置工具:由 Qoder CLI 提供,例如读文件、搜索、执行命令、调用子 Agent。
- 自定义工具:由 SDK 使用者通过
tool() 和 createSdkMcpServer() 自定义工具并暴露给模型调用。
本文重点讲如何自定义工具。内置工具完整列表见 Tools Reference - 内置工具列表。
内置工具
使用内置工具时,你不需要实现工具本身,只需要在 query() options 中控制本次会话能使用哪些工具、哪些工具预授权、哪些工具禁止。
query({
prompt: 'Read this repository and summarize risks in the authentication module. Do not modify files.',
options: {
auth: accessTokenFromEnv(),
cwd: '/path/to/project',
tools: ['Read', 'Grep', 'Glob'],
allowedTools: ['Read', 'Grep', 'Glob'],
},
});
常用内置工具包括 Read、Edit、Write、Bash、Glob、Grep、WebFetch、WebSearch、Agent 等。完整清单和名称以 Tools Reference - 内置工具列表 为准;输入输出结构见 内置工具输入输出类型,例如 FileReadInput / FileReadOutput、BashInput / BashOutput、AgentInput / AgentOutput。如果需要在 TypeScript 层统一表示工具输入或输出,参考 ToolInputSchemas 和 ToolOutputSchemas。
自定义工具
当你希望模型调用自己的业务能力时,可以自定义工具。例如查询订单、搜索内部知识库、调用审批系统、访问只读数据库等。
自定义工具通常分三步:
- 用
tool() 创建工具。
- 用
createSdkMcpServer() 把工具注册到一个 MCP server。
- 在
query({ options }) 中通过 mcpServers 接入,并用权限配置控制调用。
自定义工具接入步骤
先看一个完整的最小示例,然后按三步拆开说明每一步可以配置什么:
import {
accessTokenFromEnv,
createSdkMcpServer,
query,
tool,
} from '@qoder-ai/qoder-agent-sdk';
import { z } from 'zod';
const lookupOrder = tool(
'lookup_order',
'Look up an order by order ID.',
{
orderId: z.string().describe('Order ID, such as O-1001'),
},
async ({ orderId }) => {
const order = await orders.find(orderId);
if (!order) {
return {
isError: true,
content: [{ type: 'text', text: `Order not found: ${orderId}` }],
};
}
return {
content: [{ type: 'text', text: JSON.stringify(order) }],
};
},
{ annotations: { readOnlyHint: true } },
);
const orderTools = createSdkMcpServer({
name: 'orders',
tools: [lookupOrder],
});
const messages = query({
prompt: 'Check the status of order O-1001 and summarize it in one sentence.',
options: {
auth: accessTokenFromEnv(),
mcpServers: { orders: orderTools },
allowedTools: ['mcp__orders__lookup_order'],
},
});
for await (const message of messages) {
if (message.type === 'result') {
console.log(message.result);
}
}
这一步负责定义工具本身,包括工具名、描述、输入参数、执行逻辑和工具元信息。
tool() 用来定义一个工具。它有 5 个入参,完整类型见 tool()。
| 入参 | 类型 | 是否必填 | 语义 |
|---|
name | string | 是 | 工具在当前 MCP server 内的唯一标识 |
description | string | 是 | 给模型看的工具说明,描述工具何时使用、做什么、返回什么 |
inputSchema | Schema extends AnyZodRawShape | 是 | 定义工具输入参数的 Zod raw shape |
handler | Function | 是 | 接收解析后的参数并返回 CallToolResult 的异步执行函数 |
extras | ToolExtras | 否 | 工具额外元信息,目前用于传 annotations |
配置输入参数
inputSchema 传 Zod raw shape,也就是字段对象,不是 z.object(...)。类型参考 AnyZodRawShape;handler 参数推导参考 InferShape。
{
query: z.string().describe('Search keywords'),
maxResults: z.number().int().min(1).max(10).optional()
.describe('Maximum number of snippets to return'),
source: z.enum(['docs', 'tickets', 'wiki']).default('docs')
.describe('Where to search'),
}
常见写法:
| 需求 | 写法 |
|---|
| 必填字符串 | z.string().describe('...') |
| 可选参数 | z.string().optional().describe('...') |
| 默认值 | z.number().default(5) |
| 枚举值 | z.enum(['docs', 'tickets']) |
| 数字范围 | z.number().min(1).max(10) |
配置工具元信息
extras.annotations 用来传 MCP 工具注解。SDK 会把它原样保存到工具定义上,并在 createSdkMcpServer() 注册工具时传给 MCP server。完整字段见 ToolExtras 和 ToolAnnotations。
字段说明:
| 字段 | 类型 | 可选 | 语义 |
|---|
title | string | 是 | 工具的人类可读标题 |
readOnlyHint | boolean | 是 | 标记工具只读,不修改任何状态 |
destructiveHint | boolean | 是 | 标记工具可能修改或删除数据 |
openWorldHint | boolean | 是 | 标记工具会访问外部系统或网络 |
注意:这些字段不会替代权限配置。是否允许调用工具仍由 tools、allowedTools、disallowedTools、permissionMode、canUseTool 和 hooks 决定。当前 mcpServerStatus().tools[] 也不会把 annotations 回显给宿主应用;如果宿主 UI 需要展示这些信息,请在自己的工具定义侧保留映射。
示例
tool(
'search_docs',
'Search internal product documentation.',
{ query: z.string().describe('Search keywords') },
async ({ query }) => ({ content: [{ type: 'text', text: query }] }),
{
annotations: {
readOnlyHint: true,
destructiveHint: false,
openWorldHint: false,
},
},
);
第二步:注册到 MCP server
createSdkMcpServer() 把一个或多个工具注册为同进程 MCP server。server 名会进入完整工具名,因此建议短、稳定。
const kbTools = createSdkMcpServer({
name: 'kb',
version: '1.0.0',
tools: [searchDocs],
});
| 字段 | 怎么填 | 说明 |
|---|
name | 如 kb、orders | server 名,会组成完整工具名 mcp__{name}__{tool} |
version | 如 '1.0.0' | 信息性版本号,可省略 |
tools | [searchDocs, lookupOrder] | 注册到这个 server 的工具列表 |
完整 option 类型见 CreateSdkMcpServerOptions,返回值见 createSdkMcpServer() 返回值。
第三步:接入 query()
把 server 放进 options.mcpServers 后,CLI 会发现其中的工具,并在模型需要时通过 SDK 调回你的 handler。
query({
prompt: 'Search docs for the refund policy and summarize it.',
options: {
auth: accessTokenFromEnv(),
mcpServers: { kb: kbTools },
allowedTools: ['mcp__kb__search_docs'],
},
});
自定义工具的完整名称格式是:
mcp__{serverName}__{toolName}
例如 server 名是 orders,tool 名是 lookup_order,完整工具名就是 mcp__orders__lookup_order。这个完整名称会用于 allowedTools、disallowedTools、canUseTool、hooks matcher 和子 Agent 的 tools 配置。
当模型调用工具时,SDK 提供多层权限控制。你可以决定:
- 本次会话提供哪些工具。
- 哪些工具可以默认放行。
- 哪些工具明确禁止。
- 每次工具调用前是否交给宿主应用动态判断。
权限控制方式总览
| 方式 | 作用 | 粒度 | 适用场景 |
|---|
tools | 限制本次会话可见工具集合 | 会话级 | 想从源头收窄模型能看到的工具 |
allowedTools / disallowedTools | 预授权或禁止指定工具 | 工具级 | 明确知道要允许或禁止哪些工具 |
permissionMode | 设置整次会话的默认权限策略 | 全局 | 快速切换计划模式、自动接受编辑、跳过权限等 |
canUseTool | 每次调用前执行自定义判断逻辑 | 调用级 | 需要根据参数内容动态决策 |
hooks.PreToolUse | 通过 hooks 生命周期拦截工具调用 | 调用级 | 已经使用 hooks 体系,需要统一审计或拦截 |
这些方式可以组合使用。常见做法是:先用 tools 收窄可见工具集合,再用 allowedTools / disallowedTools 设置静态规则,最后用 canUseTool 做参数级判断。
tools 控制本次会话可见的工具集合;allowedTools 和 disallowedTools 控制权限规则。自定义 MCP 工具要使用完整工具名。
// Only expose read/search tools to this session.
query({
prompt: 'Analyze the repository without editing files.',
options: {
tools: ['Read', 'Glob', 'Grep'],
allowedTools: ['Read', 'Glob', 'Grep'],
},
});
// Explicitly deny high-risk tools.
query({
prompt: 'Review the project and report issues.',
options: {
disallowedTools: ['Bash', 'Write', 'Edit'],
},
});
// Use full names for custom MCP tools.
query({
prompt: 'Check order O-1001.',
options: {
mcpServers: { orders: orderTools },
allowedTools: ['mcp__orders__lookup_order'],
},
});
// Disable all tools. The model can only answer from its context.
query({
prompt: 'Explain what this SDK does at a high level.',
options: { tools: [] },
});
当同一个工具同时匹配允许和禁止规则时,禁止规则优先。
方式二:permissionMode
permissionMode 用一行配置设置整次会话的默认权限行为。
query({
prompt: 'Refactor the code.',
options: {
permissionMode: 'acceptEdits',
},
});
| 模式 | 效果 |
|---|
'default' | 标准权限行为,敏感操作按规则或运行时策略处理 |
'acceptEdits' | 自动接受文件编辑类操作,其他敏感操作仍按权限策略处理 |
'bypassPermissions' | 跳过权限检查,必须同时设置 allowDangerouslySkipPermissions: true |
'yolo' | 'bypassPermissions' 的兼容别名,也必须同时设置 allowDangerouslySkipPermissions: true |
'plan' | 计划模式,适合先让模型产出方案 |
'dontAsk' | 不进行交互询问,未预授权或未被规则允许的操作会被拒绝 |
'auto' | 由运行时能力自动判断 allow 或 deny |
canUseTool 会在工具调用前执行。你可以根据工具名、参数内容和审批上下文返回允许或拒绝。
query({
prompt: 'Check order O-1001.',
options: {
auth: accessTokenFromEnv(),
mcpServers: { orders: orderTools },
allowedTools: ['mcp__orders__lookup_order'],
async canUseTool(toolName, input, options) {
if (toolName !== 'mcp__orders__lookup_order') {
return {
behavior: 'deny',
message: 'Only order lookup is allowed in this workflow.',
toolUseID: options.toolUseID,
};
}
return {
behavior: 'allow',
updatedInput: input,
toolUseID: options.toolUseID,
};
},
},
});
常见返回值:
| 返回 | 效果 |
|---|
{ behavior: 'allow' } | 允许执行,使用原始参数 |
{ behavior: 'allow', updatedInput: {...} } | 允许执行,并替换工具参数 |
{ behavior: 'deny', message: 'reason' } | 拒绝执行,模型能看到原因并尝试其他方式 |
{ behavior: 'deny', message: 'reason', interrupt: true } | 拒绝并中断当前 agent loop |
在子 Agent 中使用自定义工具时,也使用完整工具名:
query({
prompt: 'Use the order-support agent to check order O-1001.',
options: {
auth: accessTokenFromEnv(),
mcpServers: { orders: orderTools },
allowedTools: ['Agent'],
agents: {
'order-support': {
description: 'Handles order lookup and explains order status.',
prompt: 'Use order tools to answer order status questions clearly.',
tools: ['mcp__orders__lookup_order'],
},
},
},
});
如果你已经使用 hooks 体系,可以通过 PreToolUse 统一拦截或审计工具调用。
query({
prompt: 'Run the test command.',
options: {
allowedTools: ['Bash'],
hooks: {
PreToolUse: [
{
matcher: 'Bash',
hooks: [
async (input) => {
const command = (input.tool_input as { command: string }).command;
if (command.includes('rm -rf')) {
return {
hookSpecificOutput: {
hookEventName: 'PreToolUse',
permissionDecision: 'deny',
permissionDecisionReason: 'rm -rf is not allowed',
},
};
}
return {
hookSpecificOutput: {
hookEventName: 'PreToolUse',
permissionDecision: 'allow',
},
};
},
],
},
],
},
},
});
canUseTool 的参数结构见 CanUseToolOptions,返回结构见 PermissionResult。更完整的权限策略见 权限控制。
工具 handler 有两类错误路径。
业务失败:返回 isError: true
可预期的业务失败推荐返回 isError: true。SDK 会把这个 CallToolResult 交给 CLI,模型能看到失败内容,并可能重试或选择其他方式。
return {
isError: true,
content: [{
type: 'text',
text: JSON.stringify({
error: 'VALIDATION_ERROR',
message: 'Only SELECT statements are allowed.',
}),
}],
};
适合使用 isError: true 的场景:
- 参数合法但业务上找不到结果,例如订单不存在。
- 安全策略拒绝执行,例如只允许
SELECT 查询。
- 外部服务返回可理解的业务错误。
非预期异常:handler 抛错
如果 handler 抛出异常,MCP 层会把异常转换成错误结果,agent loop 不会因为普通工具异常直接崩掉。但模型通常只能看到异常消息,格式和内容不如显式返回 isError: true 可控。
const toolThatMayThrow = tool(
'fetch_user',
'Fetch a user by ID.',
{ userId: z.string() },
async ({ userId }) => {
const response = await userService.fetch(userId);
if (!response.ok) {
throw new Error('User service failed');
}
return { content: [{ type: 'text', text: await response.text() }] };
},
);
建议:业务上可预期的失败用 isError: true;真正意外的异常再抛出。
工具 handler 返回 MCP 的 CallToolResult。最常用的是文本内容:
return {
content: [{ type: 'text', text: 'done' }],
};
也可以返回结构化 JSON 字符串,方便模型理解和继续处理:
return {
content: [{
type: 'text',
text: JSON.stringify({
orderId: 'O-1001',
status: 'shipped',
eta: '2026-05-20',
}),
}],
};
常见内容块;完整联合类型见 McpToolResultContent:
| 类型 | 形状 | 说明 |
|---|
| 文本 | { type: 'text', text } | 最常用,适合自然语言或 JSON 字符串 |
| 图片 | { type: 'image', data, mimeType } | data 是 base64 |
| 音频 | { type: 'audio', data, mimeType } | data 是 base64 |
| 资源链接 | { type: 'resource_link', uri, name?, description?, mimeType? } | 返回可引用资源 |
| 内嵌资源 | { type: 'resource', resource } | 返回文本或二进制资源内容 |
完整类型定义见 Tools Reference - CallToolResult。
常见踩坑
- 权限配置里写自定义工具时,要写
mcp__server__tool 完整名称。
tool() 的第三个参数传 Zod raw shape,不要传 z.object(...)。
- 工具描述要写“什么时候用、做什么、返回什么”,不要只写
query、helper 这类模糊描述。
readOnlyHint 是工具元信息和调度提示,不是权限开关;是否允许执行仍由权限配置决定。
- 不要把大而全的业务入口都塞进一个万能工具。一个工具最好完成一类清晰动作。
继续阅读
- Tools Reference:内置工具列表、
tool()、createSdkMcpServer()、CallToolResult、内置工具输入输出类型。
- MCP 集成:stdio、SSE、HTTP、OAuth 等 MCP server 接入方式。
- 权限控制:
permissionMode、allowedTools、canUseTool、权限规则更新。
- 子 Agent 使用指南:让不同 Agent 使用不同工具集。