# How to Design Tools for AI Agents: Workflows vs. API Endpoints

> Design AI agent tools around user workflows not API endpoints for less context, lower latency, and better error recovery. Learn how Composio simplifies agent tool creation.

- Repository: [Composio/awesome-codex-skills](https://github.com/composiohq/awesome-codex-skills)
- Tags: best-practices
- Published: 2026-04-26

---

**Design AI agent tools around complete user workflows rather than individual API endpoints to minimize context consumption, reduce latency, and provide actionable error recovery.**

When building AI agents that interact with external services, the way you structure tools determines whether your agent performs efficiently or struggles with fragmented logic. According to the **ComposioHQ/awesome-codex-skills** repository, successful tool design requires packaging entire workflows into single, high-impact operations rather than exposing raw API endpoints. This workflow-centric approach ensures agents stay within their context budget while delivering reliable, human-oriented functionality.

## Why Workflow-Centric Tools Outperform Raw API Endpoints

AI agents interact with external services through **tools**—tiny, purpose-built operations that the model invokes automatically. A well-designed tool does more than wrap a raw API call; it packages the entire user-level workflow the agent needs to accomplish.

Plain API-endpoint tools create four critical problems that workflow-centric designs solve:

- **Fragmented calls** force the agent to orchestrate several endpoints (e.g., list calendars → find free slot → create event). A workflow tool collapses this into a single `schedule_event` operation that checks availability and creates the event in one call.

- **Context-budget waste** occurs when every extra call consumes tokens and context space, increasing latency. Workflow tools provide compact interactions where the agent sends one request and receives one concise response.

- **Error handling complexity** requires the model to recover from partial failures across many calls. Workflow tools return centralized, actionable errors (e.g., "No free slots; try a different date" instead of HTTP 404 codes).

- **Inconsistent naming** plagues endpoint names that do not match how humans think about tasks. Workflow tools use human-oriented naming that reflects natural verbs (`schedule_event`, `analyze_invoice`).

The **MCP Builder** guide in [`mcp-builder/SKILL.md`](https://github.com/ComposioHQ/awesome-codex-skills/blob/main/mcp-builder/SKILL.md) explicitly warns against merely wrapping endpoints and urges developers to "Build for Workflows, Not Just API Endpoints". It stresses that tools should be **action-oriented**, **concise**, and **aligned with the agent’s limited context**.

## Core Design Principles for AI Agent Tools

According to [`reference/mcp_best_practices.md`](https://github.com/ComposioHQ/awesome-codex-skills/blob/main/reference/mcp_best_practices.md), effective tool design follows a structured workflow-first methodology:

1. **Identify the user’s goal**—start from the task the agent is asked to perform (e.g., "book a meeting").

2. **Map the end-to-end workflow**—list every sub-step required to achieve that goal.

3. **Collapse the workflow into a single tool** when possible, exposing only the parameters the user would naturally provide.

4. **Design the tool’s signature** with clear, typed inputs using **Pydantic** for Python or **Zod** for TypeScript, and include an optional `response_format` argument (`json` | `markdown`).

5. **Provide actionable error messages** that guide the model toward the next step instead of dumping raw HTTP error codes.

6. **Respect pagination, character limits, and context budget**—return truncated results with helpful "see more" hints.

These guidelines are reinforced in the **Tool Design Guidelines** section of [`reference/mcp_best_practices.md`](https://github.com/ComposioHQ/awesome-codex-skills/blob/main/reference/mcp_best_practices.md) and the **Tool Naming** rules in [`reference/python_mcp_server.md`](https://github.com/ComposioHQ/awesome-codex-skills/blob/main/reference/python_mcp_server.md).

## Implementing Workflow-First Tools in Practice

Below are two illustrative implementations that embody the workflow-first mindset from the repository's reference documentation.

### Python Example: schedule_event

This tool encapsulates availability lookup and event creation in a single operation, as recommended in [`reference/python_mcp_server.md`](https://github.com/ComposioHQ/awesome-codex-skills/blob/main/reference/python_mcp_server.md).

```python
from mcp.server.fastmcp import FastMCP
from pydantic import BaseModel, Field

mcp = FastMCP("calendar_mcp")

class ScheduleEventInput(BaseModel):
    title: str = Field(..., description="Event title")
    participants: list[str] = Field(..., description="List of email addresses")
    start_time: str = Field(..., description="ISO‑8601 start time")
    duration_minutes: int = Field(..., ge=1, description="Length of the meeting")

@mcp.tool(
    annotations={
        "title": "Schedule Event",
        "readOnlyHint": False,
        "destructiveHint": True,
        "idempotentHint": False,
        "openWorldHint": True,
    }
)
async def schedule_event(input: ScheduleEventInput, response_format: str = "markdown"):
    """
    Checks participants' availability and creates a calendar event.
    Returns a concise confirmation or a helpful error message.
    """
    # 1️⃣ Check free slots (internal helper)

    free = await check_availability(input.participants, input.start_time, input.duration_minutes)
    if not free:
        return {
            "isError": True,
            "content": [
                {
                    "type": "text",
                    "text": "No common free slot found. Suggest a later time or fewer participants."
                }
            ]
        }

    # 2️⃣ Create the event (single API call)

    event = await calendar_api.create_event(
        title=input.title,
        start=input.start_time,
        duration=input.duration_minutes,
        attendees=input.participants,
    )

    # 3️⃣ Return in requested format

    if response_format == "json":
        return {"event_id": event.id, "link": event.join_url}
    else:
        return {
            "content": [
                {
                    "type": "text",
                    "text": f"✅ Event **{input.title}** scheduled for {input.start_time}.\n"
                            f"Link: {event.join_url}"
                }
            ]
        }

```

The signature only asks for information a user would naturally provide, and errors are actionable ("suggest a later time") rather than technical HTTP codes.

### TypeScript Example: analyze_invoice

This implementation from [`reference/node_mcp_server.md`](https://github.com/ComposioHQ/awesome-codex-skills/blob/main/reference/node_mcp_server.md) demonstrates the fetch-validate-summarize workflow pattern.

```typescript
import { Server } from "mcp-sdk";
import { z } from "zod";

const server = new Server({
  name: "finance-mcp",
  version: "1.0.0",
});

const AnalyzeInvoiceInput = z.object({
  invoiceId: z.string().describe("Unique identifier of the invoice to analyze"),
  includeDetails: z.boolean().default(false).describe("Return line‑item breakdown?"),
});

server.registerTool({
  name: "analyze_invoice",
  description: "Fetches an invoice, validates totals, extracts key metrics, and optionally returns line‑item details.",
  inputSchema: AnalyzeInvoiceInput,
  annotations: {
    title: "Analyze Invoice",
    readOnlyHint: true,
    destructiveHint": false,
    idempotentHint: true,
    openWorldHint: false,
  },
  async handler({ invoiceId, includeDetails }, context) {
    // 1️⃣ Retrieve invoice from external billing API (single call)
    const invoice = await billingApi.getInvoice(invoiceId);

    // 2️⃣ Validate totals & compute metrics
    const total = invoice.items.reduce((sum, i) => sum + i.amount, 0);
    const discrepancy = Math.abs(total - invoice.totalAmount);

    // 3️⃣ Build response
    const base = {
      status: discrepancy < 0.01 ? "valid" : "mismatch",
      total,
      currency: invoice.currency,
    };

    if (includeDetails) {
      return {
        response_format: "json",
        data: { ...base, items: invoice.items },
      };
    }

    // Human‑readable Markdown by default
    return {
      content: [
        {
          type: "text",
          text: `**Invoice ${invoiceId}** – ${base.status}\n` +
                `Total: ${base.total} ${base.currency}\n` +
                (base.status === "mismatch"
                  ? `⚠️ Discrepancy of ${discrepancy.toFixed(2)} ${base.currency}`
                  : "✅ Totals match."),
        },
      ],
    };
  },
});

```

The optional `includeDetails` flag lets the model request extra data only when needed, conserving the token budget while supporting both **JSON** for downstream processing and **Markdown** for direct user presentation.

## Summary

- **Package workflows, not endpoints**: Collapse multi-step processes (availability checks + creation) into single tool calls to preserve LLM context budgets.

- **Use human-oriented naming**: Name tools after user goals (`schedule_event`) rather than API operations (`post_calendar_events`).

- **Implement strict typing**: Use **Pydantic** (Python) or **Zod** (TypeScript) to define clear input schemas with descriptive fields.

- **Return actionable errors**: Guide the agent toward resolution with human-readable messages rather than raw HTTP status codes.

- **Support flexible response formats**: Allow `json` for structured data and `markdown` for human-readable output via optional parameters.

- **Annotate tool behavior**: Use `readOnlyHint`, `destructiveHint`, `idempotentHint`, and `openWorldHint` annotations to help the LLM understand side effects.

## Frequently Asked Questions

### What is the difference between a workflow tool and an API endpoint tool?

A workflow tool encapsulates an entire user task—such as checking availability and creating a calendar event—into a single callable operation. An API endpoint tool merely wraps one raw HTTP endpoint, forcing the AI agent to chain multiple calls and handle intermediate state manually. According to [`mcp-builder/SKILL.md`](https://github.com/ComposioHQ/awesome-codex-skills/blob/main/mcp-builder/SKILL.md), workflow tools reduce latency and error rates by handling orchestration internally.

### How should I handle errors when designing tools for AI agents?

Design error messages that suggest the next action rather than reporting technical failures. For example, return "No common free slot found. Suggest a later time or fewer participants" instead of HTTP 404 or 500 codes. This approach, documented in [`reference/mcp_best_practices.md`](https://github.com/ComposioHQ/awesome-codex-skills/blob/main/reference/mcp_best_practices.md), enables the LLM to recover gracefully without consuming additional context tokens on debugging.

### Why should I use Pydantic or Zod for tool input schemas?

**Pydantic** (Python) and **Zod** (TypeScript) provide runtime validation and clear type definitions that help the LLM understand exactly what parameters to provide. These libraries allow you to add field descriptions, constraints, and defaults that translate directly into the tool's interface description seen by the agent. The [`reference/python_mcp_server.md`](https://github.com/ComposioHQ/awesome-codex-skills/blob/main/reference/python_mcp_server.md) and [`reference/node_mcp_server.md`](https://github.com/ComposioHQ/awesome-codex-skills/blob/main/reference/node_mcp_server.md) files mandate these libraries for production MCP server implementations.

### How do I manage context budget constraints when designing tools?

Respect the LLM's limited context window by returning concise responses and supporting pagination hints. Include optional flags (like `includeDetails`) that let the agent request additional data only when necessary. The **Tool Design Guidelines** in [`reference/mcp_best_practices.md`](https://github.com/ComposioHQ/awesome-codex-skills/blob/main/reference/mcp_best_practices.md) recommend truncating long results with "see more" hints and avoiding verbose intermediate data in favor of final summaries.