MCP Tool Design

After creating your first tool, apply these design guidelines so AI clients can discover, understand, and invoke your tools correctly.

After reading this page, you will be able to:

  • Write tool names and descriptions that help AI clients select the right tool

  • Define input properties with appropriate types and constraints

  • Design focused tools that complete quickly

Tool discovery

AI clients use tool names and descriptions to decide which tool to call. Good metadata helps AI select the right tool and provide correct inputs.

The label field defines the tool name. The meta.mcp block defines the description and input properties.

label: get-weather

meta:
  tags: [ weather, api, example ]
  mcp:
    enabled: true
    description: "Get current weather for a city. Returns temperature, humidity, and conditions."
    properties:
      - name: city
        type: string
        description: "City name (e.g., 'London', 'Tokyo', 'New York')"
        required: true

For the complete field reference, see MCP metadata fields.

Choose a clear tool name

AI clients see the label value when selecting tools, so make it descriptive and consistent.

The name should reflect the tool’s primary action and target resource. Focus on clarity over brevity. For example, use get-weather-forecast instead of just get-data.

Write effective descriptions

Tool descriptions help AI clients decide when to use your tool. Start with an action verb and explain what the tool returns.

description: "Get current weather for a city. Returns temperature in Celsius and Fahrenheit, humidity percentage, and weather description."

Include any limitations or requirements in the description:

description: "Search product catalog by name or category. Returns up to 10 results. Requires at least one search term."

Define input properties clearly

Each property needs a name, type, description, and required status. Use string, number, or boolean types.

Include example values in descriptions:

properties:
  - name: city
    type: string
    description: "City name (e.g., 'London', 'New York', 'Tokyo')"
    required: true

  - name: units
    type: string
    description: "Temperature units: 'celsius' or 'fahrenheit'. Defaults to 'celsius'."
    required: false

  - name: limit
    type: number
    description: "Maximum results to return (1-100). Defaults to 10."
    required: false

  - name: include_forecast
    type: boolean
    description: "If true, include 5-day forecast. Defaults to false."
    required: false

Mark properties as required only if the tool cannot function without them:

properties:
  - name: city
    type: string
    description: "City name to get weather for"
    required: true

Optional properties should have sensible defaults:

- name: units
  type: string
  description: "Temperature units. Defaults to 'celsius'."
  required: false

For patterns that apply defaults and validate values at runtime, see input validation patterns.

Tool execution

Keep tools focused

Each tool should do one thing well. If you find yourself adding multiple unrelated operations, split them into separate tools.

Tools that do too much or have vague purposes cause problems because AI clients rely on descriptions to choose tools. Vague descriptions lead to wrong tool selection. Also, tools that do too much are harder to test and debug.

Write descriptions that clearly state what the tool does, what input it needs, and what it returns. If a tool is doing multiple things, split it into focused tools.

Design for quick completion

MCP tools should complete quickly. AI clients wait for responses, and long-running tools cause poor user experiences.

Tools that wait indefinitely, poll continuously, or never return cause problems because MCP tools use a request/response model. A tool that never completes will time out and fail, and resources remain allocated while waiting.

Follow these guidelines:

  • Set explicit timeouts on all external calls. For timeout options, see http processor.

  • Avoid unbounded reads. Read N messages, not all messages.

  • Consider pagination for large datasets.

  • Return partial results if full processing takes too long.

For patterns that handle timeout failures gracefully, see error handling patterns.

Complete example

This example combines all the best practices:

label: search-customer-orders

processors:
  - mutation: |
      let customer_id = this.customer_id | ""
      let status = this.status | ""
      let limit = (this.limit | 10).number()
      root = {
        "orders": [
          {
            "order_id": "ord_001",
            "customer_id": $customer_id,
            "status": if $status != "" { $status } else { "delivered" },
            "total": 125.99
          }
        ],
        "count": 1,
        "filters_applied": {
          "customer_id": $customer_id,
          "status": $status,
          "limit": $limit
        }
      }

meta:
  tags: [ orders, database, production ]
  mcp:
    enabled: true
    description: "Search customer orders by customer ID, date range, or status. Returns order summaries with totals. Maximum 50 results per query."
    properties:
      - name: customer_id
        type: string
        description: "Customer ID (e.g., 'cust_12345'). Required if no other filters provided."
        required: false
      - name: status
        type: string
        description: "Order status filter: 'pending', 'shipped', 'delivered', or 'cancelled'."
        required: false
      - name: start_date
        type: string
        description: "Start date for date range filter (ISO 8601, e.g., '2024-01-01')."
        required: false
      - name: end_date
        type: string
        description: "End date for date range filter (ISO 8601, e.g., '2024-12-31')."
        required: false
      - name: limit
        type: number
        description: "Maximum results to return (1-50). Defaults to 10."
        required: false

Next steps