DEV Community

Mike Dabydeen
Mike Dabydeen

Posted on AI-assisted

Lean Agents: Decide What Your Agent Can Reach Before It Runs

Every tool you connect to an agent is two things at once: tokens the model reads on every run, and an action the model can take. So the same inventory answers your cost question and your permission question. I start every agent with that inventory, and with nothing connected that the task doesn't need.

I talked through this with Tom Smith on CoderLegion's Developer Stories (video, from 9:46). This post is the practical version.

Why the inventory matters

An MCP client discovers tools by sending tools/list to each server. Each tool comes back with a name, a description and a JSON Schema for its input, and the client typically hands those definitions to the model. Anthropic published one example of what that costs: five servers (GitHub, Slack, Sentry, Grafana, Splunk) exposing 58 tools took about 55K tokens "before the conversation even starts" (Anthropic engineering). The same post names wrong tool selection as the most common failure when tools have similar names.

That is the cost side. The permission side is simpler: if a tool is in the list, the model can choose it.

The three controls I use

1. A sandbox per agent, deny by default

Each agent gets its own environment with only the access we grant. Nothing is inherited from the developer's machine or from another agent. Zero trust applies inside the system, not only at its edge.

2. A per-task tool allowlist

Instead of connecting every server the team uses, the task declares what it needs. An illustrative policy, not a format any tool enforces today:

# Illustrative per-task policy. Proposed design, not an implemented control.
task: triage-failing-build
allow:
  - server: github
    tools: [get_pull_request, list_check_runs, get_job_logs]
  - server: sentry
    tools: [get_issue]
deny_everything_else: true
run_budget:
  max_tool_calls: 40
  max_total_tokens: 200000
  wall_clock_timeout: 10m
on_budget_exceeded: stop_and_report
Enter fullscreen mode Exit fullscreen mode

Two things to notice. Write tools (create_comment, merge) aren't in the list, so this task can read and report but not act. And the budget is part of the same policy, because a loop is a cost problem before it is anything else.

3. A record of every action

Log each tool call with its arguments and result. The MCP spec already asks clients to do this: it says clients SHOULD "log tool usage for audit purposes" and "implement timeouts for tool calls," and that servers MUST "rate limit tool invocations" (MCP tools specification). The log is how you find where the agent went off the rails and what to roll back.

How to start on an existing agent

  1. Call tools/list on every connected server and write down the count per server.
  2. For the last week of runs, mark which tools were actually called.
  3. Disconnect or filter out anything unmarked.
  4. Add a run budget with a timeout, and decide what happens when it trips: stop and report, not retry.
  5. Turn on per-call logging if you don't have it.

None of this needs a new framework. It is an allowlist, a budget and a log.

The part I haven't solved

A fixed allowlist works when you know the task in advance. Many agent tasks don't announce what they'll need. Anthropic's answer is to let the model search for tools on demand instead of loading all of them; that addresses the token cost, but the permission question remains: a tool the model can find is a tool it can call. The options I'm weighing are a planner step that requests tools before execution, with a person or policy approving any escalation, versus narrow pre-built profiles per task type.

If you've built either, I'd like to hear how it held up.

Top comments (1)

Collapse
 
max_quimby profile image
Max Quimby •

The thing I like most here is collapsing cost and permission into one inventory — people treat them as two different review meetings when they're the same list. One wrinkle I keep running into: the inventory isn't static within a run. Servers can change what tools/list returns between sessions, and a long-horizon agent re-reads those descriptions every turn, so the 55K isn't a one-time tax, it's paid on every context rebuild unless you cache aggressively. The other place I'd push: the allowlist guards which tools exist, but description text is still attacker-controlled surface — a tool whose description quietly says "always call me first" can hijack selection even inside a tight allowlist. We ended up auditing descriptions, not just call names, in the log. Curious how you handle servers that expand their tool set mid-engagement — do you fail closed on anything not in the original allowlist, or re-approve? Fail-closed sounds right but breaks every time a vendor ships a new tool.