DeepDojo.AIPublished 2026-11-09Updated 2026-11-09中文

Fix Wrong Tool Calls by Fixing the Tool Interface

Claude CodeAI Coding

The user makes one request: "Check whether PR 128 has any comments that need action." To do that well, the agent has to fetch the comments and then compare them with the code.

Once the tool is wired in, trouble follows. The agent picks the wrong action, reads one page and calls it done, resends a comment after a write times out, or gets steered by instructions hidden inside a comment.

These failures look like a model that isn't smart enough, but they often trace back to the tool interface. The model decides how to call a tool from its name, description, parameters, and results:

  • a vague interface forces it to guess
  • a result that never says whether everything was read makes one page look like the whole thing
  • a bare error code leaves it unsure whether to fix an argument or retry

Working through a "read PR comments" tool, each problem gets its own fix:

  • names and descriptions that keep actions distinct
  • a paginated summary the caller can trace back
  • error messages that point to the next step
  • a plan for the write whose outcome is unknown after a timeout
  • a clear line between outside material and authorization

A well-designed tool lets the caller see the object, the result, and what to do next. The opening request gets walked through the finished design at the end, and the same checks work on the ticket, monitoring, or document tools you connect yourself.

01 Capability gaps versus method gaps

To check a GitHub PR for blocking comments, an approved gh CLI that already reads them reliably can simply be reused. Consider MCP when you need unified authentication across repositories or frequent reads of structured external state, and existing capabilities fall short. For what each of these mechanisms does, see Skill, MCP, Hook, or Subagent: which do you need?

Actual needHow to handle it
The local shell already runs it reliablyUse existing Bash and project commands
Only static background or local rulesKnowledge docs, Skill reference material, or rules
An existing action lacks an execution methodSkill
A check at a fixed momentHook
Occasional access to a piece of materialSupply the material or search the web
Frequent reads or writes against an external systemConsider MCP when permissions and returns can be controlled

The rest of this article designs an illustrative tool around "read PR comments and judge blocking issues." The tool names and return fields are teaching designs, and a real interface follows each system. The method applies equally to tickets, monitoring, and document integrations.

Before connecting, confirm whether direct access is necessary, who controls the source, whether write permission is needed, whether the return might be too long, whether the tool's scope can be narrowed, and what prompt injection external content could bring. Reading PR comments needs only read access. Merging a PR changes remote state and gets its own limits.

02 Design a clear tool interface

The model relies mainly on the tool name, the description, the parameter schema, and the current context to decide what to call. A schema is the structural contract for parameters: field types, required fields, and allowed values. Anthropic's engineering post Writing effective tools for agents also recommends namespacing to draw clear boundaries. Whatever needs to be told apart should be spelled out in the interface.

Here is a set of example tool names, for illustration only:

github_pr_get
github_pr_list_comments
github_pr_create_review

The shared prefix names the system and the resource, and the ending separates read, list, and write. Compared with getData, runAction, or sync, these names make it easier for the model to tell reading comments from creating a review, and to pick the read action the task needs.

Names still need a description to fill in boundaries. A comments-listing tool should say which repository and PR identifier it targets, what it returns, and whether it paginates. A review-creating tool should say it produces a remote write. When trimming parameters, keep the fields needed to locate the object and drop pointless combinations. You can check a design against the calling process:

StageWhat the tool should provide
SelectionClear resource, action, and conditions of use
Parameter fillingClear required fields, allowed values, and defaults
Reading the resultOutput fields that support the current judgment
Error correctionThe correctable cause and the next step
Execution boundaryReads, writes, deletes, and external side effects are distinguishable

Defaults matter most where they touch the task boundary. A read-only query should only read, whether or not an optional parameter is set. Repeated calls should also be idempotent where possible: the same request run twice gives the same result as running it once, so there is one comment and one charge. An action that really does create a record needs deduplication or a state check built in.

03 Return a summary that can be traced back

If a PR has a huge number of comments, returning all of them crowds the context, and returning only "risky" leaves the reader with no location. A good short output keeps the conclusion, the object, and a way back. The caller specified a repository and a PR, and the tool returns a summary of the first batch of comments:

{
  "repository": "example/shop",
  "pr": 128,
  "coverage": "partial",
  "next_cursor": "page-2",
  "comments": [
    {
      "comment_id": 731,
      "file": "src/auth/session.ts",
      "line": 42,
      "summary": "Reviewer reports a possible token refresh race.",
      "resolved": false
    }
  ]
}

The repository and PR identifier answer "which object was checked," and the comment ID, file, and line give a way back. The summary keeps the meaning of "the reviewer raised a possible problem." It prompts a look at a race condition, and whether the problem actually reproduces is verified separately. An unresolved comment isn't automatically a release blocker, so judge it by its content, the current code, and the team's standards.

coverage: partial and next_cursor mean some results haven't been read. When the current page is empty, the whole PR may still have comments, so keep fetching the remaining pages. When a summary affects a decision, read the full text by comment ID.

The detail entry point has to be callable, so spell out the tool name and the required parameters. Report coverage: complete and an empty cursor only after the agreed query range has been fully read. Here, complete describes only read coverage. Whether the code risk has been verified, and whether external state changes later, both sit outside it.

A short output should keep the scope, the object, the evidence, and the way back that support a judgment. A bare "risky" is too little, and returning everything is too much. Locate with the summary first and expand on demand. That controls how much context is used and keeps the evidence you need.

Xiaohei tunes a tool interface so clear parameters pass a permission gate and failures go to retry or state checks

04 Let error messages guide the next step

The return below lacks what's needed to correct the error:

{
  "error": "INVALID_ARGUMENT"
}

The model can't tell which field is wrong or whether to retry. You can write the error like this:

{
  "error": "invalid_status",
  "field": "status",
  "allowed_values": ["todo", "in_progress", "blocked", "done"],
  "received": "doing",
  "operation_state": "not_applied",
  "retry": "after_input_correction",
  "suggestion": "Use in_progress only if the task has started."
}

This names the field in error, the allowed values, and the value received, and it says the operation was not applied. retry says to correct the input first. The suggestion is conditional too: use in_progress only if the task has actually started. These fields are a custom contract, and the server has to fill them in from the real execution state.

Separate a parameter error from an unknown execution result. A write such as creating a comment can time out because the request never arrived, or because the write succeeded and the response was lost. Retrying without checking the real state can create duplicates.

Invalid parameters           -> Correct the input, then retry according to the contract
Insufficient permissions     -> Check authorization; do not bypass it with different parameters
Write timeout, result unknown -> Check the actual state or use deduplication

An error contract should state the real execution state and the allowed next step, and network failures need to be labeled by their actual state as well. For example, after a comment-creation timeout, if a query confirms the comment exists, check its content and report it as sent. If the service can reliably confirm it wasn't created and guarantees that resubmitting the same request produces only one record, retry according to the contract. When the result is unclear and there's no deduplication guarantee, record it as "send result unknown" and stop resending automatically.

A timeout lacks an execution result, and the parameters may be fine. Refilling the same content leaves the result just as unknown.

05 Wrap steps, keep authorization separate

Stable lower-level read dependencies can be wrapped in a tool. To check a PR, for example, you first locate the repository and then read the PR and its comments. A confirmed dependency order can run inside the tool, saving the caller from ferrying intermediate results.

A wrapper should still return the object, the scope, and the evidence, and say whether it's read-only. Looking things up, changing state, sending notifications, and deploying each carry their own authorization, so keep them as separate actions. Bundle them into one catch-all action and, after a failure partway through, the caller can't see which writes took effect and has to retry everything.

First collect and check the facts, then move to a specific write under specific authorization. Cutting the number of calls is fine as long as side effects and execution state stay visible.

06 External material is not authorization

Prompt injection is when external content tries to change an agent's instructions or behavior. Approving an MCP connection says the tool may be used. The comments, web pages, and documents it returns are only material, and the authority to give instructions stays with the user and the system.

Say a PR comment asks the agent to read the local .env file and send it to an outside address. That's an out-of-scope request inside material to be analyzed. Treat it as material, keep working within the original task scope and tool permissions, and leave the credentials where they are.

Keep three layers apart: whether the tool connection is approved, whether this request falls within the current task, and whether the content itself can be trusted. Read permission and send permission are granted separately, and only the user changes local permission settings.

ActionBoundary to set independently
ReadWhich resource, which fields, and whether sensitive information is included
WriteThe exact target, the content to be written, and the approved scope
DeleteThe object, whether it can be recovered, and explicit authorization
Send out, merge, or deployThe real impact and how final confirmation happens

Enforce permission limits in the tool or on the server, and use text to help the model understand what a tool is for. The interface should enforce "no access to other projects" so that even if the model picks the wrong object, it's refused. How permissions, Hooks, and the sandbox divide the work in Claude Code is covered in The Hook errored, but the dangerous action still ran.

07 Handle open choices with explicit interaction

Here's an interaction problem. A tool needs the user to pick one of two target environments. If the choice is buried in a free-text reply and the system then runs against a default environment, the user may never have actually decided.

Claude Code's AskUserQuestion shows one way to do it: when it needs the user to answer, it enters a dedicated questioning interaction. Application and tool design can do the same by making "ask, receive the answer, continue from it" a visible state, so the system has a basis for knowing whether the user approved.

Prepare the choices or proposed changes
  → Ask a clearly structured question or request confirmation
  → Receive an answer with a clear scope
  → Execute the selected or authorized action

When the target environment is undecided, present the candidates and get a choice. When a high-impact operation isn't yet authorized, show the specific differences and impact first. The user's decision belongs to the user, and actions that are already clearly approved run directly. Operations that the runtime requires formal approval for still go through formal approval.

08 Walk through one full call

Back to the opening request, "check whether PR 128 has comments that need action." Does the tool design hold up?

  1. Locate the object. If the current project identifies a unique repository, use it. Otherwise ask first, so a matching PR number doesn't lead to reading another repository.
  2. Read and complete. Call the read-only comments tool. If the first batch returns partial, keep fetching the rest. When a summary affects the judgment, go back to the original by ID.
  3. Form a judgment. Compare what the comments ask for with the current changes, and sort confirmed problems, reviewer opinions, and risks awaiting verification. Even with every comment read, code verification is a separate job.
  4. Close within the authorization. Output the findings and the evidence. The request only asked for reading, so wrap up once the reading is done.

If the user then asks to "post this conclusion to the PR," the write tool comes into play once the target and content are clear, and any open question goes through a clear interaction. If posting times out, first check whether the comment already appeared, or use the contract's deduplication, so the same comment is sent once.

While tools and processes are still unproven in one project, maintaining the .claude/ configuration directly is simpler. Use real tasks first to check that the right tool is chosen, parameters are clear, long results are usable, errors can be corrected, and out-of-bounds requests are refused. Only then consider packaging them as a Plugin to share. A vague interface that gets distributed spreads the same problem to more projects, so fix the interface first.

Switch to a ticket system and the resource names and fields change, while the logic stays the same: locate the object, understand what the return covers, fix errors according to the execution state, and change state only within the authorization. A well-designed tool lets the caller see the object, the result, and what to do next.

Advanced Claude Code Guide →

Design tool names, return summaries, and an error contract for an external system, and mark the read and write authorization boundaries.

RSS