Engineering
August 26, 2025

Making MCP Tool Use Feel Natural with Context-Aware Tools

Bob Remeika
,
Co-founder and CEO
,

If you’ve ever had to spell out to an AI exactly which tool to use, you already know how unnatural today’s MCP interactions can feel.

Working with LLMs inside MCP clients often feels less natural than it should. Instead of simply asking, “What is Acme Co’s data retention policy?”, users frequently find themselves spelling out instructions like, “Use the retrieval tool to answer this.” The problem isn’t the LLM’s reasoning ability—it’s the lack of meaningful context in how tools are described. With vague, generic labels like “Knowledge base retrieval tool,” models can’t reliably distinguish when to use retrieval versus web search. The result is an awkward, unnatural interaction pattern that interrupts the flow of conversation.

Why Knowledge Tools Struggle with Routing

Knowledge tools—those that exist to bring in relevant context at inference-time—are particularly prone to routing problems. The core issue is that these tools aren’t usually very “self-aware.” A retrieval tool might just be described as “Knowledge base retrieval tool”, without any indication of whether it’s stocked with HR policies, engineering docs, or customer contracts. Similarly, a text-to-SQL tool can advertise itself as “Query a Postgres database” but provide no sense of what tables or data actually live in that database.

From the LLM’s perspective, this lack of specificity makes routing almost a guessing game. When a user asks, “What’s our data retention policy?”, the model might default to web search because it can’t confidently tell whether the retrieval tool has that policy. Or if a user asks for “How many invoices did we issue last quarter?”, the LLM won’t know whether the text-to-SQL tool has access to finance data or if it should try another route.

Some MCP clients, like Cursor, have introduced a “Rules” feature to compensate. Rules let users hardcode instructions like “always use the retrieval tool for compliance-related queries.” While this can help, it’s really just a clunky workaround: it requires manual setup, doesn’t scale well across different contexts, and still breaks the natural conversational flow.

This is exactly where Context-Aware Tools come in. By dynamically generating descriptions based on the actual content or schema available, these tools advertise themselves in a way that helps the LLM make better decisions. Instead of a generic “retrieval tool,” the description might read “Retrieve HR compliance policies and employee handbook content”. Instead of a vague “text-to-SQL tool,” it could say “Query financial transactions and invoice records from Postgres.” These richer, context-specific signals give the LLM the confidence to pick the right tool without handholding, making interactions far more accurate and seamless.

Generic descriptions (left) compared with Context-Aware descriptions (right)

Context-Aware Tools

The idea behind Context-Aware Tools is simple: instead of describing tools in static, generic terms, we let them describe themselves dynamically based on the content they actually expose. A “Knowledge base retrieval tool” becomes “Retrieve HR compliance policies and employee handbook details.” A “Text-to-SQL tool” becomes “Query finance and operations data including invoices, transactions, and quarterly revenue reports.”

Why does this matter? Because tool routing is fundamentally a classification problem. The LLM looks at your question, then tries to map it to the most relevant tool. If every tool description sounds the same—“retrieval tool,” “search tool,” “query tool”—the model has no strong signals to make that decision. But when descriptions are context-aware, the mapping becomes obvious. Ask about “data retention policy,” and the model sees a retrieval tool labeled with “compliance policies” and chooses it confidently. Ask about “last quarter’s invoices,” and the text-to-SQL tool advertising “finance data” becomes the clear winner.

Generic descriptions often lead to incorrect tool routing
Context-aware descriptions give the LLM high confidence to choose Retrieve tool

This approach also removes the burden from users to over-engineer prompts or rely on brittle rules. In today’s MCP clients, you often end up writing instructions like “Use the retrieval tool to answer this question” or configuring explicit routing rules (as Cursor does with its “Rules” feature). Both options solve the problem in the short term, but at the cost of natural interaction. Context-Aware Descriptions flip the script: the LLM doesn’t need nudges or hacks, because it has enough semantic context to figure things out on its own.

At Ragie, we’ve implemented this by sampling and summarizing your knowledge base content, then updating tool descriptions dynamically through the MCP server. Every time new documents are ingested, the description evolves to reflect the most relevant topics. The result is a living, context-aware tool description that grows with your data and continuously improves tool routing.

How It Works

To make Context-Aware Descriptions possible, we had to rethink how the MCP server exposes its tools. In the standard Model Context Protocol (MCP), the list/tools command returns a static set of tool definitions, each with fixed descriptions. That works fine for simple, single-tenant setups, but it quickly breaks down in multi-tenant applications like Ragie, where each customer’s knowledge base is unique.

Our solution was to make list/tools dynamic. When an MCP client asks for available tools, the response now depends on the tenant tied to the API key that made the request. Behind the scenes, we sample and summarize content from that tenant’s knowledgebase and update the tool descriptions on the fly. So instead of seeing a static “retrieval tool,” a user connected to a compliance dataset might see “Retrieve HR policies, employee handbook details, and data retention rules.”

To build this, we created and are open-sourcing a small library called Dynamic FastMCP. It extends the official Python fastmcp server framework by making it easy to plug into protocol-level commands like list/tools and generate their results dynamically. With Dynamic FastMCP, you can define Dynamic Tools whose descriptions are resolved at runtime, taking into account the user, tenant, or request context. This design is particularly helpful in multi-tenant SaaS environments, where one MCP server may be serving hundreds of different customers, each with different data domains.

Here’s what that means in practice:

  • Per-tenant context: The server ties the incoming request to a tenant via the API key.

  • Partition-aware descriptions: A retrieval tool dynamically generates its description based on the data stored in that tenant’s partition.

  • Dynamic list/tools response: The MCP client always sees tool descriptions that are up to date and specific to their data.

  • Open-source foundation: The Dynamic FastMCP package (released today) makes it straightforward to implement your own dynamic tools, without rewriting MCP internals.

With this setup, LLMs no longer have to guess. The right tool to route to is obvious because the tool descriptions themselves carry the context of the data they can access.

Example: Defining a Dynamic Tool

With Dynamic FastMCP, you can define a tool whose description is generated at runtime, based on the tenant or request context. Here’s a simple example that shows how a DynamicEcho tool can customize its description depending on the user making the request:

from dynamic_fastmcp.dynamic_fastmcp import DynamicFastMCP, DynamicTool
from mcp.server.fastmcp import Context

mcp = DynamicFastMCP()

@mcp.tool()
class DynamicEcho(DynamicTool):
    def name(self) -> str:
        return "dynamic_echo"

    def structured_output(self) -> bool | None:
        return True

    # Description is generated dynamically for each request
    async def handle_description(self, ctx: Context) -> str:
        request = ctx.request_context.request
        assert request is not None
        return f"Echo tool for user: {request.user.username}"

    # Behavior can also use the request context
    async def handle_call(self, text: str, ctx: Context) -> str:
        request = ctx.request_context.request
        return f"Echo to {request.user.username}: {text}"

When an MCP client calls list/tools, this tool doesn’t return a generic description. Instead, it returns something like:

dynamic_echo  
Description: Echo tool for user: bob

This same mechanism powers Context-Aware Descriptions in Ragie: instead of echoing a username, the tool description summarizes what’s inside your knowledgebase. The LLM sees exactly what kind of content the tool can access, which makes routing decisions reliable and natural.

Compatibility with the Official MCP Python SDK

Dynamic FastMCP is an additive layer over the official MCP Python SDK—no forks, no protocol changes, and no client-side modifications required. Under the hood, we subclass and extend the SDK’s FastMCP server and ToolManager so we can compute tool descriptions at request time, while still emitting the same MCP wire types that clients expect.

  • Built on FastMCP primitives. We import and extend FastMCP and the SDK’s tool abstractions (Tool, ToolManager), so your server, tools, resources, and authentication continue to use the official surfaces (mcp.server.fastmcp). If you’ve built an MCP server before with the SDK’s FastMCP, the shape of your code remains the same.

  • Same protocol; dynamic results. Instead of returning a static list from list/tools, our DynamicFastMCP resolves dynamic tools just-in-time by calling each tool’s handle_description(ctx) and merging those into the normal list_tools() result. Clients still receive standard Tool payloads—only the descriptions are context-aware and tenant-specific.

  • Request context stays first-class. We rely on the SDK’s Context / request-context facilities to identify the tenant from the API key and to thread auth/user info into description generation—exactly how the SDK documents request context and FastMCP properties.
  • Multi-tenant ready. Because we compute descriptions per request, the same server instance can serve many tenants. The MCP contract to the client is unchanged; only the content of descriptions varies with the partition tied to the caller’s API key. This plays nicely with the SDK’s supported transports (including Streamable HTTP / ASGI mounting).
  • Ecosystem-aligned. FastMCP originated inside (and remains tightly aligned with) the official SDK; FastMCP 1.0 was incorporated into the SDK, and FastMCP 2.0 continues as the actively maintained framework many developers use alongside the SDK. Dynamic FastMCP follows that model by extending, not replacing, SDK surfaces.

What this means in practice

  • Keep your existing client integrations. Cursor, Claude Desktop, ChatGPT MCP, etc., all continue to call list/tools and call_tool as usual—no changes needed on the client side. (Features like Cursor’s “Rules” still work, but you’ll need them less because descriptions now carry real intent.)

  • Keep your existing server code. Functions decorated with @mcp.tool() still work. You can add dynamic tools by decorating a DynamicTool class, which provides handle_description(ctx) for on-the-fly descriptions—everything else (parameter schemas, structured outputs) still flows through the SDK’s Pydantic-based plumbing.
  • Upgrade path is incremental. Start by mixing in one DynamicTool alongside your existing static tools. If you like the routing improvements, migrate more tools to context-aware descriptions over time.

In short: Dynamic FastMCP leverages the official SDK’s server, context, and tool schemas; it just makes the results of list/tools smarter—so LLMs have the signals they need to route to the right tool, without any protocol changes or client rewrites. 

Try It Today with Ragie

At Ragie, we’ve taken this one step further: our Streamable HTTP MCP server now supports Context-Aware Descriptions out of the box. That means your agents can connect over a modern, low-latency transport and immediately benefit from tools that describe themselves dynamically based on your data. No brittle rules. No clunky prompt engineering. Just natural, confident tool routing that makes your AI agents feel smarter and more intuitive.

This feature is available now. Connect your knowledge base to Ragie, try out Context-Aware Tools in your MCP clients, and see just how effortless agent interactions can become.

👉 Get started with Ragie today, or book a meeting to learn about building better agent experiences with Context-Aware MCP Tools.

Share