Skip to content

Tool Use and the Agent-Computer Interface

What separates an agentic loop from an elaborate text conversation is the tool. A loop without tools is just the model reading its own prior responses and generating new ones — a self-dialogue. Tools are what make it possible to read files, execute code, search the web, and query databases — actual actions with actual consequences.

The tool-calling mechanism runs on a standardized protocol. When the model wants to use a tool, it declares that intent. The harness executes the tool and returns the result. Understanding this protocol in detail is the foundation of building reliable loops.

Tool calls are represented as specifically-typed messages within the conversation history.

Tool Call Protocol Flow
───────────────────────────────────────────────────────────
[user] "Read foo.py and find the bug"
[assistant] stop_reason: "tool_use"
tool_calls: [{
id: "call_01",
name: "read_file",
input: {"path": "foo.py"}
}]
┌─ harness executes read_file("foo.py") ─┐
└────────────────────────────────────────┘
[tool] tool_use_id: "call_01"
content: "def foo():\n return lst[10] # IndexError"
[assistant] stop_reason: "end_turn"
text: "Line 5 has an index error. Check the list
length before accessing it, or add exception
handling."
───────────────────────────────────────────────────────────

stop_reason is the key signal. "tool_use" means the model wants to call a tool and continue working. "end_turn" means no more tools are needed and the task is complete. The loop’s branching logic is driven entirely by these two values.

Agent-Computer Interface (ACI) Design Principles

Section titled “Agent-Computer Interface (ACI) Design Principles”

Just as HCI (Human-Computer Interface) governs how humans interact with computers, ACI (Agent-Computer Interface) governs how agents interact with computers. Anthropic’s production experience has produced a set of ACI design principles worth internalizing.

Principle 1: Clear Parameter Names and Descriptions

Section titled “Principle 1: Clear Parameter Names and Descriptions”

The model learns how to use a tool entirely from the tool’s definition text. Ambiguous parameter names result in wrong values being passed.

Bad example Good example
───────────────────────────────────────────────────────────
name: "process" name: "search_files"
params: params:
- x: string - directory: string
- y: boolean (absolute path of the dir to search)
- pattern: string
(filename glob, e.g. "*.py")
- recursive: boolean
(whether to search subdirectories)
───────────────────────────────────────────────────────────

More tools means more decision points where the model can pick the wrong one. Similar functionality should be merged into a single tool; each tool should have a single responsibility.

Pattern to avoid Recommended pattern
read_python_file, read_json_file, read_text_file read_file(path, encoding?)
search_by_name, search_by_content, search_by_date search(query, field?)
create_dir, make_directory, mkdir create_directory(path)

When a tool fails, an error message that just says “error occurred” tells the model nothing. Error messages must be specific and actionable so the model can take a correct corrective action in the next iteration.

Bad error message:
"Cannot open file."
Good error message:
"Cannot open file: /home/user/foo.py
Reason: file does not exist.
Suggestion: use list_files('/home/user/') to see
which files are present."

This is illustrative pseudocode; actual API signatures differ.

# Tool definitions: the schema passed to the model
tools = [
{
"name": "read_file",
"description": "Read and return the contents of the file at the given path.",
"input_schema": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "Absolute path of the file to read (e.g. /workspace/main.py)"
},
"encoding": {
"type": "string",
"description": "File encoding (default: utf-8)",
"default": "utf-8"
}
},
"required": ["path"]
}
},
{
"name": "run_tests",
"description": "Run pytest and return the test results.",
"input_schema": {
"type": "object",
"properties": {
"test_path": {
"type": "string",
"description": "Path to a test file or directory"
}
},
"required": ["test_path"]
}
}
]
# Tool executor: maps the model's tool_call to a real function
def execute_tool(tool_call: dict) -> str:
name = tool_call["name"]
inputs = tool_call["input"]
if name == "read_file":
try:
with open(inputs["path"], encoding=inputs.get("encoding", "utf-8")) as f:
return f.read()
except FileNotFoundError:
return (
f"File not found: {inputs['path']}\n"
"Use list_files() to see which files exist."
)
elif name == "run_tests":
import subprocess
result = subprocess.run(
["python", "-m", "pytest", inputs["test_path"], "-v"],
capture_output=True, text=True
)
return result.stdout + result.stderr
else:
return f"Unknown tool: {name}"

The Model Context Protocol (MCP) is an open protocol through which a host and external tool servers exchange capabilities and context. ACI is the set of principles for designing a tool contract that works well for a model inside one harness; MCP lets that contract cross process and product boundaries. An MCP server should therefore still follow ACI principles: clear inputs and outputs, actionable errors, and least privilege.

Treat the protocol version as part of the contract. The 2026-07-28 MCP specification covers a stateless core, multi-round trips, header routing, cacheable lists, and authorization extensions. A harness should declare the supported version, transport, and permission scope, and must not broaden access merely because a server description requests it.

The next chapter examines how tool results should be structured — and how loop state is managed across iterations.

References