Writing Effective Tool Descriptions
Complete information upfront = LLMs work efficiently without probing.
The Problem
Bad tool descriptions = LLMs waste time probing and guessing. A wrapper can be technically perfect, but if the tool descriptions are vague, LLMs will make multiple failed attempts, ask users for clarification, and misinterpret responses.
Best Practice: Always Include test_connection
ALWAYS make test_connection
your first tool. It provides:
- Quick validation that credentials work
- Network connectivity check
- Minimal resource usage (typically limit=1)
- Clear debugging when things fail
{
"name": "test_connection",
"description": "GET /endpoint?limit=1 - Test API connection and authentication\n\nMinimal call to verify credentials and connectivity\n\nRESPONSE: Single record on success\nERROR: 401 for auth failure, timeout for network issues\n\nUSE THIS FIRST to verify setup is working",
"endpoint": "/appropriate-endpoint",
"method": "GET",
"input_schema": {
"type": "object",
"properties": {}
}
}
Bad vs Good Descriptions
❌ BAD - Vague Description
{
"description": "GET /customers - List customers"
}
// Result: LLM doesn't know pagination format, defaults, limits, or response structure
✅ GOOD - Complete Description
{
"description": "GET /crm/v3/objects/contacts - List paginated contacts\n\nRESPONSE SHAPE:\nSuccess: {\"results\": [array of contacts], \"paging\": {\"next\": {\"after\": \"cursor\", \"link\": \"url\"}}}\nEmpty: {\"results\": []} with no paging field\nNever returns bare array - always wrapped in object\n\nDEFAULT BEHAVIOR:\n- limit: 10 (if not specified)\n- archived: false (only active contacts)\n- properties: Returns only firstname, lastname, email, createdate, lastmodifieddate, hs_object_id\n- after: Omit for first page\n\nRATE LIMITS: 10 req/sec, 100 req per 10 sec burst, ~250k/day\n\nPAGINATION:\n- Use returned paging.next.after for next page\n- Last page: no paging field in response\n- Cursor may expire if not used promptly\n\nERROR RESPONSES:\n- 401: {\"status\":\"error\", \"message\":\"Authentication required\", \"category\":\"AUTHENTICATION_ERROR\"}\n- 429: {\"status\":\"error\", \"message\":\"You have reached your limit\", \"category\":\"RATE_LIMIT\"}\n- 400: {\"status\":\"error\", \"message\":\"Error validating request\", \"errors\":[...], \"category\":\"VALIDATION_ERROR\"}"
}
What to Include in Every Description
Response Shape
- • Success response structure
- • Empty result format
- • Whether array or object
- • Field names and types
Default Behavior
- • Default parameter values
- • What happens when omitted
- • Which fields are returned
- • Sort order defaults
Rate Limits
- • Requests per second
- • Burst limits
- • Daily quotas
- • Retry behavior
Pagination
- • Cursor format
- • How to get next page
- • Last page indicators
- • Cursor expiration
Error Responses
- • 401 authentication format
- • 429 rate limit format
- • 400 validation errors
- • 404 not found format
Special Behavior
- • Quirks and gotchas
- • Undocumented features
- • Version differences
- • Edge cases
Research-First Approach
Before creating any MCP wrapper, conduct deep API research to gather complete information. Use this template to guide your research:
# API Deep Research Request for [SERVICE NAME]
I need you to gather COMPLETE information about the [SERVICE] API to create perfect MCP tool descriptions. Please run actual API calls and document the EXACT behavior.
## 1. Parameter Discovery (CRITICAL!)
For EACH endpoint, I need you to discover:
### Test with REAL API calls:
```bash
# Example: What happens with different parameters?
curl -X GET "https://api.example.com/items"
curl -X GET "https://api.example.com/items?limit=5"
curl -X GET "https://api.example.com/items?limit=0"
curl -X GET "https://api.example.com/items?limit=999999"
curl -X GET "https://api.example.com/items?invalid_param=test"
```
Document:
- **Exact parameter names** (is it 'message', 'msg', 'text', or 'body'?)
- **What happens when omitted** (default values)
- **Validation rules** (what makes it fail?)
- **Limits** (max length, min/max values)
- **Special formats** (date format, phone format)
## 2. Response Shape Testing
Run these scenarios and capture EXACT responses:
```bash
# Success case with data
curl -X GET "https://api.example.com/items?limit=2" | jq
# Empty result case
curl -X GET "https://api.example.com/items?filter=nonexistent" | jq
# Error cases
curl -X GET "https://api.example.com/items?limit=invalid" | jq
curl -X POST "https://api.example.com/items" -d '{}' | jq # Missing required fields
curl -X GET "https://api.example.com/items/99999999" | jq # Not found
```
Provide the COMPLETE response for each, not summaries.
Parameter Discovery
Test with REAL API calls to discover exact behavior:
Exact Parameter Names
Is it 'message', 'msg', 'text', or 'body'? Test to find out exactly.
Default Values
What happens when parameters are omitted? Document the defaults.
Validation Rules
What makes requests fail? Test boundary conditions and invalid inputs.
Format Requirements
Date formats, phone number formats, special encoding requirements.
Common Pitfalls to Avoid
❌ Assuming Documentation is Complete
Official docs often miss edge cases, undocumented parameters, and actual behavior.
❌ Not Testing Error Cases
LLMs need to know exact error formats to handle failures gracefully.
❌ Vague Pagination Instructions
"Use cursor for next page" isn't enough - show exact cursor location and format.
❌ Missing Rate Limit Info
LLMs can't optimize without knowing rate limits and quotas.
Real-World Example: HubSpot Contacts
Complete Tool Description
Endpoint: GET /crm/v3/objects/contacts
Response Success:
{"results": [...], "paging": {"next": {"after": "cursor"}}}
Response Empty:
{"results": []} (no paging field)
Defaults:
- limit: 10
- archived: false
- properties: firstname, lastname, email, createdate, lastmodifieddate, hs_object_id
Rate Limits:
- 10 requests/second
- 100 requests per 10 second burst
- ~250k requests/day
Pagination:
- Use paging.next.after for next page
- Last page has no paging field
- Cursor expires after ~10 minutes
The Result
With complete tool descriptions, LLMs can:
- ✅ Make successful API calls on the first attempt
- ✅ Handle errors appropriately without retrying blindly
- ✅ Optimize for rate limits automatically
- ✅ Navigate pagination efficiently
- ✅ Provide accurate feedback to users
- ✅ Work with the API as effectively as a human developer