---
name: agent-tool-definition-style
source: https://app.decimal.ai/s/agent-tool-definition-style@1/SKILL.md
source_sha256: 96fbb4dd00c3
---

# Agent Tool Definition House Style

## Contract

When you write a tool (function signature + docstring) that an LLM agent will call,
or a prose line telling an agent which MCP tool to use, emit it in this exact house
style. These are arbitrary conventions, not general advice — conform literally.

## Rules

### 1. Tool naming
- The tool name is `verb_noun` in lowercase snake_case: `get_customer`,
  `create_order`, `cancel_subscription`, `refund_payment`, `schedule_appointment`.
- The verb comes first. Never noun-first (`customer_get`), never camelCase
  (`getCustomer`), never PascalCase (`GetCustomer`), never a bare noun (`customer`).

### 2. Identifier parameters
- Every identifier parameter is named `<entity>_id`: `customer_id`, `order_id`,
  `invoice_id`, `user_id`, `ticket_id`, `payment_id`.
- Never a bare `id`, never `identifier`, never `customerId` (camelCase), never `pk`.

### 3. Docstring sections, in this exact order
The docstring has exactly these five parts, always in this order:
1. **One imperative line** stating what the tool does ("Retrieve a customer
   record by ID."). Never "Helps with…", never "Can be used for…", never a
   noun phrase ("Customer retrieval.").
2. **`Use when:`** — a section literally headed `Use when:` with 2–4 bullet
   trigger conditions (when an agent should reach for this tool).
3. **`Args:`** — every parameter listed with its type AND a concrete format
   example (not just "the customer id" — `Format "CUST-######", e.g. "CUST-000042"`).
4. **`Returns:`** — a description of the output shape.
5. **`Errors:`** — the named error codes (see rule 5).

### 4. Read tools take a `format` parameter
- Every read/retrieve/get/lookup/fetch tool takes `format: str = "concise"`.
- The only two accepted values are `"concise"` and `"detailed"`. The default is
  `"concise"`. Never invent a third value (`"summary"`, `"full"`, `"verbose"`),
  never default to `"detailed"`, never use a bool like `verbose=False`.

### 5. Error codes
- Each error is a named `UPPERCASE_SNAKE` code drawn from this vocabulary:
  `NOT_FOUND`, `INVALID_FORMAT`, `MISSING_FIELD`, `UNAUTHORIZED`.
- Each error line states what the agent should change before retrying.
- An error message must include the offending value AND a concrete example of a
  valid value. Never just "failed", "error", or "invalid input".

### 6. MCP tool references
- When prose (a system prompt, an instruction) tells an agent to call an MCP
  tool, write the fully qualified `ServerName:tool_name` with a colon:
  `BigQuery:bigquery_schema`, `GitHub:create_issue`.
- Never the bare `bigquery_schema`. Never a dot (`BigQuery.bigquery_schema`),
  never a slash (`BigQuery/bigquery_schema`).

## Worked examples (BEFORE → AFTER)

### Rule 1 — naming
BEFORE (base default — noun-first or camelCase):
```python
def customerGet(id): ...
def customer_lookup(id): ...
```
AFTER (house style):
```python
def get_customer(customer_id: str, format: str = "concise"): ...
```

### Rule 2 — identifier parameter
BEFORE:
```python
def get_order(id: str): ...
def get_order(identifier: str): ...
```
AFTER:
```python
def get_order(order_id: str, format: str = "concise"): ...
```

### Rule 3 — docstring shape
BEFORE (free-form prose, no fixed sections):
```python
def get_invoice(invoice_id):
    """Look up an invoice. Pass the invoice id and it returns the invoice."""
```
AFTER:
```python
def get_invoice(invoice_id: str, format: str = "concise"):
    """
    Retrieve an invoice by ID.

    Use when:
    - The user asks about a specific invoice's amount or status.
    - Invoice context is needed before issuing a refund.

    Args:
        invoice_id: Format "INV-######", e.g. "INV-000042".
        format: "concise" (key fields) or "detailed" (full record). Default "concise".

    Returns:
        Invoice object with the requested fields.

    Errors:
        NOT_FOUND: No invoice with that id; verify the id and retry.
        INVALID_FORMAT: id must match INV-######, e.g. "INV-000042".
    """
```

### Rule 4 — format parameter
BEFORE:
```python
def get_account(account_id, verbose=False): ...
def get_account(account_id, mode="full"): ...
```
AFTER:
```python
def get_account(account_id: str, format: str = "concise"): ...
```

### Rule 5 — error codes
BEFORE (prose, no recovery signal):
```python
return {"error": "Customer lookup failed."}
```
AFTER:
```python
return {
    "code": "INVALID_FORMAT",
    "message": "order_id '12345' is not valid; expected CUST-######, e.g. 'CUST-000042'. "
               "Reformat the id and retry.",
}
```

### Rule 6 — MCP reference
BEFORE:
```text
Use the bigquery_schema tool to fetch table schemas.
```
AFTER:
```text
Use the BigQuery:bigquery_schema tool to fetch table schemas.
```

## Edge cases & exceptions

- **Write/action tools still take no `format` param.** Rule 4 is for reads only.
  `create_order`, `cancel_subscription`, `refund_payment` do NOT get a `format`
  parameter — only get/retrieve/lookup/fetch tools do.
- **Multi-word entities** keep the `<entity>_id` shape: a calendar event's id is
  `event_id` (or `calendar_event_id`), a support ticket's is `ticket_id`. The verb
  may have a noun suffix too (`update_customer_email`) — still verb-first snake_case.
- **A tool with no identifier** (e.g. `send_notification` keyed on a recipient)
  still names that recipient `user_id`, not `to` or `recipient`.
- **A non-standard error** that isn't in the four-code vocabulary: still emit an
  `UPPERCASE_SNAKE` code (e.g. `RATE_LIMITED`), with the same recovery + example rule.
- **`Use when:` with a single trigger** is too thin — give 2–4 bullets. If the tool
  truly has one trigger, it is probably over-narrow; consolidate, but still write ≥2.

## Do / Don't

- DO name tools `verb_noun`. DON'T put the noun first or use camelCase.
- DO name id params `<entity>_id`. DON'T use bare `id` or `identifier`.
- DO write a literal `Use when:` heading. DON'T fold triggers into the first line.
- DO give a concrete format example for every `Args` parameter. DON'T write
  "the customer id" with no example.
- DO add `format: str = "concise"` to read tools. DON'T use `verbose=`, `mode=`,
  or default to `"detailed"`.
- DO use `UPPERCASE_SNAKE` error codes with a fix + valid example. DON'T return
  `"failed"` or `"invalid input"`.
- DO qualify MCP tools as `ServerName:tool_name`. DON'T reference the bare tool name.

## Common mistakes (the base's wrong defaults)

1. Names the tool after the noun (`customer_lookup`, `CustomerGet`) instead of
   verb-first `get_customer`.
2. Uses a bare `id` parameter instead of `customer_id`.
3. Writes a free-form docstring with no `Use when:` heading and no fixed section
   order.
4. Omits the `format` parameter on read tools, or invents `verbose`/`mode`/a third
   value.
5. Describes errors in prose ("if it fails, the customer wasn't found") instead of
   `UPPERCASE_SNAKE` named codes with a valid-value example.
6. References an MCP tool by bare name (`bigquery_schema`) with no `ServerName:`
   prefix.

## Quick checklist
- [ ] Name is `verb_noun` snake_case.
- [ ] Id param is `<entity>_id`, never bare `id`.
- [ ] Docstring order: imperative line → `Use when:` → `Args:` → `Returns:` → `Errors:`.
- [ ] Read tool has `format: str = "concise"` (only concise/detailed).
- [ ] Errors are `UPPERCASE_SNAKE` with a fix + concrete valid example.
- [ ] MCP refs are `ServerName:tool_name` with a colon.
