Agent tools should be small application capabilities, not general-purpose escape hatches. A tool named ApproveInvoice is easier to reason about than RunSql. A tool named SearchCustomerTickets is safer than CallInternalApi.
Narrow tools reduce ambiguity for the model and reduce blast radius for the application. The model gets fewer choices, smaller schemas, and clearer descriptions. The application gets a tool surface that can be validated, authorized, logged, tested, and reviewed like normal code.
For example, avoid one broad execution tool:
using System.ComponentModel;
public sealed class DatabaseTools
{
[Description("Executes a SQL query against the database.")]
public Task<string> RunSqlAsync(string query)
{
// ...
}
}
Prefer narrow, business-aligned capabilities:
using System.ComponentModel;
public sealed class InvoiceTools
{
[Description("Approves an invoice by its ID.")]
public Task ApproveInvoiceAsync(
[Description("The exact ID of the invoice to approve.")] int invoiceId)
{
// ...
}
}
Boring is a feature here. Prefer explicit parameters, predictable return shapes, short descriptions, and business-level verbs. Avoid complex nested JSON inputs when a simple primitive such as int invoiceId is enough. Avoid tools that expose shell access, arbitrary HTTP requests, broad database access, file system access, or “do anything” admin operations unless they are isolated behind strong policy and approval.
This also applies to MCP toolsets. Do not pass every tool from a server into every agent just because discovery makes it easy. Select the tools the agent actually needs for the workflow, and keep privileged operations behind separate authorization or approval boundaries.
The goal is not to make the agent less useful. The goal is to make each tool call legible enough that a developer, operator, or reviewer can understand what could happen before it happens.