跳转到主要内容

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 的权限控制能力用于管理模型在一次 query() 会话里能做什么。它可以限制模型可见的工具,设置默认授权策略,在工具执行前交给宿主应用审批,也可以在用户授权后把新规则应用到当前会话。 权限控制不是一个单独的 API,而是一组放在 query({ options }) 里的配置。通常你会先决定本次会话允许模型使用哪些工具,再决定这些工具在什么情况下可以执行,最后按需要接入运行时审批、动态规则更新、settings 或 hooks。
const messages = query({
  prompt: 'Inspect the repository and summarize risky changes.',
  options: {
    auth: accessTokenFromEnv(),
    cwd: '/path/to/project',
    tools: ['Read', 'Grep', 'Bash'],
    allowedTools: ['Read', 'Grep'],
    disallowedTools: ['Bash'],
    permissionMode: 'default',
  },
});

for await (const message of messages) {
  console.log(message);
}
上面这个例子表达了一个常见策略:模型可以看到 ReadGrepBash,其中 ReadGrep 预授权,Bash 禁止执行。实际项目里,你可以继续加上 canUseTool,把未预授权的操作交给自己的产品 UI、审批系统或风控服务判断。

快速开始:让宿主应用审批工具调用

当你需要把工具调用接入自己的审批逻辑时,使用 canUseTool。SDK 会在运行时把工具名、工具入参和一组可展示的审批信息交给你的 callback。callback 返回 allow 时工具继续执行,返回 deny 时工具被拒绝。
const readOrder = tool(
  'read_order',
  'Read an order by ID.',
  { orderId: z.string() },
  async ({ orderId }) => ({
    content: [{ type: 'text', text: `order:${orderId}` }],
  }),
);

const server = createSdkMcpServer({
  name: 'orders',
  tools: [readOrder],
});

query({
  prompt: 'Read order 1001.',
  options: {
    auth: accessTokenFromEnv(),
    mcpServers: { orders: server },
    permissionMode: 'default',
    async canUseTool(toolName, input, options) {
      if (toolName !== 'mcp__orders__read_order') {
        return {
          behavior: 'deny',
          message: 'Only order reads are allowed in this workflow.',
          toolUseID: options.toolUseID,
        };
      }

      return {
        behavior: 'allow',
        updatedInput: input,
        toolUseID: options.toolUseID,
      };
    },
  },
});
这个例子里,read_order 是一个 SDK MCP 工具。模型调用它时,完整工具名会是 mcp__orders__read_ordercanUseTool 只允许这个工具执行,并把原始入参作为 updatedInput 返回。返回 toolUseID 可以让运行时把审批结果和本次工具调用准确对应起来。

控制默认策略:permissionMode

permissionMode 决定会话的默认权限策略。它适合表达“这次会话整体处于什么模式”,例如先计划、自动接受编辑、不询问直接拒绝,或者在受控环境里跳过权限检查。
query({
  prompt: 'Plan the migration. Do not edit files yet.',
  options: {
    auth: accessTokenFromEnv(),
    cwd: '/path/to/project',
    permissionMode: 'plan',
    planModeInstructions: 'Only produce a concise migration checklist.',
  },
});
plan 模式适合让模型先产出计划。planModeInstructions 可以覆盖计划模式下的工作流说明,让模型以你需要的格式输出计划。
Mode行为
default标准权限行为。工具调用按 tools、allow/deny 规则、动态审批或运行时策略处理
acceptEdits自动接受文件编辑类操作,适合已确认可以修改工作区的场景
bypassPermissions跳过权限检查,必须同时设置 allowDangerouslySkipPermissions: true
yolobypassPermissions 的兼容别名,也必须同时设置 allowDangerouslySkipPermissions: true
plan计划模式,适合先产出执行计划,默认不执行实际变更
dontAsk不进行交互询问。未预授权、未被规则允许的操作会被拒绝
auto由运行时能力自动判断 allow 或 deny。安全的工作区内文件编辑可能自动放行
需要在同一个会话里切换模式时,可以使用返回的 Query 对象:
const q = query({
  prompt: 'Plan the change first.',
  options: {
    auth: accessTokenFromEnv(),
    cwd: '/path/to/project',
    permissionMode: 'plan',
  },
});

await q.setPermissionMode('default');
bypassPermissionsyolo 都是高风险模式。SDK 要求显式传入 allowDangerouslySkipPermissions: true,避免调用方误把普通会话变成跳过权限检查的会话。
query({
  prompt: 'Run the trusted local maintenance task.',
  options: {
    auth: accessTokenFromEnv(),
    cwd: '/path/to/project',
    permissionMode: 'bypassPermissions',
    allowDangerouslySkipPermissions: true,
  },
});

控制工具范围:tools、allowedTools、disallowedTools

工具控制回答的是“模型能看到哪些工具,以及哪些工具默认允许或禁止”。这三个字段经常一起出现,但语义不同。
query({
  prompt: 'Inspect the repo without modifying it.',
  options: {
    auth: accessTokenFromEnv(),
    cwd: '/path/to/project',
    tools: ['Read', 'Grep', 'Bash'],
    allowedTools: ['Read', 'Grep'],
    disallowedTools: ['Bash'],
  },
});
这段配置表示:本次会话只提供 ReadGrepBash 三个工具;其中 ReadGrep 预授权;Bash 被禁止,即使模型想调用也不会执行。
字段作用适合场景
tools限制本次会话可用工具集合缩小模型能力边界
allowedTools添加允许规则让低风险工具无需反复审批
disallowedTools添加拒绝规则明确禁止高风险工具
当同一个工具同时匹配 allow 和 deny 时,deny 优先。这能确保禁止规则不会被更宽泛的允许规则绕过。 MCP 工具也使用完整工具名匹配。例如 SDK MCP server 名为 orders,工具名为 read_order,完整工具名就是 mcp__orders__read_order
query({
  prompt: 'Read order 1001.',
  options: {
    auth: accessTokenFromEnv(),
    mcpServers: { orders: server },
    allowedTools: ['mcp__orders__read_order'],
  },
});

运行时审批:canUseTool

canUseTool 适合宿主应用需要参与审批的场景。比如你要把权限请求展示在自己的 UI 里,让用户点“允许一次”“始终允许本会话”或“拒绝”;或者你要调用企业风控服务判断某个命令是否可执行。
query({
  prompt: 'Create a changelog file for this release.',
  options: {
    auth: accessTokenFromEnv(),
    cwd: '/path/to/project',
    permissionMode: 'default',
    async canUseTool(toolName, input, options) {
      showApprovalDialog({
        title: options.title ?? toolName,
        description: options.description,
        input,
      });

      const approved = await waitForUserApproval(options.signal);

      if (!approved) {
        return {
          behavior: 'deny',
          message: 'Rejected by user.',
          toolUseID: options.toolUseID,
        };
      }

      return {
        behavior: 'allow',
        updatedInput: input,
        toolUseID: options.toolUseID,
      };
    },
  },
});
canUseTool 的签名如下:
type CanUseTool = (
  toolName: string,
  input: Record<string, unknown>,
  options: {
    signal: AbortSignal;
    suggestions?: PermissionUpdate[];
    blockedPath?: string;
    decisionReason?: string;
    title?: string;
    displayName?: string;
    description?: string;
    toolUseID: string;
    agentID?: string;
  },
) => Promise<PermissionResult>;
关键字段解释:
字段说明
toolName完整工具名,例如 ReadBashmcp__orders__read_order
input本次工具调用的原始参数
options.toolUseID本次工具调用 ID,返回审批结果时建议带回
options.signal授权请求被取消时会 abort,UI 或远程审批应监听它
options.title / displayName / description运行时生成的人类可读文案,适合直接用于审批 UI
options.suggestions运行时建议的权限更新,可用于“始终允许本会话”
options.blockedPath路径相关授权场景中的受限路径
options.decisionReason运行时提供的审批原因说明,可用于展示或审计
options.agentID子 Agent 发起工具调用时的 Agent ID
返回 allow 表示继续执行工具:
return {
  behavior: 'allow',
  updatedInput: input,
  toolUseID: options.toolUseID,
};
updatedInput 是工具最终收到的参数。你可以原样返回,也可以在审批通过后修改参数。例如给查询增加租户 ID、把路径改写到安全目录,或者去掉不允许的字段。 返回 deny 表示拒绝工具:
return {
  behavior: 'deny',
  message: 'This command is not allowed in the current workspace.',
  toolUseID: options.toolUseID,
};
deny.message 必填,它会成为拒绝原因的一部分,供模型、日志或宿主应用展示。SDK 收到 CLI 的授权请求但没有配置 canUseTool 时,会返回错误,不会默认放行。 当权限系统直接拒绝工具调用时,消息流中可能出现结构化的权限拒绝消息:
type SDKPermissionDeniedMessage = {
  type: 'system';
  subtype: 'permission_denied';
  tool_name: string;
  tool_use_id?: string;
  message?: string;
  decision_reason?: string;
  decision_reason_type?: string;
};
这类消息常见于 permissionMode: 'dontAsk'、自动拒绝或规则拒绝等场景。宿主应用可以用它更新 UI 状态或写审计日志。

会话内更新权限:PermissionUpdate

PermissionUpdate 用于在一次审批之后更新当前会话里的权限规则。最常见的场景是用户在审批 UI 里选择“始终允许本会话”。这时你可以把运行时给出的 suggestions 原样返回,也可以自己构造明确的规则。
async function canUseTool(toolName, input, options) {
  const decision = await showApprovalDialog({
    toolName,
    suggestions: options.suggestions,
  });

  if (decision === 'always-allow-this-session') {
    return {
      behavior: 'allow',
      updatedInput: input,
      toolUseID: options.toolUseID,
      updatedPermissions: options.suggestions,
    };
  }

  if (decision === 'allow-once') {
    return {
      behavior: 'allow',
      updatedInput: input,
      toolUseID: options.toolUseID,
    };
  }

  return {
    behavior: 'deny',
    message: 'Rejected by user.',
    toolUseID: options.toolUseID,
  };
}
也可以直接构造规则:
return {
  behavior: 'allow',
  updatedInput: input,
  toolUseID: options.toolUseID,
  updatedPermissions: [
    {
      type: 'addRules',
      behavior: 'allow',
      destination: 'session',
      rules: [{ toolName: 'mcp__orders__read_order' }],
    },
  ],
};
支持的更新类型:
类型作用
addRules追加 allow、ask 或 deny 规则
replaceRules替换规则
removeRules删除规则
setMode切换权限模式
addDirectories追加允许访问的目录
removeDirectories移除目录授权
推荐把动态权限更新写入当前会话:
destination: 'session'
session 只影响当前 query 会话后续的权限检查。需要持久化到本地、项目或用户级配置时,优先使用 settings 管理流程,而不是依赖单次工具审批回调里的动态更新。

访问额外目录:additionalDirectories

默认情况下,会话以 cwd 作为主要工作目录。模型需要读取或修改 cwd 之外的目录时,应显式传入 additionalDirectories
query({
  prompt: 'Inspect the app and the shared package.',
  options: {
    auth: accessTokenFromEnv(),
    cwd: '/repo/app',
    additionalDirectories: ['/repo/packages/shared'],
  },
});
这个配置表示会话主工作目录是 /repo/app,同时允许模型访问 /repo/packages/shared。这适合 monorepo、跨仓库调试、共享库排查等场景。 运行过程中也可以通过 PermissionUpdate 调整目录授权:
return {
  behavior: 'allow',
  updatedInput: input,
  toolUseID: options.toolUseID,
  updatedPermissions: [
    {
      type: 'addDirectories',
      destination: 'session',
      directories: ['/repo/packages/shared'],
    },
  ],
};
目录授权属于权限边界的一部分。不要把大范围目录加入 additionalDirectories 作为通用默认值;更稳妥的做法是按任务需要添加最小目录集合。

外部授权工具:permissionPromptToolName

permissionPromptToolName 用于把权限请求交给运行环境中的 permission prompt tool,而不是在 SDK host 中实现 canUseTool。它适合已有外部审批工具、远程运行环境或统一权限网关的场景。
query({
  prompt: 'Run the task.',
  options: {
    auth: accessTokenFromEnv(),
    permissionPromptToolName: 'mcp__permission_server__approve',
  },
});
使用时要注意三点:
  • permissionPromptToolName 必须是当前运行环境可以识别的 prompt tool 名称。
  • permissionPromptToolNamecanUseTool 互斥,不能同时传入。
  • SDK host 自己要决定审批时,优先使用 canUseTool
permission prompt tool 会收到下面的输入:
type PermissionPromptToolInput = {
  tool_name: string;
  input: Record<string, unknown>;
  tool_use_id?: string;
};
它需要返回权限结果:
type PermissionPromptToolOutput =
  | {
      behavior: 'allow';
      updatedInput: Record<string, unknown>;
      updatedPermissions?: PermissionUpdate[];
      toolUseID?: string;
    }
  | {
      behavior: 'deny';
      message: string;
      interrupt?: boolean;
      toolUseID?: string;
    };
allow.updatedInput 是最终执行工具时使用的入参。如果希望保持原始入参,应该原样返回收到的 inputdeny.message 必填。interrupt: true 表示拒绝后中断当前 Agent 流程。

用 settings 提供权限规则

settings 适合在会话启动前提供静态权限配置。它比 canUseTool 更适合表达“这个项目默认允许什么、拒绝什么、额外目录有哪些”。
query({
  prompt: 'Inspect the project.',
  options: {
    auth: accessTokenFromEnv(),
    cwd: '/path/to/project',
    settings: {
      permissions: {
        allow: ['Read', 'Grep'],
        deny: ['Bash'],
        ask: ['Write'],
        defaultMode: 'default',
        additionalDirectories: ['/path/to/shared-lib'],
      },
    },
  },
});
字段说明:
字段说明
permissions.allow允许规则
permissions.deny拒绝规则
permissions.ask始终询问规则
permissions.defaultMode默认权限模式
permissions.disableBypassPermissionsMode设置为 'disable' 时禁用跳过权限检查模式
permissions.additionalDirectories额外可访问目录
如果你的应用会读取并应用 settings 中的默认权限模式,建议在执行高风险模式前做自己的产品级确认。bypassPermissionsyolo 这类模式应该只出现在明确受信任的环境中。

用 hooks 做高级拦截和审计

Hooks 适合已经接入 SDK hooks 体系,并希望在工具生命周期里做更细粒度控制的场景。和 canUseTool 相比,hooks 更适合横切逻辑,例如审计、告警、统一拦截、记录拒绝原因。
query({
  prompt: 'Inspect the repo.',
  options: {
    auth: accessTokenFromEnv(),
    cwd: '/path/to/project',
    hooks: {
      PreToolUse: [
        {
          matcher: 'Bash',
          hooks: [
            async (input) => {
              return {
                hookSpecificOutput: {
                  hookEventName: 'PreToolUse',
                  permissionDecision: 'deny',
                  permissionDecisionReason: 'Shell commands are disabled here.',
                },
              };
            },
          ],
        },
      ],
    },
  },
});
权限相关 hook 主要有三类:
Hook触发时机常见用途
PreToolUse工具调用前提前允许、拒绝、要求询问或交给后续流程
PermissionRequest进入权限请求时在正常 prompt 前直接返回允许或拒绝
PermissionDenied权限被拒绝后审计、告警、记录拒绝原因
PreToolUse 可以返回:
{
  hookSpecificOutput: {
    hookEventName: 'PreToolUse',
    permissionDecision: 'allow' | 'deny' | 'ask' | 'defer',
    permissionDecisionReason?: string,
    updatedInput?: Record<string, unknown>,
  },
}
PermissionRequest 可以返回类似工具审批的权限结果:
{
  hookSpecificOutput: {
    hookEventName: 'PermissionRequest',
    decision: {
      behavior: 'deny',
      message: 'Denied by policy.',
    },
  },
}
PermissionDenied 通常用于观察结果,不负责放行工具。它的输入会包含被拒绝的工具名、工具入参、工具调用 ID,以及拒绝原因。

MCP Tool Policy

如果权限策略天然属于某个 MCP server,也可以直接在 MCP server config 里声明 tool-level permission policy。这样策略跟随 MCP server 配置,而不是散落在全局 allowedToolsdisallowedTools 中。
query({
  prompt: 'Use repo tools.',
  options: {
    auth: accessTokenFromEnv(),
    mcpServers: {
      repo_tools: {
        type: 'http',
        url: process.env.REPO_TOOLS_MCP_URL!,
        tools: [
          { name: 'search', permission_policy: 'always_allow' },
          { name: 'write_file', permission_policy: 'always_ask' },
          { name: 'delete_file', permission_policy: 'always_deny' },
        ],
      },
    },
  },
});
策略含义:
策略行为
always_allow匹配工具直接允许
always_ask匹配工具进入授权流程
always_deny匹配工具直接拒绝
name 可以是 MCP tool 的原始名称,也可以是完整工具名,例如 mcp__repo_tools__search。实际匹配时,运行时会把策略名称映射到当前 MCP 工具调用。