CourseModel Context Protocol · Module 10: Design Patterns and Agents · part 70 of 83
Part 70 · Module 10: Design Patterns and Agents

Topic 3: Agentic patterns

16 min read·22 Sept 2026

An agentic pattern is an arrangement of models, hosts, and servers: who decides what, who talks to which server, and whose context holds what. Three are common enough to name.

Single agent with many servers

The simplest arrangement: one Host, one model, several servers. The host namespaces each server's tools (notes__search_notes, calendar__check_availability) and the model chooses among all of them. The canonical Host already supports this, since it takes a dict of clients.

python
"""Single agent, many servers: one Host, two stdio servers, one model choosing among all tools."""
from __future__ import annotations

import anyio

from mcp import Client

from examples.m10_common import KeywordAgent, stdio_params
from notes_assistant.host import Host

QUESTIONS = [
    "What do my notes say about the half-life of caffeine?",
    "Is the sleep lab free the week of 19 October?",
    "How many participants will the nap study have, and is the sleep lab free the week of 19 October?",
]


async def main() -> None:
    async with (
        Client(stdio_params("-m notes_assistant.server")) as notes,
        Client(stdio_params("examples/m10_calendar_server.py")) as calendar,
    ):
        host = Host({"notes": notes, "calendar": calendar}, chat_fn=KeywordAgent())
        specs = await host.load_tools()
        print("the model sees:", [s["function"]["name"] for s in specs])
        for question in QUESTIONS:
            answer = await host.ask(question)
            print(f"\nQ: {question}")
            print("   calls:", [f"{c.name}({', '.join(f'{k}={v!r}' for k, v in c.arguments.items())})" for c in answer.calls])
            print("   A:", answer.text.replace("\n", " ")[:110])


if __name__ == "__main__":
    anyio.run(main)

Code explained

  • In simple words: one assistant with two phone lines, one to the notes and one to the calendar.
  • What happens:
    • Two stdio subprocesses start, one per server, each with its own Client.
    • Host({"notes": notes, "calendar": calendar}, ...) gives the model both tool sets with prefixes, plus the host's own read_resource.
    • Three questions go through the same host: one for each server, and one compound question that needs both.
  • Comes out:
    text
    the model sees: ['notes__search_notes', 'notes__create_note', 'calendar__check_availability', 'calendar__list_bookings', 'read_resource']
    
    Q: What do my notes say about the half-life of caffeine?
       calls: ["notes__search_notes(query='half life caffeine')", "read_resource(uri='notes://coffee-and-focus')"]
       A: Half-life is about 5 hours, so a 4 pm coffee still leaves half the dose at 9 pm. [coffee-and-focus]
    
    Q: Is the sleep lab free the week of 19 October?
       calls: ["calendar__check_availability(equipment='sleep-lab', week_of='2026-10-19')"]
       A: {   "equipment": "sleep-lab",   "week_of": "2026-10-19",   "free": true,   "booked_by": null }
    
    Q: How many participants will the nap study have, and is the sleep lab free the week of 19 October?
       calls: ["calendar__check_availability(equipment='sleep-lab', week_of='2026-10-19')"]
       A: {   "equipment": "sleep-lab",   "week_of": "2026-10-19",   "free": true,   "booked_by": null }

    The first two questions are routed correctly with no help: the calendar question's words ("sleep", "lab", "free", "week") overlap the calendar tool's description far more than the notes tool's. The third shows the pattern's limit with a weak model: the compound question gets one tool call and a half answer. The calendar part won on word overlap; the nap study part was silently dropped. A strong real model usually makes both calls, but "usually" is the problem: as questions and tool lists grow, one context has to hold every tool definition, every intermediate result, and the whole plan.

The rest of this course is yours to keep

This course is bought on its own, once, and stays readable afterwards, including the parts added to it later.