Cohesive API Design
Cohesion in MCP server design means your tools, resources, and prompts form a unified, understandable whole—both for AI clients that must choose between them and for users who need predictable behavior.
The Multi-Server Reality
Your MCP server operates in an environment you don't control. Consider what an AI client sees when a user has multiple servers connected:
Connected MCP Servers (typical business user setup):
1. google-drive-server (document storage)
- create_document, update_document, delete_document,
- list_documents, search_documents, share_document
2. asana-server (task management)
- create_task, update_task, delete_task, list_tasks,
- create_project, assign_task, set_due_date
3. salesforce-server (CRM)
- query_accounts, update_opportunity, list_contacts, log_activity
4. your-server (you're building this)
- ???
Total tools visible to AI: 20+ (and growing)
Your server's tools must be instantly distinguishable in this crowded environment.
Principles of Cohesive Design
1. Domain Prefixing
Prefix tool names with your domain to avoid collisions:
#![allow(unused)] fn main() { // Collision risk: generic names Tool::new("query") // Collides with postgres-server Tool::new("search") // Collides with Google Drive search_documents Tool::new("list") // Collides with everything // Cohesive: domain-specific names Tool::new("sales_query") // Clearly your sales system Tool::new("sales_report") // Consistent prefix Tool::new("sales_forecast") // AI understands these are related }
The AI can now reason: "The user asked about sales, I'll use the sales_* tools."
2. Consistent Verb Patterns
Choose a verb convention and stick to it across all tools:
#![allow(unused)] fn main() { // Inconsistent verbs (confusing) Tool::new("get_customer") // "get" Tool::new("fetch_orders") // "fetch" - same meaning, different word Tool::new("retrieve_products") // "retrieve" - yet another synonym Tool::new("load_inventory") // "load" - and another // Consistent verbs (cohesive) Tool::new("get_customer") Tool::new("get_orders") Tool::new("get_products") Tool::new("get_inventory") }
Consistent patterns help the AI predict tool names and understand tool relationships.
3. Hierarchical Organization
Structure tools to reflect their relationships:
#![allow(unused)] fn main() { // Flat structure (hard to understand relationships) vec![ Tool::new("create_order"), Tool::new("add_item"), Tool::new("remove_item"), Tool::new("apply_discount"), Tool::new("calculate_total"), Tool::new("submit_order"), Tool::new("cancel_order"), ] // Hierarchical structure (clear relationships) // Order lifecycle tools Tool::new("order_create") .description("Create a new order. Returns order_id for subsequent operations.") Tool::new("order_modify") .description("Add items, remove items, or apply discounts to an existing order.") .input_schema(json!({ "properties": { "order_id": { "type": "string" }, "action": { "type": "string", "enum": ["add_item", "remove_item", "apply_discount"] } } })) Tool::new("order_finalize") .description("Calculate totals and submit the order, or cancel it.") .input_schema(json!({ "properties": { "order_id": { "type": "string" }, "action": { "type": "string", "enum": ["submit", "cancel"] } } })) }
Three tools instead of seven, with clear lifecycle stages.
Designing for AI Understanding
Description Templates
Use consistent description structures across all tools:
#![allow(unused)] fn main() { // Template: What it does | When to use it | What it returns Tool::new("sales_query") .description( "Execute SQL queries against the sales database. \ Use for retrieving sales records, revenue data, and transaction history. \ Returns query results as JSON array of records." ) Tool::new("sales_report") .description( "Generate formatted sales reports for a date range. \ Use when the user needs summaries, trends, or printable reports. \ Returns report data with totals, averages, and visualizable metrics." ) Tool::new("sales_forecast") .description( "Predict future sales based on historical data. \ Use when the user asks about projections, predictions, or planning. \ Returns forecast data with confidence intervals." ) }
The AI can now distinguish:
- Raw data needs →
sales_query - Summaries/reports →
sales_report - Future predictions →
sales_forecast
Negative Descriptions
Sometimes it helps to say what a tool is not for:
#![allow(unused)] fn main() { Tool::new("sales_query") .description( "Execute read-only SQL queries against the sales database. \ Use for retrieving sales records and transaction history. \ \ NOTE: This tool CANNOT modify data. For updates, use sales_admin. \ NOTE: For reports and summaries, use sales_report instead (faster)." ) }
Output Consistency
Tools in the same domain should return consistent structures:
#![allow(unused)] fn main() { // All sales tools return a consistent envelope { "success": true, "data": { /* tool-specific data */ }, "metadata": { "query_time_ms": 45, "source": "sales_db_replica", "cached": false } } }
This helps the AI chain tools together—it knows what to expect.
Cohesion Across Tool-Resource-Prompt
True cohesion spans all three MCP primitives:
#![allow(unused)] fn main() { // TOOLS: Actions on the sales domain Tool::new("sales_query") Tool::new("sales_report") Tool::new("sales_forecast") // RESOURCES: Reference data for sales operations Resource::new("sales://schema") .description("Sales database schema - tables, columns, relationships") Resource::new("sales://regions") .description("List of sales regions with IDs and territories") Resource::new("sales://products") .description("Product catalog with IDs, names, and categories") // PROMPTS: Guided workflows combining tools and resources Prompt::new("quarterly-sales-analysis") .description("Comprehensive quarterly sales analysis with trends and forecasts") Prompt::new("sales-territory-review") .description("Review sales performance by territory with recommendations") }
The AI sees a complete, cohesive sales domain:
- Resources provide context (what data exists)
- Tools provide actions (what can be done)
- Prompts provide workflows (how to accomplish complex tasks)
Testing Cohesion
The "50 Tools" Test
List all tools from your server plus common business servers (Google Drive, Asana, Salesforce). Can an AI easily distinguish yours?
google-drive: create_document, update_document, list_documents
asana: create_task, update_task, list_tasks
salesforce: query_accounts, update_opportunity, list_contacts
your-server: ???
If your tools are "query", "list", "get" - FAIL
If your tools are "sales_query", "sales_report", "sales_forecast" - PASS
The "Explain It" Test
Describe your server to a colleague in one sentence. If you can't, your tools aren't cohesive.
FAIL: "It queries databases, generates reports, and also manages inventory
and does some customer stuff"
PASS: "It provides sales analytics - querying historical data, generating
reports, and forecasting future sales"
The "New Tool" Test
When you add a new tool, does its name and description obviously fit with existing tools?
Existing: sales_query, sales_report, sales_forecast
Adding customer support?
FAIL: support_ticket, help_request (different domain)
PASS: Create a new server for customer support
Adding sales alerts?
PASS: sales_alert_create, sales_alert_list (same domain, consistent naming)
Advanced: Foundation and Domain Servers
As your organization scales MCP adoption, cohesion becomes even more critical. In Part VIII: Server Composition, we explore a powerful pattern: Foundation Servers wrapped by Domain Servers.
The Pattern
Instead of building monolithic servers or having every team create their own database tools, you create a layered architecture:
┌─────────────────────────────────────────────────────────────┐
│ Business Users │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Sales Manager │ │ Finance Manager │ Domain Servers │
│ │ Domain Server │ │ Domain Server │ (department- │
│ │ │ │ │ specific) │
│ │ • pipeline_view │ │ • budget_check │ │
│ │ • territory_perf │ │ • expense_report │ │
│ │ • forecast_q4 │ │ • revenue_audit │ │
│ └────────┬─────────┘ └────────┬─────────┘ │
│ │ │ │
│ └──────────┬──────────┘ │
│ │ │
│ ┌──────────▼──────────┐ │
│ │ Foundation Server │ Foundation Server │
│ │ (db-explorer) │ (general-purpose) │
│ │ │ │
│ │ • db_query │ │
│ │ • db_schema │ │
│ │ • db_export │ │
│ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Why This Matters for Cohesion
Foundation Servers are general-purpose, reusable across the organization:
db-explorer: Generic database accessfile-manager: Document and file operationsapi-gateway: External API integrations
Domain Servers wrap foundations with business-specific cohesion:
- Focused on one department's workflows
- Pre-configured with relevant schemas and permissions
- Include prompts tailored to that department's tasks
- Hide complexity that's irrelevant to those users
#![allow(unused)] fn main() { // Sales Manager Domain Server // Wraps db-explorer but exposes only sales-relevant operations Tool::new("pipeline_view") .description("View sales pipeline with deal stages and probabilities") // Internally calls db_query with pre-built sales pipeline query Tool::new("territory_performance") .description("Compare territory performance against targets") // Internally calls db_query + db_export for territory reports Prompt::new("weekly-forecast") .description("Generate weekly sales forecast for your territories") // Guides the manager through a structured forecasting workflow }
Benefits
- User-Appropriate Cohesion: Sales managers see sales tools, not raw SQL
- Controlled Access: Domain servers enforce what each role can access
- Maintainability: Update the foundation; all domain servers benefit
- Reduced Tool Sprawl: Each user sees only 5-10 relevant tools, not 50
When to Use This Pattern
- Multiple departments need different views of the same data
- You want to control what each role can access
- Business users shouldn't need to understand database schemas
- You're scaling from one team to organization-wide MCP adoption
We cover this pattern in depth in Chapter 19: Server Composition, including implementation details, authentication flows, and real-world examples.
Summary
Cohesive design makes your MCP server:
- Distinguishable: AI easily identifies your tools among many servers
- Predictable: Users know what to expect from your domain
- Maintainable: New tools fit naturally into existing patterns
The key insight: design for the multi-server environment from the start. Your tools don't exist in isolation—they compete for the AI's attention alongside dozens of other tools.
Next, we'll examine the single responsibility principle—why each tool should do one thing well.