Tool Schema Examples

Bad vs good tool definitions showing how schema design, descriptions, and error handling affect agent behavior.

1. Search Tool — Description and Output Structure

Bad

{ "name": "search", "description": "Search utility.", "parameters": { "input": { "type": "string" } } }

The agent has no idea whether this searches names, contents, or both. input gives no format hint. Empty return is ambiguous — no matches or error?

Good

{
  "name": "search_files",
  "description": "Search for files by name pattern in the project. Returns paths sorted by modification time. Do not use for content search — use grep_files instead.",
  "parameters": {
    "pattern": { "type": "string", "description": "Glob pattern. Examples: '**/*.ts', 'src/**/index.js'" },
    "max_results": { "type": "integer", "default": 50 }
  },
  "output": {
    "status": { "type": "string", "enum": ["ok", "error"] },
    "files": ["string"],
    "total_matches": "integer",
    "truncated": "boolean"
  }
}

Clear purpose, boundary with grep_files, structured output with truncated flag. No ambiguity.

2. Mutation Tool — Kitchen-Sink vs Separate Actions

Bad

{
  "name": "manage_user",
  "description": "Manage users in the system.",
  "parameters": {
    "action": { "type": "string", "enum": ["create", "update", "delete", "deactivate"] },
    "user_id": { "type": "string" },
    "data": { "type": "object" }
  }
}

Safety is invisible — delete and create share a description. The opaque data object gives no schema guidance per action.

Good

{
  "name": "create_user",
  "description": "Create a new user. If email exists, returns existing user (idempotent).",
  "parameters": {
    "email": { "type": "string" },
    "display_name": { "type": "string" },
    "role": { "type": "string", "enum": ["viewer", "editor", "admin"], "default": "viewer" }
  }
}
{
  "name": "delete_user",
  "description": "Permanently delete a user and all data. Cannot be undone. Use deactivate_user to disable without data loss.",
  "parameters": {
    "user_id": { "type": "string" },
    "confirm": { "type": "boolean", "description": "Must be true. Safety check." }
  }
}

Separate tools, separate safety profiles. create_user is idempotent, delete_user is destructive with a confirmation gate and a pointer to a safer alternative.

3. Data Retrieval — Unfiltered Dump vs Paginated and Filtered

Bad

{ "name": "get_logs", "description": "Get logs.", "parameters": { "source": { "type": "string" } } }

Returns the entire log as a single string — could be 5 lines or 500,000. No scope control, no filtering, no metadata.

Good

{
  "name": "get_logs",
  "description": "Retrieve log entries filtered by severity and time. Newest first. For full export use export_logs_to_file.",
  "parameters": {
    "service": { "type": "string" },
    "severity": { "type": "string", "enum": ["debug","info","warn","error","fatal"], "default": "info" },
    "since_minutes": { "type": "integer", "default": 60 },
    "max_entries": { "type": "integer", "default": 100 },
    "contains": { "type": "string", "description": "Substring filter" }
  },
  "output": {
    "status": "string",
    "entries": [{"timestamp": "...", "severity": "...", "message": "..."}],
    "total_matching": "integer",
    "returned": "integer"
  }
}

Agent controls scope with five parameters. total_matching vs returned tells the agent if there are more results.

4. Error Handling — Raw Strings vs Structured Errors

Bad

The tool returns error information as plain strings:

"Error: ECONNREFUSED 10.0.0.5:5432 - connection refused"

Or stack traces:

"Traceback (most recent call last):\n  File \"db.py\", line 42...\npsycopg2.OperationalError: could not connect to server"

The agent cannot distinguish connection errors from permission errors from syntax errors. Stack traces waste tokens.

Good

{
  "status": "error",
  "error": {
    "code": "connection_failed",
    "message": "Cannot connect to database at 10.0.0.5:5432",
    "category": "transient",
    "recoverable": true,
    "retry_after_seconds": 5,
    "suggestion": "Database may be starting up. Retry in a few seconds."
  }
}

recoverable and category let the agent decide programmatically. retry_after_seconds gives a specific wait. suggestion provides a fallback strategy. No stack traces, no implementation internals.