Agent Tool Design
The Agent Tool Contract — 5 principles for designing tools that agents call reliably.
The 5 Principles
Principle 1: Predictable Signature
Tools must have typed, named parameters with clear required/optional distinction. No positional ambiguity.
Good:
// Clear, named, typed function searchCode({ query, limit = 20, type = 'semantic' }) { ... }
Bad:
// Positional, ambiguous function searchCode(q, n, t) { ... }
Principle 2: Rich Errors
Errors must include: error code (machine-readable), message (human-readable), context (debugging data).
Good:
throw {
code: 'FILE_NOT_FOUND',
message: File not found: ${path},
context: { path, cwd: process.cwd() },
};
Bad:
throw new Error('not found'); // No context for agent to act on
Principle 3: Token-Efficient Output
Tools return structured minimal data. No prose explanations, no redundant wrapping, no verbose status messages. Agents format output themselves.
Good:
return { files: ['a.js', 'b.js'], total: 2 };
Bad:
return { status: 'success', message: 'Found 2 files successfully', data: { files: [...], metadata: {...} } };
Rule of thumb: If the output contains prose an agent would re-read to extract facts, it's too verbose.
Principle 4: Idempotency
Tools must be safe to retry. Running a tool twice should produce the same result as running it once.
Good:
// Upsert instead of insert db.upsert({ id, ...data }); // mkdir -p instead of mkdir fs.mkdirSync(path, { recursive: true });
Bad:
// Fails on retry db.insert({ id, ...data }); // duplicate key error fs.mkdirSync(path); // EEXIST error
Principle 5: Graceful Degradation
Partial success > hard failure. Return what succeeded with a clear indication of what didn't.
Good:
return { succeeded: ['file1.js', 'file2.js'], failed: [{ file: 'file3.js', reason: 'PERMISSION_DENIED' }], partial: true, };
Bad:
// One file fails -> entire batch throws throw new Error('Failed to process file3.js');
Anti-Pattern Table
Anti-Pattern Problem Fix
Verbose status wrapping Wastes tokens; agent re-parses to extract data Return data directly
Positional args Ambiguous; breaks on refactor Named params with types
Swallowed exceptions Agent thinks success; work is lost Always surface errors explicitly
Non-idempotent mutations Retry causes duplicate data or errors Upsert semantics; check-then-set
Hard failures on partial input One bad item breaks entire batch Return partial results
Side-effect-heavy reads Read tools that trigger writes confuse agents Separate reads from writes
String error messages only Agent can't programmatically handle errors Include machine-readable error codes
Untyped return shape Agent can't reliably destructure output Document and enforce return schema
Review Checklist
Before shipping any tool:
[ ] Parameters are named (not positional) [ ] Required vs optional params are explicit [ ] All error paths return { code, message, context } [ ] Output contains no prose — only structured data [ ] Tool is idempotent (safe to retry) [ ] Partial failure returns partial results, not throws [ ] Return shape is documented in JSDoc or TypeScript types [ ] Token budget for output estimated (< 500 tokens for standard tools)
Iron Laws
-
ALWAYS use named parameters — never positional arguments in tool signatures; positional args break on refactor and create ambiguity for agents.
-
ALWAYS include machine-readable error codes — never surface plain string errors only; agents need { code, message, context } to handle errors programmatically.
-
NEVER mix reads and writes in the same tool — read tools that trigger side effects confuse agents and prevent safe retries.
-
ALWAYS design for idempotency — retry must produce the same result as the first call; use upsert semantics and mkdir -p patterns.
-
ALWAYS return partial results on partial failure — never let one failing item abort the entire batch; return { succeeded, failed, partial: true } .
Integration
-
Used by: tool-creator skill when designing new tools
-
Reviewed by: code-reviewer agent during tool PRs
-
Pairs with: dynamic-api-integration skill (consuming external tools)
-
Complements: agent-evaluation skill (evaluating tool output quality)
Memory Protocol (MANDATORY)
Before starting: Read .claude/context/memory/learnings.md
After completing:
-
New pattern -> .claude/context/memory/learnings.md
-
Issue found -> .claude/context/memory/issues.md
-
Decision made -> .claude/context/memory/decisions.md
ASSUME INTERRUPTION: If it's not in memory, it didn't happen.