I want to share my findings on how to make MCP (Model Context Protocol) Server tools more responsive and adaptive to user needs by allowing configurable tool descriptions. This can significantly enhance the user experience by providing more relevant and context-aware descriptions for specific projects or tasks.
If you are not yet familiar with MCP, I recommend checking out the official documentation. In short, MCP is a protocol that allows different AI models and tools to communicate and work together seamlessly. In practice, MCP servers are small plugins for your AI agent or chat tool (for example, Claude Desktop) that provide specific functionalities, such as web browsing, code execution, or data retrieval.
MCP Tool Descriptions
Usually, an MCP server tool definition looks like this:
{
"name": "get-webpage",
"description": "A tool for retrieving the content of a webpage. It accepts a URL as input and returns the HTML content of the page.",
"parameters": {
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "The URL of the webpage to visit."
}
},
"required": ["url"]
}
}
Using Python it might look like this:
from fastmcp import FastMCP
mcp = FastMCP("Demo 🚀")
@mcp.tool
def get_webpage(url: str) -> str:
"""A tool for retrieving the content of a webpage. It accepts a URL as input and returns the HTML content of the page."""
return f"Fetching content from {url}..."
The description
field is crucial—it informs the AI model about the tool’s purpose and how to use it. In the example above, the description is static, “hardcoded.” If we build an MCP server and distribute it to users, their AI agent’s LLMs will always see the same description for the tool, regardless of the context or the user’s specific needs.
Introducing Configurable Descriptions
To make tool descriptions more adaptable, we can introduce a mechanism to configure them dynamically. This can be achieved by allowing users to set custom descriptions when they initialize the MCP server, or by using an external configuration file.
Let’s review an example. We have two different MCP servers, each exposing a get-webpage
tool. The purpose of the tool is similar, but the implementation is different:
- The first MCP server is designed for general web browsing and returns the full HTML.
- The second focuses on content extraction, returning only the main content while stripping ads and distractions.
A user might need to use both servers, but with different descriptions for the same tool. Configurable descriptions solve this by letting each MCP server define its own tailored description for get-webpage
.
Example Implementation
Server 1: Rocket 🚀
mcp = FastMCP("Demo MCP Server. Rocket 🚀")
get_webpage_description_prefix = os.environ.get("GET_WEBPAGE_DESCRIPTION_PREFIX", "")
get_webpage_description_suffix = os.environ.get("GET_WEBPAGE_DESCRIPTION_SUFFIX", "")
@mcp.tool(
description=(
f"{get_webpage_description_prefix}"
"A tool for retrieving the content of a webpage. "
"It accepts a URL as input and returns the HTML content of the page."
f"{get_webpage_description_suffix}"
),
)
def get_webpage(url: str) -> str:
return f"Fetching content from {url}..."
Server 2: Extracto 🧽
mcp = FastMCP("Content Extractor. Extracto 🧽")
get_webpage_description_prefix = os.environ.get("GET_WEBPAGE_DESCRIPTION_PREFIX", "")
get_webpage_description_suffix = os.environ.get("GET_WEBPAGE_DESCRIPTION_SUFFIX", "")
@mcp.tool(
description=(
f"{get_webpage_description_prefix}"
"A tool for retrieving the content of a webpage. "
"It accepts a URL as input and returns the HTML content of the page."
f"{get_webpage_description_suffix}"
),
)
def get_webpage(url: str) -> str:
return f"Fetching content from {url}..."
When configuring the AI agent, we can set different environment variables for each server:
{
"mcpServers": {
"rocket-server": {
"command": "/path/to/server/rocket.py",
"env": {
"GET_WEBPAGE_DESCRIPTION_PREFIX": "Get full webpage content. All HTML structure is preserved. ",
"GET_WEBPAGE_DESCRIPTION_SUFFIX": "IMPORTANT. Use this tool when you need to see the entire webpage."
}
},
"content-extractor": {
"command": "/path/to/server/extractor.py",
"env": {
"GET_WEBPAGE_DESCRIPTION_PREFIX": "Get main content of the webpage. Ads and other distractions are removed. ",
"GET_WEBPAGE_DESCRIPTION_SUFFIX": "IMPORTANT. Use this tool when you need to focus on the main content of the page."
}
}
}
}
With this setup, the AI model sees the correct description for each context, ensuring it chooses the right tool.
Another Use Case: Reusing MCP Servers
Often, we assume a user will add an MCP server once and it will cover all their needs. But many cases require the same MCP tools in different contexts. A typical example is using a service with two different accounts.
Example: Email Accounts
Suppose we have an MCP server for email. It accepts IMAP/SMTP credentials and allows reading/sending emails. A user has two accounts—work and personal. Connecting twice is easy:
{
"mcpServers": {
"email-work": {
"command": "/path/to/server/email.py",
"env": {
"IMAP_SERVER": "imap.work.com",
"IMAP_USER": "user@work.com"
}
},
"email-personal": {
"command": "/path/to/server/email.py",
"env": {
"IMAP_SERVER": "imap.personal.com",
"IMAP_USER": "user@personal.com"
}
}
}
}
The problem: both have the same tool description. The LLM cannot tell them apart and might use the wrong one.
Static version:
mcp = FastMCP("Email Server 📧")
@mcp.tool(
description=(
"A tool for sending emails. "
"It accepts the email content and recipient address as input."
),
)
def send_email(to: str, subject: str, body: str) -> str:
return f"Sending email to {to} with subject '{subject}' and body:\n{body}"
Improved version with configurable descriptions:
mcp = FastMCP("Email Server 📧")
mail_box_name = os.environ.get("MAIL_BOX_NAME", "")
@mcp.tool(
description=(
f"This is mailbox {mail_box_name}. Use this tool only when working with {mail_box_name} emails. "
"A tool for sending emails. "
"It accepts the email content and recipient address as input. "
f"IMPORTANT. Use this tool only when you need to send email from {mail_box_name} mailbox."
),
)
def send_email(to: str, subject: str, body: str) -> str:
return f"Sending email to {to} with subject '{subject}' and body:\n{body}"
Now, configure each instance with a custom name:
{
"mcpServers": {
"email-work": {
"command": "/path/to/server/email.py",
"env": {
"IMAP_SERVER": "imap.work.com",
"IMAP_USER": "user@work.com",
....other env vars...
"MAIL_BOX_NAME": "Work"
}
},
"email-personal": {
"command": "/path/to/server/email.py",
"env": {
"IMAP_SERVER": "imap.personal.com",
"IMAP_USER": "user@personal.com",
....other env vars...
"MAIL_BOX_NAME": "Personal"
}
}
}
}
Now, if a user types “Send an email to my boss from my work account”, the model will correctly pick the Work mailbox tool. In fact, it may infer the right choice even without the phrase “work account”, since “boss” usually implies a work context.
MCP SDK is important
In examples above, I used the FastMCP SDK to demonstrate how tools descriptions can be configured. FastMCP makes it easy to build MCP servers in Python, and it supports dynamic descriptions out of the box.
However, not every MCP SDK supports this feature. I saw some SDKs where it is not possible. Maybe they will add it in the future or already added it. If you are building an MCP server and want to use configurable descriptions, check if your SDK supports it or consider switching to one that does.
Summary
Configurable tool descriptions may look like a small tweak, but they make MCP servers far more powerful and reusable:
- They let the same tool adapt to different contexts (full HTML vs content extraction).
- They allow the same MCP server to be reused across accounts (work vs personal).
- They give AI agents clearer signals, reducing the chance of confusion and misuse.
With just a bit of extra flexibility in tool definitions, we can make MCP setups much smarter, more user-friendly, and easier to scale.