Advanced Agent Patterns

Building Multi-Step Agent Workflows

⏱ 15 min

Every lesson so far has looked at abilities mostly one at a time. Real requests rarely are: “find last month’s top posts and draft a roundup linking to them” needs several tool calls in sequence, each one feeding the next. This lesson is about designing abilities that chain well, and understanding what actually orchestrates that chain, which is not your MCP server.

What you'll learn in this lesson
Where the orchestration actually happens
The model plans the sequence, your abilities just need to compose cleanly.
Designing abilities that chain, not just abilities that work alone
Output shapes that feed naturally into the next tool's input.
A worked example, three abilities, one task
Finding top posts, drafting a roundup, and linking it in one flow.
What can go wrong mid-chain
Partial failures, and why each step should be safe to retry or safe to fail alone.
Prerequisites

Several related abilities from Course 2 (or built across earlier lessons in this course), and a connected client from Lessons 1 or 2. This lesson doesn’t introduce new MCP mechanics, it’s about designing your existing abilities so a model can combine them well.

There is no “workflow” object in MCP

This is worth stating plainly because it’s easy to expect otherwise: MCP has no concept of a multi-step workflow, a saved sequence, or a pipeline. What actually happens when an agent completes a multi-step task is that the model, Claude or whichever client you’re using, reads the available tool list, decides on its own which tools to call and in what order, calls the first one, reads the result, decides the next call based on that result, and repeats until it thinks the task is done. Your abilities are the building blocks. The model is the one doing the planning, every single time, fresh, based on what’s in front of it.

This means your job isn’t to build “a workflow,” it’s to build individual abilities whose inputs and outputs compose well together, so that whatever plan the model comes up with actually works.

Designing abilities that chain well

What makes an ability chain-friendly
1
Output includes IDs, not just descriptions
A "list top posts" ability that returns post IDs alongside titles lets a later step reference an exact post, a version returning only prose forces the model to guess or re-search.
2
Descriptions state what a tool needs as input, referencing likely sources
A "draft a roundup" ability's description can note it accepts a list of post IDs, hinting to the model that an earlier list-style tool is the right thing to call first.
3
Each step is independently useful
An ability that only makes sense as step 2 of one specific flow is a sign it should probably just be inlined into the step 1 or step 3 logic instead.
4
Errors are specific enough to recover from
A generic "something went wrong" mid-chain gives the model nothing to act on, a specific error lets it adjust and retry the right step.

A worked example: three abilities, one request

You: "Find last month's three best-performing posts by comment count,
draft a roundup post linking to each of them, and leave it as a draft
for me to review."

For a model to complete that in one conversational turn, it needs three abilities that compose:

// File: inc/abilities/workflow-example.php

// Ability 1: returns structured data with IDs, not just prose
wp_register_ability( 'my-plugin/top-posts-by-comments', array(
    'label'       => 'Find top posts by comment count',
    'description' => 'Returns the top N posts published in a given date range, '
        . 'ranked by comment count, each with its post ID, title, URL, and comment '
        . 'count. Use this to identify posts worth referencing in a roundup or summary.',
    'input_schema' => array(
        'type'       => 'object',
        'properties' => array(
            'since' => array( 'type' => 'string', 'format' => 'date' ),
            'until' => array( 'type' => 'string', 'format' => 'date' ),
            'limit' => array( 'type' => 'integer', 'default' => 3 ),
        ),
        'required' => array( 'since', 'until' ),
    ),
    'execute_callback'    => 'my_plugin_top_posts_by_comments',
    'permission_callback' => 'my_plugin_can_read_posts',
    'meta' => array( 'mcp' => array( 'public' => true ) ),
) );

// Ability 2: explicitly documents that it accepts the shape Ability 1 returns
wp_register_ability( 'my-plugin/create-draft-roundup', array(
    'label'       => 'Create a draft roundup post linking to given posts',
    'description' => 'Creates a new draft post that summarizes and links to a list of '
        . 'existing posts. Accepts an array of post IDs, typically from a tool like '
        . 'my-plugin/top-posts-by-comments. Always creates the roundup as a draft.',
    'input_schema' => array(
        'type'       => 'object',
        'properties' => array(
            'title'    => array( 'type' => 'string' ),
            'post_ids' => array( 'type' => 'array', 'items' => array( 'type' => 'integer' ) ),
        ),
        'required' => array( 'title', 'post_ids' ),
    ),
    'execute_callback'    => 'my_plugin_create_draft_roundup',
    'permission_callback' => 'my_plugin_can_edit_posts',
    'meta' => array( 'mcp' => array( 'public' => true ) ),
) );

With those descriptions in place, a capable model reliably plans: call top-posts-by-comments with last month’s date range, read the three post IDs back, call create-draft-roundup with those exact IDs. Nobody wrote that sequence down anywhere, it’s a consequence of the abilities being described well enough for the model to connect them itself.

What breaks mid-chain, and how to design around it

Failure modes specific to multi-step chains
Step 2 runs on stale data from step 1
If there's a meaningful delay between calls, a "get post by ID" step should still re-check the post still exists and is in the expected state, not assume step 1's data is still current.
A partial failure leaves a half-finished result
If your roundup-drafting ability fails after inserting the post but before adding all links, make sure it still returns enough information (the post_id it did create) for the model to report accurately or retry cleanly, rather than silently losing track of a partially created post.
The model repeats a step that isn't safe to repeat
Read-only steps like "find top posts" are naturally safe to call twice, writing steps like "create draft" are not, this is exactly why Lesson 6's confirmation-token pattern exists for anything destructive, and why a "create" ability should be designed to be safely re-callable or clearly signal when it was already run.
Assuming the model always picks the 'right' order

Even with well-written descriptions, models don’t always chain steps in the sequence you’d design by hand, particularly with ambiguous requests. Test with realistic, slightly underspecified phrasing, not just the cleanest possible request, that’s what reveals which of your descriptions need to be more explicit about ordering or dependencies.

Test it

Run the full worked example against your own connected client and confirm each step actually happened, not just that a plausible-looking final message came back.

Verifying a multi-step chain actually ran, not just narrated
The draft roundup post exists in wp-admin
Not just described in the chat, an actual post row.
It links to the same three posts the first call found
Cross-check the IDs in the final draft against what the first tool call actually returned.
Re-running the same request behaves sensibly
Either producing a second roundup deliberately, or the model recognizing one already exists, depending on how you've scoped the abilities.

Recap

Multi-step agent workflows aren’t something you configure, they emerge from the model planning a sequence of calls against whatever abilities you’ve registered. Your part of the job is making each ability’s inputs and outputs compose well, IDs over prose, explicit references to the tools that typically feed them, and specific, recoverable errors, so that whatever sequence the model comes up with actually works end to end.

Resources & further reading

← Designing Confirmation-Gated (Human-in-the-Loop) Agent Actions Building a Vendor-Agnostic Setup That Switches LLM Providers →