Tools, Resources, and Prompts

Advanced Tool Patterns

5 min read

The patterns below all answer the same question in different ways: how much work should happen inside one tool call, and how much should be left to the model to orchestrate?

Tool Composition

Two tools that are always used together are often better as one. Each round trip costs a turn, and every turn is a chance for the model to lose the thread:

Two granular tools vs one composed tool

granularcomposed"Summarise what we know a…One user requestsearch_documentsTurn 1 — model calls, reads 20 …Model picks 5Turn 2 — spends context decidin…summarizeTurn 3 — finally summarisessearch_and_summarizeOne call. Ranking and truncatio…Answer

Composition is not free. A composed tool hides its intermediate steps, so when the summary is wrong you cannot tell whether the search or the summariser failed. The rule of thumb: compose when the intermediate result is never independently useful, and keep them separate when the model might legitimately want to stop halfway.

@server.list_tools()
async def list_tools():
    return [
        Tool(
            name="search_and_summarize",
            description="Search documents and return a summary",
            inputSchema={
                "type": "object",
                "properties": {
                    "query": {"type": "string"},
                    "max_results": {"type": "integer", "default": 5}
                },
                "required": ["query"]
            }
        )
    ]

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    if name == "search_and_summarize":
        # Step 1: Search
        results = await search_documents(arguments["query"])

        # Step 2: Summarize
        summary = await generate_summary(results[:arguments.get("max_results", 5)])

        return [TextContent(type="text", text=summary)]

Streaming Results

For long-running operations, stream results:

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    if name == "analyze_large_dataset":
        results = []

        async for batch in process_in_batches(arguments["data"]):
            results.append(batch)
            # Yield intermediate results
            yield [TextContent(type="text", text=f"Processed batch: {len(results)}")]

        # Final result
        yield [TextContent(type="text", text=json.dumps(results))]

Confirmable Actions

For dangerous operations, require confirmation:

Tool(
    name="delete_all_files",
    description="Delete all files in a directory. DESTRUCTIVE ACTION.",
    inputSchema={
        "type": "object",
        "properties": {
            "directory": {"type": "string"},
            "confirm": {
                "type": "boolean",
                "description": "Must be true to proceed"
            }
        },
        "required": ["directory", "confirm"]
    }
)

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    if name == "delete_all_files":
        if not arguments.get("confirm"):
            return [TextContent(
                type="text",
                text="Action not confirmed. Set confirm=true to proceed."
            )]
        # Proceed with deletion
        await delete_files(arguments["directory"])

Tool Dependencies

Build tools that depend on other tools:

class ToolRegistry:
    def __init__(self):
        self.tools = {}

    def register(self, name, handler):
        self.tools[name] = handler

    async def call(self, name, arguments):
        return await self.tools[name](arguments)

registry = ToolRegistry()

async def get_user_handler(args):
    return await db.get_user(args["id"])

async def get_user_orders_handler(args):
    # Depends on get_user
    user = await registry.call("get_user", {"id": args["user_id"]})
    return await db.get_orders(user["id"])

registry.register("get_user", get_user_handler)
registry.register("get_user_orders", get_user_orders_handler)

Next, we'll explore advanced resource patterns. :::

Quiz

Module 3 Quiz: Tools, Resources, and Prompts

Take Quiz
Was this lesson helpful?

Sign in to rate