412 lines
13 KiB
Markdown
412 lines
13 KiB
Markdown
# Agent-Centric Design for MXCP Tools
|
|
|
|
**Designing MXCP tools that LLMs can effectively use with zero prior context.**
|
|
|
|
## Overview
|
|
|
|
When building MXCP servers, remember: **LLMs are your primary users**. Your tools must enable LLMs to accomplish real-world tasks effectively. This guide provides principles for designing tools that work well for AI agents.
|
|
|
|
## Core Principles
|
|
|
|
### 1. Build for Workflows, Not Just Data Access
|
|
|
|
**Don't simply expose database tables or API endpoints - design tools around complete workflows.**
|
|
|
|
#### ❌ Poor Design: Raw Data Access
|
|
```yaml
|
|
# tools/get_user.yml
|
|
tool:
|
|
name: get_user
|
|
description: "Get user by ID"
|
|
parameters:
|
|
- name: user_id
|
|
type: integer
|
|
source:
|
|
code: SELECT * FROM users WHERE id = $user_id
|
|
|
|
# tools/get_orders.yml
|
|
tool:
|
|
name: get_orders
|
|
description: "Get orders by user"
|
|
parameters:
|
|
- name: user_id
|
|
type: integer
|
|
source:
|
|
code: SELECT * FROM orders WHERE user_id = $user_id
|
|
```
|
|
|
|
**Problem**: LLM needs multiple tool calls to answer "What did user 123 buy?"
|
|
|
|
#### ✅ Good Design: Workflow-Oriented
|
|
```yaml
|
|
# tools/get_user_purchase_summary.yml
|
|
tool:
|
|
name: get_user_purchase_summary
|
|
description: "Get complete purchase history for a user including orders, products, and total spending. Use this to understand a user's buying behavior and preferences."
|
|
parameters:
|
|
- name: user_id
|
|
type: integer
|
|
description: "User identifier"
|
|
- name: date_range
|
|
type: string
|
|
description: "Optional date range: 'last_30_days', 'last_year', or 'all_time'"
|
|
default: "all_time"
|
|
return:
|
|
type: object
|
|
properties:
|
|
user_info: { type: object, description: "Basic user information" }
|
|
order_count: { type: integer, description: "Total number of orders" }
|
|
total_spent: { type: number, description: "Total amount spent in USD" }
|
|
top_products: { type: array, description: "Most frequently purchased products" }
|
|
source:
|
|
code: |
|
|
WITH user_orders AS (
|
|
SELECT o.*, p.name as product_name, p.category
|
|
FROM orders o
|
|
JOIN order_items oi ON o.id = oi.order_id
|
|
JOIN products p ON oi.product_id = p.id
|
|
WHERE o.user_id = $user_id
|
|
AND ($date_range = 'all_time'
|
|
OR ($date_range = 'last_30_days' AND o.created_at > CURRENT_DATE - INTERVAL 30 DAY)
|
|
OR ($date_range = 'last_year' AND o.created_at > CURRENT_DATE - INTERVAL 1 YEAR))
|
|
)
|
|
SELECT
|
|
json_object(
|
|
'user_info', (SELECT json_object('id', id, 'name', name) FROM users WHERE id = $user_id),
|
|
'order_count', COUNT(DISTINCT id),
|
|
'total_spent', SUM(total_amount),
|
|
'top_products', (
|
|
SELECT json_group_array(json_object('product', product_name, 'count', count))
|
|
FROM (SELECT product_name, COUNT(*) as count FROM user_orders GROUP BY product_name ORDER BY count DESC LIMIT 5)
|
|
)
|
|
) as result
|
|
FROM user_orders
|
|
```
|
|
|
|
**Benefit**: Single tool call answers complete questions about user behavior.
|
|
|
|
### 2. Optimize for Limited Context
|
|
|
|
**LLMs have constrained context windows - make every token count.**
|
|
|
|
#### Design for Concise Responses
|
|
|
|
```yaml
|
|
tool:
|
|
name: search_products
|
|
parameters:
|
|
- name: query
|
|
type: string
|
|
description: "Search query"
|
|
- name: detail_level
|
|
type: string
|
|
description: "Response detail level"
|
|
enum: ["minimal", "standard", "full"]
|
|
default: "standard"
|
|
examples:
|
|
- "minimal: Only ID, name, price"
|
|
- "standard: Basic info + category + stock"
|
|
- "full: All fields including descriptions"
|
|
source:
|
|
code: |
|
|
SELECT
|
|
CASE $detail_level
|
|
WHEN 'minimal' THEN json_object('id', id, 'name', name, 'price', price)
|
|
WHEN 'standard' THEN json_object('id', id, 'name', name, 'price', price, 'category', category, 'stock', stock)
|
|
ELSE json_object('id', id, 'name', name, 'price', price, 'category', category, 'stock', stock, 'description', description, 'specs', specs)
|
|
END as product
|
|
FROM products
|
|
WHERE name LIKE '%' || $query || '%'
|
|
```
|
|
|
|
**Principle**: Default to high-signal information, provide options for more detail.
|
|
|
|
#### Use Human-Readable Identifiers
|
|
|
|
```yaml
|
|
# ✅ GOOD: Return names alongside IDs
|
|
return:
|
|
type: object
|
|
properties:
|
|
customer_id: { type: string, description: "Customer ID (e.g., 'CUST_12345')" }
|
|
customer_name: { type: string, description: "Customer display name" }
|
|
assigned_to_id: { type: string, description: "Assigned user ID" }
|
|
assigned_to_name: { type: string, description: "Assigned user name" }
|
|
|
|
# ❌ BAD: Only return opaque IDs
|
|
return:
|
|
type: object
|
|
properties:
|
|
customer_id: { type: integer }
|
|
assigned_to: { type: integer }
|
|
```
|
|
|
|
**Benefit**: LLM can understand relationships without additional lookups.
|
|
|
|
### 3. Design Actionable Error Messages
|
|
|
|
**Error messages should guide LLMs toward correct usage patterns.**
|
|
|
|
#### ✅ Good Error Messages (Python Tools)
|
|
|
|
```python
|
|
def search_large_dataset(query: str, limit: int = 100) -> dict:
|
|
"""Search with intelligent error guidance"""
|
|
|
|
# Validate inputs
|
|
if not query or len(query) < 3:
|
|
return {
|
|
"success": False,
|
|
"error": "Query must be at least 3 characters. Provide a more specific search term to get better results.",
|
|
"error_code": "QUERY_TOO_SHORT",
|
|
"suggestion": "Try adding more keywords or using specific product names"
|
|
}
|
|
|
|
if limit > 1000:
|
|
return {
|
|
"success": False,
|
|
"error": f"Limit of {limit} exceeds maximum allowed (1000). Use filters to narrow your search: add 'category' or 'price_range' parameters.",
|
|
"error_code": "LIMIT_EXCEEDED",
|
|
"max_limit": 1000,
|
|
"suggestion": "Try using category='electronics' or price_range='0-100' to reduce results"
|
|
}
|
|
|
|
# Execute search
|
|
results = db.execute(
|
|
"SELECT * FROM products WHERE name LIKE $query LIMIT $limit",
|
|
{"query": f"%{query}%", "limit": limit}
|
|
)
|
|
|
|
if not results:
|
|
return {
|
|
"success": False,
|
|
"error": f"No products found matching '{query}'. Try broader terms or check spelling.",
|
|
"error_code": "NO_RESULTS",
|
|
"suggestion": "Use 'list_categories' tool to see available product categories"
|
|
}
|
|
|
|
return {
|
|
"success": True,
|
|
"count": len(results),
|
|
"results": results
|
|
}
|
|
```
|
|
|
|
**Principle**: Every error should suggest a specific next action.
|
|
|
|
### 4. Follow Natural Task Subdivisions
|
|
|
|
**Tool names should reflect how humans think about tasks, not just database structure.**
|
|
|
|
#### ✅ Good: Task-Oriented Naming
|
|
```
|
|
get_customer_purchase_history # What users want to know
|
|
analyze_sales_by_region # Natural analysis task
|
|
check_inventory_status # Action-oriented
|
|
schedule_report_generation # Complete workflow
|
|
```
|
|
|
|
#### ❌ Poor: Database-Oriented Naming
|
|
```
|
|
select_from_orders # Database operation
|
|
join_users_and_purchases # Technical operation
|
|
aggregate_by_column # Generic operation
|
|
```
|
|
|
|
**Use consistent prefixes for discoverability**:
|
|
```yaml
|
|
# Customer operations
|
|
- get_customer_details
|
|
- get_customer_orders
|
|
- get_customer_analytics
|
|
|
|
# Product operations
|
|
- search_products
|
|
- get_product_details
|
|
- check_product_availability
|
|
|
|
# Analytics operations
|
|
- analyze_sales_trends
|
|
- analyze_customer_behavior
|
|
- analyze_inventory_turnover
|
|
```
|
|
|
|
### 5. Provide Comprehensive Documentation
|
|
|
|
**Every field must have a description that helps LLMs understand usage.**
|
|
|
|
See **references/llm-friendly-documentation.md** for complete documentation guidelines.
|
|
|
|
**Quick checklist**:
|
|
- [ ] Tool description explains WHAT, returns WHAT, WHEN to use
|
|
- [ ] Every parameter has description with examples
|
|
- [ ] Return type properties all have descriptions
|
|
- [ ] Cross-references to related tools
|
|
- [ ] Examples show realistic usage
|
|
|
|
## MXCP-Specific Best Practices
|
|
|
|
### Use SQL for Workflow Consolidation
|
|
|
|
**SQL is powerful for combining multiple data sources in one query:**
|
|
|
|
```yaml
|
|
tool:
|
|
name: get_order_fulfillment_status
|
|
description: "Get complete order fulfillment information including shipping, payments, and inventory status. Use this to answer questions about order status and estimated delivery."
|
|
source:
|
|
code: |
|
|
SELECT
|
|
o.id as order_id,
|
|
o.status as order_status,
|
|
u.name as customer_name,
|
|
s.carrier,
|
|
s.tracking_number,
|
|
s.estimated_delivery,
|
|
p.status as payment_status,
|
|
json_group_array(
|
|
json_object(
|
|
'product', prod.name,
|
|
'quantity', oi.quantity,
|
|
'in_stock', prod.stock >= oi.quantity
|
|
)
|
|
) as items
|
|
FROM orders o
|
|
JOIN users u ON o.user_id = u.id
|
|
LEFT JOIN shipments s ON o.id = s.order_id
|
|
LEFT JOIN payments p ON o.id = p.order_id
|
|
JOIN order_items oi ON o.id = oi.order_id
|
|
JOIN products prod ON oi.product_id = prod.id
|
|
WHERE o.id = $order_id
|
|
GROUP BY o.id
|
|
```
|
|
|
|
**Single tool call provides complete fulfillment picture.**
|
|
|
|
### Use Python for Complex Workflows
|
|
|
|
```python
|
|
async def analyze_customer_churn_risk(customer_id: str) -> dict:
|
|
"""
|
|
Comprehensive churn risk analysis combining multiple data sources.
|
|
|
|
Returns risk score, contributing factors, and recommended actions.
|
|
Use this to identify customers who may leave and take preventive action.
|
|
"""
|
|
# Get customer history
|
|
orders = db.execute(
|
|
"SELECT * FROM orders WHERE customer_id = $cid ORDER BY created_at DESC",
|
|
{"cid": customer_id}
|
|
)
|
|
|
|
support_tickets = db.execute(
|
|
"SELECT * FROM support_tickets WHERE customer_id = $cid",
|
|
{"cid": customer_id}
|
|
)
|
|
|
|
# Calculate risk factors
|
|
days_since_last_order = (datetime.now() - orders[0]["created_at"]).days if orders else 999
|
|
unresolved_tickets = len([t for t in support_tickets if t["status"] != "resolved"])
|
|
total_spent = sum(o["total_amount"] for o in orders)
|
|
|
|
# Determine risk level
|
|
risk_score = 0
|
|
factors = []
|
|
|
|
if days_since_last_order > 90:
|
|
risk_score += 30
|
|
factors.append("No purchases in 90+ days")
|
|
|
|
if unresolved_tickets > 0:
|
|
risk_score += 20 * unresolved_tickets
|
|
factors.append(f"{unresolved_tickets} unresolved support tickets")
|
|
|
|
if total_spent < 100:
|
|
risk_score += 10
|
|
factors.append("Low lifetime value")
|
|
|
|
# Generate recommendations
|
|
recommendations = []
|
|
if days_since_last_order > 90:
|
|
recommendations.append("Send re-engagement email with discount")
|
|
if unresolved_tickets > 0:
|
|
recommendations.append("Prioritize resolution of open support tickets")
|
|
|
|
return {
|
|
"success": True,
|
|
"customer_id": customer_id,
|
|
"risk_score": min(risk_score, 100),
|
|
"risk_level": "high" if risk_score > 60 else "medium" if risk_score > 30 else "low",
|
|
"contributing_factors": factors,
|
|
"recommendations": recommendations,
|
|
"days_since_last_order": days_since_last_order,
|
|
"unresolved_tickets": unresolved_tickets
|
|
}
|
|
```
|
|
|
|
### Leverage MXCP Policies for Context-Aware Tools
|
|
|
|
```yaml
|
|
tool:
|
|
name: get_employee_compensation
|
|
description: "Get employee compensation details. Returns salary and benefits information based on user permissions."
|
|
parameters:
|
|
- name: employee_id
|
|
type: string
|
|
description: "Employee identifier"
|
|
return:
|
|
type: object
|
|
properties:
|
|
employee_id: { type: string }
|
|
name: { type: string }
|
|
salary: { type: number, description: "Annual salary (admin only)" }
|
|
benefits: { type: array, description: "Benefits package" }
|
|
policies:
|
|
output:
|
|
- condition: "user.role != 'hr_manager' && user.role != 'admin'"
|
|
action: filter_fields
|
|
fields: ["salary"]
|
|
reason: "Salary information restricted to HR managers and admins"
|
|
source:
|
|
code: |
|
|
SELECT
|
|
employee_id,
|
|
name,
|
|
salary,
|
|
benefits
|
|
FROM employees
|
|
WHERE employee_id = $employee_id
|
|
```
|
|
|
|
**LLM can call same tool, MXCP automatically filters based on user context.**
|
|
|
|
## Testing Agent-Centric Design
|
|
|
|
### Create Realistic Evaluation Scenarios
|
|
|
|
See **references/mxcp-evaluation-guide.md** for complete evaluation guidelines.
|
|
|
|
**Quick validation**:
|
|
1. Can an LLM answer complex multi-step questions using your tools?
|
|
2. Do tool descriptions clearly indicate when to use each tool?
|
|
3. Do error messages guide the LLM toward correct usage?
|
|
4. Can common tasks be completed with minimal tool calls?
|
|
|
|
## Summary
|
|
|
|
**Agent-centric design principles for MXCP**:
|
|
|
|
1. ✅ **Build for workflows** - Consolidate related operations
|
|
2. ✅ **Optimize for context** - Provide detail level options, use readable identifiers
|
|
3. ✅ **Actionable errors** - Guide LLMs with specific suggestions
|
|
4. ✅ **Natural naming** - Task-oriented, not database-oriented
|
|
5. ✅ **Comprehensive docs** - Every parameter and field documented
|
|
|
|
**MXCP advantages**:
|
|
- SQL enables powerful workflow consolidation
|
|
- Python handles complex multi-step logic
|
|
- Policies provide automatic context-aware filtering
|
|
- Type system ensures clear contracts
|
|
|
|
**Remember**: Design for the LLM as your user, not the human. Humans configure tools, LLMs use them.
|