Agent Tool Design skill for Codex

Tools a model picks right and calls right first time. Codex designs agent tools around user tasks rather than raw endpoints, writes descriptions that say when not to use a tool, and turns closed sets into enums under strict schemas. Outputs are capped and paginated with truncation hints, errors tell the model how to recover, destructive tools sit behind human confirmation, and a tool-selection eval proves the descriptions work.

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- agent-tool-design

Installs to ~/.agents/skills/agent-tool-design/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $agent-tool-design, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Designs the tools, function schemas and MCP tools that LLM agents call - naming, descriptions, JSON Schema parameters with enums and strict mode, granularity, response shaping and pagination, actionable error messages, idempotency, read-only vs destructive annotations and confirmation gates - and evaluates tool selection accuracy. Use when writing function-calling definitions or MCP server tools, when an agent picks the wrong tool or passes bad arguments, when tool outputs flood the context window, or when reviewing a toolset before release.

Use it when

  • You are writing function-calling or MCP tool definitions
  • An agent picks the wrong tool or passes invalid arguments
  • Tool results overflow the context window
  • A toolset includes delete, send, pay or deploy actions

Use something else when

How to check the result

A tool-selection eval of 30 or more real requests reaches the target accuracy with stubbed execution, and every destructive tool requires confirmation in a test.

The mistake it prevents

Exposing dozens of overlapping, endpoint-shaped tools, then trying to fix wrong tool choices with longer system prompts instead of better descriptions.

~/.agents/skills/agent-tool-design/SKILL.md

--- name: agent-tool-design description: Designs the tools, function schemas and MCP tools that LLM agents call - naming, descriptions, JSON Schema parameters with enums and strict mode,… license: MIT ---

Done well means: an agent with no extra prompting picks the right tool from its name and description, fills arguments correctly on the first try, gets back a compact result it can act on, recovers from errors because the message says what to do next, and cannot perform a destructive action without a confirmation step.

Tools are a user interface whose user is a model. Design them for that reader.

Workflow

- [ ] 1. List the user tasks the agent must complete (5-15 real requests)
- [ ] 2. Map each task to the fewest tool calls; that defines the toolset
- [ ] 3. Write names, descriptions and schemas (rules below)
- [ ] 4. Shape outputs: fields the next step needs, IDs for follow-ups, truncation hints
- [ ] 5. Write error messages that name the fix
- [ ] 6. Classify each tool: read-only, idempotent write, destructive/outward-facing
- [ ] 7. Build a tool-selection eval and run it; iterate on descriptions, not prompts

Granularity: design around tasks, not endpoints

SmellBetter
list_users, then get_user per ID, then get_orders per usersearch_customers(query, include=["orders"])
One api_request(method, path, body) toolNamed tools per task with typed parameters
60 tools exposed at once5-20 per agent; group by role or load per task (tool search, active tool subsets, MCP server per domain)
Two tools whose descriptions overlap (“search_docs”, “find_docs”)Merge, or state crisply when to use each

A model choosing between 40 similar tools makes more mistakes than one choosing among 8 distinct ones.

Names and descriptions

  • Name: verb_noun in snake_case, specific (create_calendar_event, not calendar). Prefix by service when tools from several systems coexist (github_create_issue, linear_create_issue).
  • Description, in this order: what it does; when to use it (and when to use another tool instead); what it returns; important limits. 1-4 sentences.
  • Put formats in the description and schema: “Dates are ISO 8601 (2026-03-01)”, “IDs look like ORD-1234”.
{
  "type": "function",
  "name": "search_orders",
  "description": "Search a customer's orders by status and date range. Use for questions about order history; use get_order for one known order ID. Returns at most `limit` orders, newest first, with order_id, status, total and created_at.",
  "strict": true,
  "parameters": {
    "type": "object",
    "properties": {
      "customer_id": { "type": "string", "description": "Customer ID like CUS-123" },
      "status": { "type": ["string", "null"], "enum": ["pending", "shipped", "delivered", "cancelled", null] },
      "created_after": { "type": ["string", "null"], "description": "ISO date, inclusive" },
      "limit": { "type": "integer", "description": "1-50, default 10 when unsure" }
    },
    "required": ["customer_id", "status", "created_after", "limit"],
    "additionalProperties": false
  }
}

Parameter schema rules

  • Enums for every closed set. Free text where an enum fits is the top cause of bad arguments.
  • With strict mode (OpenAI strict: true), every property is required and additionalProperties: false; express optional as a nullable type. Details: $openai-function-calling and $openai-structured-outputs.
  • Flat over nested. Two levels deep at most.
  • No parameters the model cannot know (internal DB IDs it never saw, auth tokens, tenant IDs). Inject identity and tenancy server-side from the session; never accept them as arguments.
  • Units in names: timeout_seconds, amount_cents.
  • Validate again in the handler: schemas constrain shape, not authorisation or business rules.

Output shaping

RuleWhy
Return only fields the next step needsContext window is the agent’s working memory
Always include stable IDs for follow-up callsAvoids a second lookup
Paginate: limit param, return next_cursor and totalLarge lists flood context
Truncate long text with an explicit marker: "…[truncated 12,400 chars; call get_document(id, offset=4000)]"Model knows more exists and how to get it
Human-readable values over codes ("status": "shipped" not 3)Fewer misreadings
Consistent shape across toolsFewer parsing mistakes
Cap any tool result (e.g. 20k characters)One huge result can push instructions out of context

Tool output is untrusted input to the model: web pages, emails and tickets can contain injected instructions. Mark it as data and see $prompt-injection-defense.

Error messages

Errors are returned to the model as tool results, so write them for recovery:

def get_order(order_id: str) -> dict:
    if not ORDER_ID.fullmatch(order_id):
        return {"error": "invalid_order_id",
                "message": f"'{order_id}' is not an order ID. IDs look like ORD-1234; "
                           "use search_orders to find one by customer."}
    order = repo.get(order_id, tenant=current_tenant())
    if order is None:
        return {"error": "not_found", "message": f"No order {order_id} for this customer."}
    return order.to_agent_view()
  • Say what was wrong and the next action. Never return a raw stack trace (leaks internals, wastes tokens).
  • Distinguish retryable (rate_limited, timeout, include retry_after_seconds) from permanent errors so the agent stops retrying the impossible.

Safety classification

ClassExamplesRequired handling
Read-onlysearch, get, listNone beyond auth and tenancy
Idempotent writeupsert profile field, set statusIdempotency key or natural key; safe to retry
Destructive or outward-facingdelete, refund, send email/message, deploy, publish, payHuman confirmation outside the model; show exact action and target first
  • MCP tools: set annotations readOnlyHint, destructiveHint, idempotentHint, openWorldHint honestly; clients use them for approval UX. Annotations are hints, so the server must still enforce permissions.
  • Split risky operations: draft_email (safe) and send_email(draft_id) (gated) is easier to approve and audit than one send_email(to, body).
  • Destructive tools take explicit targets (invoice_id), never filters like “all overdue”.

Evaluate tool selection

Build 30-100 cases of {request, expected_tool, key_args} from real tasks, including near-misses between similar tools and requests that should use no tool. Run the agent with tool execution stubbed, then score:

  • tool selection accuracy (correct first tool),
  • argument validity (schema + business validation pass),
  • calls per task (fewer is better at equal success),
  • refusals or confirmations on destructive cases.

Change descriptions and schemas, rerun, compare. Treat the toolset like an API: version it and keep the eval in CI ($llm-evals).

Gotchas

  • Descriptions that say “ALWAYS use this tool” make the model overuse it. State the condition.
  • Optional parameters with silent defaults hide mistakes; make defaults explicit in the description.
  • Returning success for partial failures ({"ok": true} when 3 of 10 emails bounced) makes the agent report false completion. Return per-item results.
  • Timeouts: long tools should return a job ID and offer get_job_status, not block for minutes.
  • Renaming a tool breaks saved conversations and evals; keep aliases during a transition.

Review checklist

- [ ] Every tool maps to a real user task; no overlapping pairs without "use X instead" text
- [ ] Names verb_noun, descriptions state when/when-not, return shape, limits
- [ ] Closed sets are enums; strict schemas where supported; no tenant/auth params
- [ ] Outputs capped, paginated, IDs included, truncation explained
- [ ] Errors actionable, retryable vs permanent marked
- [ ] Destructive/outward tools gated by human confirmation and take explicit targets
- [ ] Selection eval: accuracy >= target (e.g. 95%) on N cases, run in CI

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Agent Tool Design"
  short_description: "Tools a model picks right and calls right first time"
  default_prompt: "$agent-tool-design Our agent keeps calling search_docs when it should call get_ticket; fix the tool definitions"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem