Tool-Aware Agents: Giving AI Real Access to Git, Tests, and CLIs
A model that can only produce text has to describe what it would do. A tool-aware agent actually does it — runs git diff, executes the test suite, calls a package manager, queries a cloud API — and reads the real output back before deciding on its next step. That shift, from describing actions to taking them, is what separates a chat assistant from an agent.
The Core Mechanism: Structured Tool Calls
Modern models don’t call tools by guessing shell syntax in free text — they’re given a structured schema for each tool and emit a structured call, which your system executes and returns as structured output. This matters because it lets you validate, scope, and log every action the agent takes.
{ "name": "run_tests", "description": "Run the project's test suite, optionally scoped to a path", "parameters": { "type": "object", "properties": { "path": { "type": "string", "description": "File or directory to test, defaults to all" }, "fail_fast": { "type": "boolean", "default": false } }, "required": [] }}The model emits run_tests({"path": "src/billing/", "fail_fast": true}), your system executes it inside the sandbox described in Agent Runtime Environments, and returns the real test output — pass/fail counts, stack traces, coverage delta — for the model to reason about.
The Standard Tool Set for a Coding Agent
| Tool category | Example actions | Why the agent needs real output, not a guess |
|---|---|---|
| Git | diff, commit, branch, blame | Can’t safely propose a change without seeing the actual current state |
| Test runners | run unit/integration tests | Verification requires ground truth, not a plausible-sounding claim |
| CLIs | linters, formatters, build tools | Enforces project conventions the model wasn’t trained on |
| Package managers | install, audit, update | Dependency resolution has real constraints a model can’t simulate reliably |
| Cloud tools | deploy, provision, query logs | Real infrastructure state changes with real consequences |
| Security scanners | SAST, secret detection, SCA | Ground-truth vulnerability data, not a model’s best guess |
Model Context Protocol and Standardized Tool Interfaces
The Model Context Protocol (MCP) formalized what many teams were building ad hoc: a standard way for an agent to discover which tools are available, what parameters they take, and what permissions they require, without hardcoding integrations per model or per vendor. An MCP server exposing “git,” “filesystem,” and “database” tools can be reused across any MCP-compatible agent, which is a meaningfully different integration story than writing custom function-calling glue for every model provider.
Designing Tool Schemas That Don’t Get Misused
The most common failure isn’t the model refusing to use a tool — it’s using an overly broad tool in an unsafe way. Two design habits prevent most of this:
Scope tools narrowly. A run_shell(command: string) tool that accepts arbitrary shell strings is nearly impossible to secure. A run_tests(path), git_commit(message, files), and install_package(name, version) tool set is far easier to validate, log, and rate-limit individually.
Make dangerous actions explicit, not implicit. A deploy_to_production tool should be a distinct, clearly-labeled call — not a side effect of a generic run_cli tool that happens to accept a deploy command. This is what makes governance and human-in-the-loop approval enforceable: you can gate a specific tool, but you can’t gate “whatever string this model decided to run.”
Where to Start
If you’re adding tool use to an existing assistant, start with read-only tools (git diff, test results, log queries) before granting write access (commits, deploys, installs). Read-only tool use gets you most of the accuracy improvement — an agent that can actually see the test failure writes a better fix than one guessing at it — with none of the blast radius of write access, and it’s the fastest way to build confidence in the pattern before extending it.