The Anti-Pattern: 50 Confusing Tools

The most common mistake when building MCP servers is treating them like REST APIs. "We have 47 endpoints, so we'll create 47 tools." This approach fails spectacularly in the MCP environment.

The API Conversion Trap

Consider a typical e-commerce API:

POST   /api/products              # Create product
GET    /api/products              # List products
GET    /api/products/{id}         # Get product
PUT    /api/products/{id}         # Update product
DELETE /api/products/{id}         # Delete product
POST   /api/products/{id}/images  # Add image
DELETE /api/products/{id}/images/{img_id}  # Remove image
GET    /api/products/{id}/reviews # Get reviews
POST   /api/products/{id}/reviews # Add review
PUT    /api/products/{id}/inventory # Update inventory
GET    /api/categories            # List categories
POST   /api/categories            # Create category
# ... 35 more endpoints

The naive approach converts each endpoint to a tool:

#![allow(unused)]
fn main() {
// DON'T DO THIS
let tools = vec![
    Tool::new("create_product"),
    Tool::new("list_products"),
    Tool::new("get_product"),
    Tool::new("update_product"),
    Tool::new("delete_product"),
    Tool::new("add_product_image"),
    Tool::new("remove_product_image"),
    Tool::new("get_product_reviews"),
    Tool::new("add_product_review"),
    Tool::new("update_inventory"),
    Tool::new("list_categories"),
    Tool::new("create_category"),
    // ... 35 more tools
];
}

This creates a nightmare for AI clients.

Why This Fails

Problem 1: Tool Selection Overload

When an AI sees 47 tools, it must evaluate each one against the user's request. The cognitive load increases non-linearly:

User: "Add a new laptop to the store"

AI must consider:
- create_product? (probably)
- add_product_image? (maybe needed after?)
- update_inventory? (should set initial stock?)
- list_categories? (need to find Electronics category first?)
- create_category? (if Electronics doesn't exist?)

With 47 tools, the AI might:
- Choose the wrong tool
- Call tools in a suboptimal order
- Miss required steps
- Get confused and ask for clarification

Problem 2: Name Collisions

Your 47 tools don't exist in isolation. Other MCP servers connected to the same client may have similar names:

Your server:                  Asana server:            Google Drive server:
- create_product             - create_task            - create_document
- update_product             - update_task            - update_document
- delete_product             - delete_task            - delete_document
- list_products              - list_tasks             - list_documents
- get_product                - get_task               - get_document

A business user might have your e-commerce server connected alongside their project management (Asana, Notion) and document storage (Google Drive, SharePoint). The AI sees a sea of create_*, update_*, delete_*, list_*, get_* tools. Without excellent descriptions, it will make mistakes.

Problem 3: Implicit Workflows Hidden

APIs encode workflows implicitly through endpoint sequences. MCP tools are independent—there's no built-in way to say "call A, then B, then C":

REST workflow (implicit in client code):
1. POST /api/products → get product_id
2. POST /api/products/{id}/images → attach image
3. PUT /api/products/{id}/inventory → set stock

MCP reality:
- AI sees 3 independent tools
- No indication they should be called together
- User must know to request all three steps
- Or AI must infer the workflow (unreliable)

Problem 4: Description Burden

Each of your 47 tools needs a description good enough for an AI to understand when to use it. Most API endpoints don't have descriptions written for this purpose:

#![allow(unused)]
fn main() {
// Typical API-converted tool (inadequate)
Tool::new("update_inventory")
    .description("Updates inventory")  // Useless for AI decision-making

// What the AI actually needs
Tool::new("update_product_stock_level")
    .description(
        "Set the available quantity for a product in the inventory system. \
        Use this after creating a new product or when restocking. \
        Requires product_id and quantity. Quantity must be non-negative. \
        Returns the updated inventory record with last_modified timestamp."
    )
}

Writing 47 descriptions of this quality is significant work—and maintaining them as the API evolves is even harder.

Real-World Consequences

Case Study: The 73-Tool Disaster

A team converted their entire internal API to MCP tools: 73 tools covering user management, billing, reporting, and admin functions. Results:

  • AI accuracy dropped to 34% for multi-step tasks
  • Response latency increased 5x as the AI evaluated all 73 tools
  • Support tickets tripled as users got unexpected results
  • Rollback within 2 weeks to a 12-tool focused design

Case Study: The Naming Collision

A database tool server used query as a tool name. When connected alongside a logging server (which also had query), the AI would randomly choose between them based on subtle description differences. Users reported "sometimes it queries the database, sometimes it searches logs, I can't predict which."

The Better Approach: Purposeful Design

Instead of converting APIs to tools 1:1, design for how AI clients actually work:

1. Focus on User Tasks, Not API Operations

#![allow(unused)]
fn main() {
// Instead of 7 product CRUD tools, one task-focused tool:
Tool::new("manage_product_catalog")
    .description(
        "Create, update, or manage products in the catalog. \
        Handles product details, images, categories, and initial inventory. \
        Provide the operation type and relevant product data."
    )
    .input_schema(json!({
        "type": "object",
        "properties": {
            "operation": {
                "type": "string",
                "enum": ["create", "update", "add_image", "set_category", "discontinue"]
            },
            "product": {
                "type": "object",
                "properties": {
                    "id": { "type": "string" },
                    "name": { "type": "string" },
                    "description": { "type": "string" },
                    "price": { "type": "number" },
                    "category": { "type": "string" },
                    "initial_stock": { "type": "integer" }
                }
            }
        }
    }))
}

2. Use Prompts for Workflows

Instead of hoping the AI calls tools in the right order, define workflows as prompts:

#![allow(unused)]
fn main() {
Prompt::new("add-new-product")
    .description("Complete workflow to add a new product with images and inventory")
    .template(
        "I'll help you add a new product to the catalog. \
        This will:\n\
        1. Create the product with basic details\n\
        2. Upload any product images\n\
        3. Set initial inventory levels\n\
        4. Assign to appropriate categories\n\n\
        Please provide the product details..."
    )
}

3. Use Resources for Reference Data

Instead of list_categories and get_category tools, expose categories as resources:

#![allow(unused)]
fn main() {
Resource::new("catalog://categories")
    .description("All product categories with IDs and hierarchy")
    .mime_type("application/json")
}

The AI can read this resource to understand available categories without making a tool call.

Summary: From API to MCP

API ThinkingMCP Thinking
One endpoint = one toolOne user task = one tool
CRUD operationsHigh-level actions
Client controls workflowPrompts guide workflow
Endpoints are independentTools designed for multi-server environment
Minimal descriptionsAI-decision-quality descriptions
47 endpoints → 47 tools47 endpoints → 8-12 focused tools + prompts + resources

The next section covers how to design tool sets that are cohesive—tools that work together naturally and are easily distinguished by AI clients.