跳到正文
FunCoding

搜索

搜索文档、Skill 和 MCP

Permission Handling

Control which tools agents can run automatically and which require explicit approval.

Tool policies control whether tools are enabled and whether they run without approval. Tool names not listed in toolPolicies default to enabled and auto-approved, so set policies explicitly for tools that need review.

Tool Policies

The simplest way to manage permissions is through tool policies. Set them per-tool when creating an agent:

import { Agent, createBuiltinTools } from "@cline/sdk"

const agent = new Agent({
  // ...config
  tools: createBuiltinTools({ cwd: process.cwd() }),
  toolPolicies: {
    read_files: { autoApprove: true },       // Always run without asking
    search_codebase: { autoApprove: true },  // Always run without asking
    editor: { autoApprove: false },          // Always ask before running
    run_commands: { autoApprove: false },    // Always ask before running
  },
})

Policy Options

PolicyEffect
{ autoApprove: true }Tool executes immediately without approval
{ autoApprove: false }Tool waits for approval before executing
{ enabled: false }Tool is completely disabled (model won't see it)
No policy setDefaults to enabled and auto-approved

Auto-Approve Everything

For trusted environments (CI pipelines, sandboxed containers, development scripts):

const agent = new Agent({
  // ...config
  tools: allTools,
  toolPolicies: Object.fromEntries(
    allTools.map((t) => [t.name, { autoApprove: true }])
  ),
})

Or with ClineCore:

const session = await cline.start({
  config: {
    enableTools: true,
  },
  toolPolicies: {
    run_commands: { autoApprove: true },
    editor: { autoApprove: true },
    read_files: { autoApprove: true },
    apply_patch: { autoApprove: true },
    search_codebase: { autoApprove: true },
    fetch_web_content: { autoApprove: true },
  },
  capabilities: {
    requestToolApproval: async () => ({ approved: true }),
  },
  // ...
})

Auto-approving all tools means the agent can execute any shell command, modify any file, and make network requests without your review. Only use this in environments where the agent's actions are either sandboxed or fully trusted.

Interactive Approval

For applications that need human oversight, implement a custom approval handler:

import { ClineCore } from "@cline/sdk"
import * as readline from "readline"

const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
const ask = (q: string) => new Promise<string>((res) => rl.question(q, res))

const cline = await ClineCore.create({
  clientName: "interactive-app",
  capabilities: {
    requestToolApproval: async (request) => {
      console.log(`\nTool: ${request.toolName}`)
      console.log(`Input: ${JSON.stringify(request.input, null, 2)}`)

      const answer = await ask("Approve? (y/n): ")
      return { approved: answer.toLowerCase() === "y" }
    },
  },
})

Tiered Permissions

A practical middle ground: auto-approve read-only operations, require approval for writes:

const READ_TOOLS = new Set(["read_files", "search_codebase", "fetch_web_content"])
const WRITE_TOOLS = new Set(["run_commands", "editor", "apply_patch"])

const toolPolicies: Record<string, { autoApprove: boolean }> = {}

for (const tool of READ_TOOLS) {
  toolPolicies[tool] = { autoApprove: true }
}
for (const tool of WRITE_TOOLS) {
  toolPolicies[tool] = { autoApprove: false }
}

Conditional Approval Logic

Approve based on what the tool is actually doing, not just which tool it is:

const cline = await ClineCore.create({
  clientName: "smart-approval",
  capabilities: {
    requestToolApproval: async (request) => {
      // request.input is the model's raw tool input, so check its shape first.
      const input = request.input as { commands?: unknown; files?: unknown }

      // Auto-approve a fixed set of commands that only read: { commands: string[] }.
      // Match whole commands, because flags can change what a command does.
      // Git can also run programs named in a repository's config (for example
      // core.fsmonitor), so approve Git commands only in repositories you trust.
      if (request.toolName === "run_commands" && Array.isArray(input.commands)) {
        const safeCommands = new Set(["ls", "git status", "git log --oneline -20"])
        if (input.commands.every((cmd) => typeof cmd === "string" && safeCommands.has(cmd.trim()))) {
          return { approved: true }
        }
      }

      // Auto-approve reads in specific directories: { files: { path: string }[] }.
      if (request.toolName === "read_files" && Array.isArray(input.files)) {
        const isAllowed = (file: unknown) => {
          const path = (file as { path?: unknown })?.path
          return typeof path === "string" && !path.includes("..") &&
            (path.startsWith("/src/") || path.startsWith("/tests/"))
        }
        if (input.files.every(isAllowed)) {
          return { approved: true }
        }
      }

      // Everything else requires manual approval
      console.log(`Approval needed: ${request.toolName}`)
      console.log(`Input: ${JSON.stringify(request.input)}`)
      return { approved: false }
    },
  },
})

What Happens When a Tool is Rejected

When approval is denied, the agent receives a rejection message and can adjust its approach. It might:

  • Ask the user for clarification
  • Try a different tool to accomplish the same goal
  • Modify its approach and try again with different parameters
  • Give up on that subtask and move on

The agent does not get stuck in a loop. The rejection counts as a response, and the agent proceeds with its next iteration.