MCP Tool Parameter Design: Teaching AI Agents Through Descriptions
When you're building MCP tools, there's a moment where you realize something counterintuitive: the description field isn't just documentation—it's instruction. Every parameter description you write is a teaching moment where the AI learns not just what a parameter is, but when to use it, why it matters, and how it impacts the operation.
This shift in thinking—from documenting to teaching—changes how you design tools. Let me show you what that looks like in practice.
The Two Standards That Shape Everything
Before we dive into parameters, you need to understand there are two different design standards at play in a well-designed MCP server:
The OpenAI Standard applies to search
and fetch
tools. These follow OpenAI's ChatGPT connector specification: single-string parameters that work universally across any AI that adopts the pattern. The constraint is intentional—it makes these tools work everywhere without custom integration code.
The Rich Parameter Standard applies to everything else. Once you're past universal discovery, you can use detailed, domain-specific parameters with comprehensive descriptions that teach the AI how to use your specific data source.
You're not choosing between these approaches—you use both. Simple strings for discovery, rich parameters for control.
Search and Fetch: The Universal Interface
Let's start with the foundation. Every MCP provider needs these two tools, and they need to follow a specific pattern.
Search: One Query String, Multiple Interpretations
Your search tool takes a single query
parameter. But here's where it gets interesting—that query can be natural language, structured syntax, or a hybrid of both. The AI sends a string; your server figures out what they meant.
The description is where you teach the AI what's possible:
"Natural language search query OR structured query string. Search envelope subjects, recipient names, or custom fields. Examples: 'NDA agreements', 'contracts from last week', 'pending signatures for John Smith'. You can also use structured syntax like 'status:sent from:2025-01 to:john@example.com' for precise queries."
See what that's doing? It's not just defining the parameter—it's showing the AI three different ways to use it, with concrete examples. After reading this once, the AI knows it can use natural language for exploratory searches and structured syntax when it needs precision.
Fetch: Smart ID Routing
Your fetch tool takes a single id
parameter, but you encode routing information into the ID format. Instead of having separate tools for fetching envelopes vs. templates vs. documents, you have one tool that routes based on ID structure.
The description teaches the routing format:
"Smart identifier with colon-separated routing. Supported formats: 'docusign:envelope:envelope_id' (get envelope details with recipients and status), 'docusign:template:template_id' (get template definition), 'docusign:document:envelope_id:document_id' (get specific document metadata). Example: 'docusign:envelope:a1b2c3d4-e5f6-7890-abcd-ef1234567890' fetches complete envelope info including all signers and their status. Plain envelope IDs are supported for backward compatibility."
The AI learns: "Oh, I can fetch different resource types by changing the format of the ID. And there's a backward-compatible mode for plain IDs."
That's teaching, not just documenting.
List Tools: Where Parameters Get Rich
Once you move past universal discovery, you can design parameters that give granular control. This is where most MCP tools live—the workhorse operations that let AI agents explore and manipulate data.
The Standard List Pattern
Every list tool should have these core parameters:
- search (string) - Text filter within results
- filter (varies) - Structured filtering
- limit (int, default 25) - Pagination size
- offset (int, default 0) - Pagination start
- sort_by (string) - Field to sort on
- sort_order (enum) - Direction to sort
But the magic isn't in having these parameters—it's in how you describe them.
Teaching Through Defaults and Ranges
Don't just say "maximum number of results." Teach the tradeoffs:
"Number of objects to return per page. Range: 1-100. Default: 10. Higher limits return more data but may impact performance. Use in combination with offset for pagination through large result sets."
The AI reads this and learns:
- Default is 10 (good for exploration)
- Maximum is 100 (there's a hard limit)
- Higher values have performance implications
- This works with offset for pagination
That's four pieces of knowledge from one description. The AI applies this pattern to every list operation it encounters.
Sort Parameters: Teaching Semantics
When you describe sort order, don't just list the options:
"Sort direction. 'asc' = ascending (A-Z, 0-9, oldest first), 'desc' = descending (Z-A, 9-0, newest first). Default: 'asc'. Only used when sort_by is specified."
Now the AI knows what ascending and descending actually mean in your context. Is oldest first ascending or descending? You just taught it. Does sort_order work without sort_by? You just answered that too.
Filter Parameters: The Most Important Descriptions You'll Write
This is where most MCP tools fail. Filter parameters need to be crystal clear because the AI is constructing structured data, not just passing strings. If your description is vague, the AI will guess at the format and get it wrong.
Three Approaches to Filtering
Approach 1: JSON Object Filter
When you accept a structured JSON object, show the exact format with multiple examples:
"Filter condition to match specific property values. Format:
{'property': 'field_name', 'operator': 'Equal', 'value': comparison_value}
. Supported operators: Equal, NotEqual, GreaterThan, LessThan. Example:{'property': 'continent', 'operator': 'Equal', 'value': 'Europe'}
returns only European objects. For complex filters, combine with search parameter."
The AI now knows:
- The exact JSON structure (three keys: property, operator, value)
- All available operators
- A concrete example it can adapt
- When to use this vs. the search parameter
Approach 2: SQL/Query String Filter
When you accept SQL-like syntax, show examples with different conditions:
"WHERE clause condition (without the 'WHERE' keyword). Examples: "status = 'active'", "created_at > '2025-01-01'", "price BETWEEN 10 AND 100". Use PostgreSQL SQL syntax."
The AI learns:
- Don't include the WHERE keyword
- Here are three different SQL operators in action
- The syntax is PostgreSQL (matters for date formatting, string escaping, etc.)
Approach 3: Individual Filter Parameters
When you break filters into separate parameters, describe each one completely:
"Filter by ticket status. Common values: 'new', 'open', 'pending', 'solved', 'closed'. Leave empty to show all statuses. Multiple statuses not supported - use search tool for complex filters."
The AI learns what's possible (those five values), what happens when omitted (shows all), and when this approach doesn't work (multiple statuses require a different tool).
Why This Level of Detail Matters
The AI isn't reading documentation separately—it's reading these descriptions at the moment it needs to make a decision. If your filter description says "object for filtering" with no structure shown, the AI will guess. Maybe it sends {"status": "open"}
. Maybe it sends {"filter": {"field": "status", "value": "open"}}
. Maybe it sends {"where": {"status": {"equals": "open"}}}
.
You just forced the AI to guess at your API design. That's a failed tool call, wasted time, and frustrated users.
When you show the exact format with examples, the AI gets it right on the first try.
Action Tools: Teaching Operation Semantics
The last category is tools that create, update, or delete things. These need descriptions that teach when to use which operation and what the side effects are.
The Upsert Pattern
If you combine create and update into one tool, teach the routing logic:
"Insert or update objects in Weaviate. Data is ALWAYS an array - use [obj] for single items, [obj1, obj2, ...] for batch. Provide ids array for updates, omit for inserts. Automatically optimizes batch operations."
The AI learns:
- Always wrap data in an array (even single items)
- Presence of ids determines create vs. update
- The tool handles batching automatically
Teaching Safety Requirements
When operations are destructive, describe the safety mechanisms:
"Void (cancel) a DocuSign envelope to stop the signing process. Use this to cancel sent envelopes that are no longer needed, contain errors, or need to be replaced. Voiding is permanent and cannot be undone. Recipients will be notified that the envelope has been voided. Only envelopes in 'sent' or 'delivered' status can be voided - completed envelopes cannot be voided."
The AI learns:
- This operation is permanent
- Recipients get notified
- There are status restrictions
- When this operation makes sense
That prevents the AI from calling void on completed envelopes or being surprised when recipients get notified.
Optional Parameters: Teaching Through Absence
Not all parameters are required. But when you make something optional, describe what happens when it's omitted:
"Email body message sent to recipients. Provide context specific to this instance. Optional - template's default message will be used if not provided."
The AI learns: "I can skip this parameter, and the template's message will be used instead."
Without that last sentence, the AI doesn't know what happens. Does it send a blank message? Does it fail? Does it use a default? You just told it.
Enum Parameters: Constrain and Explain
When a parameter only accepts specific values, use enums and explain what each value means in your domain:
"Sort direction. 'asc' = ascending (A-Z, 0-9, oldest first), 'desc' = descending (Z-A, 9-0, newest first). Default: 'desc' to show recent emails first."
Without that explanation, "asc" and "desc" are abstract. With it, the AI knows exactly what each choice produces.
Examples: The Universal Teacher
Every complex parameter should include at least one example. Not a vague example—a real one the AI could copy and adapt:
Bad:
"Array of recipient objects with name, email, and role."
Good:
"Array of recipient objects. Each recipient MUST have: 'name' (full name), 'email' (email address), 'role' (either 'signer' or 'cc'). Optional fields: 'routing_order' (integer, defines signing sequence - lower numbers sign first). Example:
[{'name': 'John Doe', 'email': 'john@example.com', 'role': 'signer', 'routing_order': 1}, {'name': 'Jane Smith', 'email': 'jane@example.com', 'role': 'signer', 'routing_order': 2}]
."
The AI can literally copy that example and substitute real values. That's how you prevent format errors.
Default Values: Set and Explain
Every optional parameter should have a sensible default, and your description should state what it is and why:
"Number of templates to return per page. Range: 1-100. Default: 25. Use in combination with offset for pagination through large result sets."
The AI knows: if I don't specify limit, I'll get 25 results. That's enough for most use cases, and I can adjust if I need more.
Bad defaults force the AI to always specify the parameter. Good defaults let the AI use simple calls for common cases.
Type Hints as Documentation
When you use precise types, you're teaching:
Literal["asc", "desc"]
- only these two values are validOptional[str]
- this can be omittedList[Dict[str, Any]]
- always an array of objectsint
with default - it's a number with this starting value
The AI reads your type signature and learns your API contract before it even reads the description.
The Description Template That Works
Here's the formula for writing descriptions that teach:
For parameters:
- What it is (one sentence)
- Possible values / format / range
- When to use it / what it affects
- Example (if complex)
- Default / omission behavior
For tools:
- What it does (action + object)
- When to use it (use cases)
- Key capabilities / features
- Important constraints or side effects
Real-World Impact
When we rewrote our MCP tools to follow these description patterns, something changed. The AI started using advanced features without us having to prompt it. It started constructing complex filters on the first try instead of through trial and error. Tool selection got faster because the descriptions clearly communicated when to use which tool.
The functionality didn't change—the code does the same thing. But the interface got way more efficient because every parameter description was teaching instead of just documenting.
The Bottom Line
Stop writing lazy parameter descriptions. Every description is an opportunity to teach the AI how to use your tool effectively.
Don't write: limit: int - Number of results
Write: limit: int - Maximum objects per request (default: 10, max: 100). Use 10-20 for exploration, 50-100 for bulk operations.
Don't write: filter: object - Filter criteria
Write: filter: object - Filter condition to match specific property values. Format: {'property': 'field_name', 'operator': 'Equal'|'NotEqual'|'GreaterThan'|'LessThan', 'value': comparison_value}. Example: {'property': 'continent', 'operator': 'Equal', 'value': 'Europe'} returns only European objects.
The difference is massive. One teaches, the other just defines.
Your descriptions are read at the moment the AI needs to make a decision. Make them count. Show formats, give examples, explain tradeoffs, document defaults, and teach semantics.
The AI will thank you with faster tool selection and correct tool usage. Your users will thank you when their queries work on the first try.
Want to see this in practice? Check out our tool implementations or read about the six-tool pattern that structures these parameters.