Exercise: Tool Design Review

ch04-01-tool-design-review
⭐⭐ intermediate ⏱️ 30 min

A startup has asked you to review their MCP server design before they deploy it to production. Their server started as a direct conversion of their REST API, and they're concerned about usability.

Your task is to identify the design problems and propose a refactored design that follows the principles from this chapter.

🎯 Learning Objectives

Thinking

Doing

💬 Discussion

  • What would happen if a user connects this server alongside GitHub and filesystem servers?
  • How would an AI decide which tool to use for "show me recent activity"?
  • What's the difference between a good tool set for humans vs. AI?
review.md

💡 Hints

Hint 1: Identifying collision risks

Look for generic names that other servers might also use:

  • query - postgres-server also has query
  • list - many servers have list operations
  • get - very generic, could mean anything
  • action - what kind of action?

Ask: "If I saw just this tool name, would I know which server it came from?"

Hint 2: Domain groupings

Consider organizing by business domain, not by operation type:

Customer domain:

  • customer_get, customer_list, customer_update

Order domain:

  • order_get, order_list, order_create, order_cancel

Reporting domain:

  • report_sales, report_inventory, report_customers

Admin domain:

  • admin_send_email, admin_create_ticket, admin_export
Hint 3: Refactoring the report tool

The report tool does 4 different things. Split by report type:

report_sales
  - Description: "Generate sales report with revenue, units, and trends.
    Use when user asks about sales performance, revenue, or sales trends.
    Returns report data with totals, comparisons, and visualizable metrics."
  - Parameters: { date_range, group_by, include_forecast }

report_inventory

  • Description: "Generate inventory status report with stock levels and alerts. Use when user asks about stock, inventory, or supply levels. Returns current stock, reorder alerts, and turnover metrics."
  • Parameters: { warehouse, category, include_projections }

report_customers

  • Description: "Generate customer analytics report with segments and health. Use when user asks about customer behavior, churn, or segments. Returns segment breakdown, health scores, and trend analysis."
  • Parameters: { segment, time_period, include_cohort_analysis }
⚠️ Try the exercise first! Show Solution
# MCP Server Design Review - Solution

Problem Analysis

Tool 1: query

Problems:

  • ❌ Generic name - collides with postgres-server's query
  • ❌ Vague description - "Query data" tells AI nothing
  • ❌ Swiss army knife - queries any table with dynamic type

Tool 2: modify

Problems:

  • ❌ Swiss army knife - insert, update, AND delete in one tool
  • ❌ Dangerous - no separation between safe and destructive operations
  • ❌ Vague description and parameters

Tool 3: get

Problems:

  • ❌ Generic name - get is used everywhere
  • ❌ Swiss army knife - gets customers, orders, products, or users
  • ❌ Description "Get something" is useless

Tool 4: list

Problems:

  • ❌ Generic name - collides with many servers
  • ❌ Swiss army knife - lists any entity type
  • ❌ AI must guess what "things" to list

Tool 5: report

Problems:

  • ❌ Swiss army knife - 4 different report types
  • ❌ AI must know all report types exist
  • ❌ Different reports need different parameters

Tool 6: action

Problems:

  • ❌ Extremely generic - "Perform action" on what?
  • ❌ Mixes unrelated operations (email, tickets, archive, export)
  • ❌ AI can't discover what actions are available

Refactored Design

Customer Domain

  <div class="solution-explanation">
    <h4>Explanation</h4>

customer_get Description: "Get customer details by ID. Use when user asks about a specific customer. Returns profile, contact info, and account status."

customer_list Description: "List customers with optional filters. Use when user asks to see customers or search for customers. Returns paginated customer list with summary info."

customer_update Description: "Update customer information. Use when user explicitly requests customer changes. Returns updated customer record." order_get Description: "Get order details by ID. Use for order lookups and status checks. Returns order with items, status, and tracking."

order_list Description: "List orders with filters. Use for order history and order searches. Returns paginated orders with summary."

order_create Description: "Create a new order. Use when user wants to place an order. Returns created order with ID." report_sales Description: "Generate sales performance report. Use for revenue analysis, sales trends, and performance reviews. Returns totals, comparisons, and trend data."

report_inventory Description: "Generate inventory status report. Use for stock levels, reorder alerts, and supply planning. Returns stock levels and projections."

report_customer_analytics Description: "Generate customer analytics report. Use for churn analysis, segmentation, and customer health. Returns segment data and health metrics." admin_send_email Description: "Send email to customer or internal recipient. Use when user explicitly requests sending an email. Returns send confirmation and tracking ID."

admin_export_data Description: "Export data to file format. Use when user needs data download or file export. Returns file path or download URL."

🧪 Tests

Run these tests locally with:

cargo test
View Test Code
#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests {
    // These are conceptual tests for the exercise
#[test]
fn tool_names_have_domain_prefix() {
    let tool_names = vec![
        &quot;customer_get&quot;,
        &quot;customer_list&quot;,
        &quot;order_create&quot;,
        &quot;report_sales&quot;,
    ];

    for name in tool_names {
        assert!(
            name.contains(&quot;_&quot;),
            &quot;Tool {} should have domain prefix&quot;,
            name
        );
    }
}

#[test]
fn descriptions_follow_template() {
    let description = &quot;Execute read-only queries against the customer database. \
        Use for retrieving customer records. \
        Returns query results as JSON array.&quot;;

    assert!(description.contains(&quot;Use for&quot;),
        &quot;Description should explain when to use&quot;);
    assert!(description.contains(&quot;Returns&quot;),
        &quot;Description should explain what it returns&quot;);
}
}

}

🤔 Reflection

  • How would you handle a case where a tool legitimately needs to do multiple things?
  • What's the trade-off between fewer multi-purpose tools and many focused tools?
  • How might you document the relationships between related tools?
  • Should you ever break the domain prefix convention? When?