Comparative Analysis of MCP Principle and Function Call
Release Time : 2025-05-06
Views : 5.4K

Development Background of MCP

The birth of MCP marks that prompt engineering has entered a new stage of development. By providing more structured context information, it significantly enhances the capabilities of the model. When designing prompts, our goal is to integrate more specific information (such as local files, database contents, or real-time network data) so that the model can better understand and solve practical problems.

Limitations of Traditional Methods

Before the emergence of MCP, to solve complex problems, we had to:

  • Manually filter information from the database
  • Use tools to retrieve relevant information
  • Add the filtered information to the prompt one by one

This method works well for simple problems (such as requiring the large model to make summaries), but it becomes increasingly difficult to handle as the complexity of the problem increases.

Emergence of Function Call

To overcome these challenges, many large language model (LLM) platforms (such as OpenAI and Google) have introduced the function call feature. This mechanism allows the model to call predefined functions as needed to obtain data or perform specific operations, greatly improving the degree of automation.

Limitations of Function Call

However, function call also has obvious limitations:

  • High dependence on the platform
  • Differences in API implementation among different LLM platforms
  • Developers must rewrite the code when switching models, increasing development costs
  • Challenges in aspects such as security and interactivity

Design Concept of MCP

In fact, data and tools have always been there. The key lies in how to connect them to the model more intelligently and uniformly. Anthropic designed MCP based on such a need. As a "universal adapter" for AI models, it enables LLMs to easily access data or call tools.

Advantages of MCP

How the Model Intelligently Selects Agents/Tools

The core of MCP is to enable us to conveniently call multiple tools. So, when does the LLM (model) determine which tools to use?

Tool Selection Process

Anthropic provides us with a detailed explanation. When a user asks a question:

  1. The client (Claude Desktop/Cursor) sends the question to the LLM
  2. The LLM analyzes the available tools and decides which one(s) to use
  3. The client executes the selected tool(s) through the MCP Server
  4. The execution result of the tool is sent back to the LLM
  5. The LLM summarizes the execution result and generates natural language to present to the user

How does the model determine which tools to use?

By analyzing the client example code provided by the MCP official, it can be seen that the model relies on the prompt to identify the currently available tools. The specific approach is as follows:

  • Pass the usage descriptions of each tool to the model in text form
  • Enable the model to know which tools are available for selection
  • Make the best choice based on the real-time situation

Analysis of Key Code

async def start(self):
    # Initialize all mcp servers
    for server in self.servers:
        await server.initialize()

    # Get all tools named all_tools
    all_tools = []
    for server in self.servers:
        tools = await server.list_tools()
        all_tools.extend(tools)

    # Format the functional descriptions of all tools into a string for the LLM to use
    tools_description = "\\n".join(
        [tool.format_for_llm() for tool in all_tools]
    )

    # Ask the LLM (Claude) which tools should be used
    system_message = (
        "You are a helpful assistant with access to these tools:\\n\\n"
        f"{tools_description}\\n"
        "Choose the appropriate tool based on the user's question. "
        "If no tool is needed, reply directly.\\n\\n"
        "IMPORTANT: When you need to use a tool, you must ONLY respond with "
        "the exact JSON object format below, nothing else:\\n"
        "{\\n"
        '    "tool": "tool-name",\\n'
        '    "arguments": {\\n'
        '        "argument-name": "value"\\n'
        "    }\\n"
        "}\\n\\n"
        "After receiving a tool's response:\\n"
        "1. Transform the raw data into a natural, conversational response\\n"
        "2. Keep responses concise but informative\\n"
        "3. Focus on the most relevant information\\n"
        "4. Use appropriate context from the user's question\\n"
        "5. Avoid simply repeating the raw data\\n\\n"
        "Please use only the tools that are explicitly defined above."
    )
    messages = [{"role": "system", "content": system_message}]

    while True:
        # Assume that the user message input has been processed here
        messages.append({"role": "user", "content": user_input})

        # Send the system_message and the user message input to the LLM together
        llm_response = self.llm_client.get_response(messages)

Tool Formatting

How is the tool description information formatted?

class Tool:
    """Represents a tool with its properties and formatting."""

    def __init__(
        self, name: str, description: str, input_schema: dict[str, Any]
    ) -> None:
        self.name: str = name
        self.description: str = description
        self.input_schema: dict[str, Any] = input_schema

    # Convert the tool's name/tool's purpose (description) and the tool's required parameters (args_desc) into text
    def format_for_llm(self) -> str:
        """Format tool information for LLM.

        Returns:
            A formatted string describing the tool.
        """
        args_desc = []
        if "properties" in self.input_schema:
            for param_name, param_info in self.input_schema["properties"].items():
                arg_desc = (
                    f"- {param_name}: {param_info.get('description', 'No description')}"
                )
                if param_name in self.input_schema.get("required", []):
                    arg_desc += " (required)"
                args_desc.append(arg_desc)

        return f""" Tool: {self.name} Description: {self.description} Arguments: {chr(10).join(args_desc)} """

Source of Tool Information

Where do the tool's description and input_schema come from? By analyzing the Python SDK source code of MCP:

In most cases, when using the decorator @mcp.tool() to decorate a function, the corresponding name and description directly come from the function name of the user-defined function and the function's docstring, etc.

@classmethod
def from_function(
    cls,
    fn: Callable,
    name: str | None = None,
    description: str | None = None,
    context_kwarg: str | None = None,
) -> "Tool":
    """Create a Tool from a function."""
    func_name = name or fn.__name__  # Get the function name

    if func_name == "<lambda>":
        raise ValueError("You must provide a name for lambda functions")

    func_doc = description or fn.__doc__ or ""  # Get the function docstring
    is_async = inspect.iscoroutinefunction(fn)

Summary: The model determines which tools to use through prompt engineering, that is, by providing structured descriptions of all tools and few-shot examples. On the other hand, Anthropic must have specifically trained Claude. After all, it is its own protocol, and Claude can better understand the tool prompts and output structured tool call JSON code.

Tool Execution and Result Feedback Mechanism

The execution of tools is relatively simple and straightforward. Continuing from the previous step, we send the system prompt (instructions and tool call descriptions) and the user message to the model together, and then receive the model's reply.

Execution Process

After the model analyzes the user's request, it decides whether to call a tool:

  • When no tool is needed: The model directly generates a natural language reply
  • When a tool is needed: The model outputs a structured JSON-formatted tool call request

If the reply contains a structured JSON-formatted tool call request, the client will execute the corresponding tool based on this JSON code.

Code Implementation

async def start(self):
    # As introduced above, how the model selects tools

    while True:
        # Assume that the user message input has been processed here
        messages.append({"role": "user", "content": user_input})

        # Get the output of the LLM
        llm_response = self.llm_client.get_response(messages)

        # Process the output of the LLM (if there is a tool call, execute the corresponding tool)
        result = await self.process_llm_response(llm_response)

        # If the result is different from the llm_response, it means a tool call has been executed (there is additional information)
        # Then resend the result of the tool call to the LLM for processing
        if result != llm_response:
            messages.append({"role": "assistant", "content": llm_response})
            messages.append({"role": "system", "content": result})

            final_response = self.llm_client.get_response(messages)
            logging.info("\\nFinal response: %s", final_response)
            messages.append(
                {"role": "assistant", "content": final_response}
            )
        # Otherwise, it means no tool call has been executed, so directly return the output of the LLM to the user
        else:
            messages.append({"role": "assistant", "content": llm_response})

Error Handling

If there is a problem with the JSON code of the tool call or the model has hallucinations, the system will skip the invalid call request.

Conclusions and Practical Suggestions

Based on the above principle analysis, it can be seen that tool documentation is crucial. The model relies on the tool description text to understand and select applicable tools, which means that carefully written tool names, docstrings, and parameter descriptions are particularly important.

Given that the selection mechanism of MCP is based on prompts, theoretically, any model that can provide corresponding tool descriptions can be used compatibly with MCP.


AIbase
Zhiqi Future, Your AI Solution Think Tank
© 2025AIbase