Introduction to MCP

Model Context Protocol (MCP) is a new way to connect AI language models with external data sources and tools. At its heart, MCP is an open protocol introduced by Anthropic in late 2024. It standardises how applications provide context to LLMs (Large Language Models).

MCP - when running - does a dynamic discovery of the available tools and uses it to return a response that user wanted. This blog post I will first explain different parts of an MCP server and then go on explain how they work with each other and finally explain how MCP dynamically discovers the which tool to use and how.

I am assuming you already know what MPC is - If you would like to know more about it you can take a look at Model Context Protocol Documentation.

Core Components

MCP follows a client-server architecture where a host application (Claude Desktop Application) can connect to multiple servers such as filesystem , Brave Search and many more

This separation of concerns allows the LLM to remain focused on reasoning (i know LLM’s cannot reason) while dedicated servers handle specific functionalities. Here are all the part that makes MCP

  1. MCP Hosts: Programs like Claude Desktop, IDEs, or AI tools that want to access data through MCP
  2. MCP Clients: Protocol clients that maintain 1:1 connections with servers
  3. MCP Servers: Lightweight programs you write that does get work done for a server through the standardised protocol
  4. Local Data Sources: Your computer’s files, databases, and services that MCP servers can securely access
  5. Remote Services: External systems available over the internet that MCP servers can connect to

I am going to spend the rest of the post talking about the MCP servers - the code you write to run with the desktop client.

Important parts of an MCP Server

MCP servers can/may have three main types of capabilities and two managers which helps manage context and the server lifecycle.

1. Resources

Resources in MCP are like GET endpoints in a REST API. They provide data to LLMs but shouldn’t perform significant computation or have side effects. Resources are identified by URIs and can be static or dynamic.

Example:

1
2
3
4
5
6
7
8
9
@mcp.resource("config://app")
def get_config() -> str:
    """Static configuration data"""
    return "App configuration here"

@mcp.resource("users://{user_id}/profile")
def get_user_profile(user_id: str) -> str:
    """Dynamic user data"""
    return f"Profile data for user {user_id}"

2. Tools

Tools are how LLMs take actions through your server. Unlike resources, tools are expected to perform computation and have side effects. They’re similar to POST endpoints in a REST API.

Example:

1
2
3
4
@mcp.tool()
def calculate_bmi(weight_kg: float, height_m: float) -> float:
    """Calculate BMI given weight in kg and height in meters"""
    return weight_kg / (height_m**2)

Tools are defined with the @mcp.tool() decorator, which uses Python type hints and docstrings to automatically generate tool definitions, making them self-documenting and ensuring type safety.

3. Prompts

Prompts are reusable templates that help LLMs interact with your server effectively. Think of these as pre-defined conversation starters or workflows.

Example:

1
2
3
@mcp.prompt()
def review_code(code: str) -> str:
    return f"Please review this code:\n\n{code}"

4. Context Object

The Context object gives your tools and resources access to MCP capabilities:

1
2
3
4
5
6
7
8
@mcp.tool()
async def long_task(files: list[str], ctx: Context) -> str:
    """Process multiple files with progress tracking"""
    for i, file in enumerate(files):
        ctx.info(f"Processing {file}")
        await ctx.report_progress(i, len(files))
        data, mime_type = await ctx.read_resource(f"file://{file}")
    return "Processing complete"

The Context object provides methods for reporting progress, logging information, and accessing other resources, making it easier to build complex tools.

5.Lifespan Management

For more complex applications, you might need to manage application lifecycle with type-safe context:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@asynccontextmanager
async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
    """Manage application lifecycle with type-safe context"""
    # Initialize on startup
    db = await Database.connect()
    try:
        yield AppContext(db=db)
    finally:
        # Cleanup on shutdown
        await db.disconnect()

# Pass lifespan to server
mcp = FastMCP("My App", lifespan=app_lifespan)

So, How do they work with each other?

MCP Communication Flow

Let’s Imagine a you interacting with an MCP-enabled application, such as Claude Desktop. The process begins when the user poses a question or request. This input is first handled by the host application, which then sends the request to the MCP client. The client establishes a connection with the appropriate MCP server, initiating a handshake to ensure both sides are ready to communicate.

Once the connection is acknowledged by the server, the client proceeds to discover what capabilities—such as tools or resources—the server can provide. The server responds with a list of these available options, effectively advertising its functionality. Based on the user’s original intent, the client then decides whether to request a specific resource or invoke a tool, and sends the appropriate request to the server.

If the requested operation requires data from an external service or another data source, the server reaches out to that service, retrieves the necessary information, and processes it as needed. The server then formats the response and sends it back to the client. Before presenting the final answer to the user, the client may further format the response to ensure it is suitable for AI consumption and user-friendly display.

Ultimately, the host application delivers the answer back to the user, completing the cycle. This orchestrated flow ensures that each component in the MCP ecosystem plays a well-defined role, resulting in a seamless and efficient user experience.

This flow creates a seamless experience where users can interact with their data and external services through natural language conversations with the LLM.

Here is an exmample:

External ServiceMCP ServerMCP ClientMCP HostUserExternal ServiceMCP ServerMCP ClientMCP HostUserAsk a questionProcess requestInitialize connectionAcknowledge connectionDiscover capabilitiesReturn available tools/resourcesRequest resource or invoke toolAccess external serviceReturn dataReturn formatted responseFormat for AI consumptionPresent answer to user

We have talked enough about how MCP selects the best tool for the job but how does MCP client know which is the best tool?.

Tool Discovery and Selection Deep Dive

One of the most powerful aspects of MCP is how it enables Claude to autonomously discover and select appropriate tools based on context and MCP’s architecture choices makes it really easy.

MCP is built on JSON-RPC 2.0, which provides a standardised message format for communication between clients and servers. This enables Claude to:

  1. Receive Tool Metadata: When connecting to an MCP server, Claude receives detailed metadata about each available tool, including:

    • Name and description
    • Required parameters and their types
    • Expected return values
    • Documentation strings
  2. Understand Tool Capabilities: Claude parses this metadata to build an internal representation of what each tool can do, similar to how humans understand tool functionality from documentation. When a user asks a question, Claude performs a sophisticated matching process to determine which tools might help:

    a. Query Analysis: Claude analyses the semantic meaning of the user’s query.

    b. Tool Matching: It compares this meaning against the capabilities of available tools.

    c. Parameter Extraction: Claude identifies relevant information from the user’s query that can serve as parameters for the selected tools.

    d. Confidence Assessment: Claude evaluates how confident it is that a particular tool will help answer the query.

This process happens automatically and invisibly to the user, who simply sees Claude providing answers.

Claude’s Tool Selection Process

When a user asks a question and Claude has access to multiple MCP tools and resources, Claude needs to decide which ones are relevant. This matching process involves several sophisticated steps:

  1. Semantic Understanding of the Query: First, Claude analyzes the user’s question to understand the intent and requirements. For example, if you ask “What’s the weather in Bangalore?”, Claude identifies this as a request for weather information about a specific location.

  2. Tool Capability Analysis: Claude examines each available MCP tool by looking at:

    • The tool’s name and description
    • The documentation strings (docstrings) that explain what the tool does
    • The input parameters the tool expects
    • The type of output the tool returns
  3. Relevance Scoring: Claude then scores each tool based on how well it matches the user’s intent. This isn’t just simple keyword matching - it’s a deep semantic comparison between what the user needs and what each tool provides.

  4. Parameter Extraction and Preparation: For tools that seem relevant, Claude extracts appropriate parameter values from the user’s query. Taking our weather example, Claude would recognize “London” as the city parameter for a weather-checking tool.

  5. Confidence Assessment: Before actually invoking a tool, Claude evaluates its confidence that the selected tool will help with the user’s request. If confidence is high, Claude proceeds with using the tool.

  6. Multi-Tool Planning: For complex queries, Claude may determine that multiple tools need to be used in sequence. It then plans the order of operations, considering how the output of one tool might serve as input for another.

What makes this process particularly sophisticated is how it relies on Claude’s general reasoning capabilities rather than hard-coded rules. Claude is essentially “reading” the documentation of each tool and making judgments about their applicability to the current situation, much like a human developer would decide which library function to call.

This matching process happens invisibly to the user, who simply experiences Claude providing answers without needing to specify which tools to use or how to use them. The entire process is part of what makes MCP particularly powerful compared to traditional API integration approaches where explicit function calls must be programmed.

And that’s it for today

Resources