FastMCP, a python framework for building MCP, is better than the official SDK!

Written by
Caleb Hayes
Updated on:June-18th-2025
Recommendation

FastMCP, the new favorite of Python developers, makes your MCP development more efficient!

Core content:
1. Introduction to the FastMCP framework and its relationship with the official SDK
2. New features of FastMCP 2.0: client support, server combination, etc.
3. Development example: Implementing a mathematical operation intelligent question and answer application based on FastMCP

Yang Fangxian
Founder of 53A/Most Valuable Expert of Tencent Cloud (TVP)
This article introduces FastMCP, a Python framework that is better than the official SDK.

Compared with the official SDK, FastMCP's API design is simpler, development efficiency is higher, and it has stronger scalability. It supports multiple client/server transmission modes (Stdio, SSE, memory) and resource template mechanisms, greatly reducing the development threshold of MCP servers and clients .

The biggest feeling I got from using FastMCP for development is that compared with the official SDK, FastMCP greatly reduces the client development cost (you can create an MCP client with just one line of code).

This article is the sixth in the MCP series. The main contents of this article are:

  1. Introduction to FastMCP
  2. Development example: Implementation of intelligent question-answering application for mathematical operations based on FastMCP

FastMCP

  • Project address: https://github.com/jlowin/fastmcp
  • Project documentation address: https://gofastmcp.com/getting-started/welcome

Relationship between FastMCP and official SDK

FastMCP is a standard framework for building MCP servers and clients. FastMCP 1.0 has been incorporated into the official MCP Python SDK.

Currently, FastMCP has been updated to version 2.0. Version 2.0 significantly expands the basic server building capabilities of version 1.0 by introducing complete client support, server combination, OpenAPI/FastAPI integration, remote server proxy, built-in testing tools and other functions.

Why choose FastMCP?

The MCP protocol is powerful, but its implementation involves a lot of repetitive work - including server settings, protocol handlers, content type handling, and error management. FastMCP handles all the complex protocol details and server management, allowing developers to focus on building high-quality tools. Its design features include:

  • High-level abstraction : Usually only a decorator is needed to define the functionality;
  • Continuous innovation : The core concepts of FastMCP 1.0 have been contributed to the official MCP SDK, and FastMCP 2.0 (current project), as an actively developed version, has added many enhancements, including: powerful client libraries, server proxy and composition modes, OpenAPI/FastAPI integration, and other extension capabilities.

Design goals of FastMCP

  • Fast : High-level interfaces reduce the amount of code and speed up the development process
  • Simple : Building an MCP server with minimal boilerplate code
  • Conform to Python habits : Let Python developers get started naturally
  • Complete : Full support for MCP core specification server and client implementations

Development Example

Install

Run the following command to install FastMCP:

uv pip install fastmcp

Server Implementation

from  fastmcp  import  FastMCP

mcp = FastMCP(name= "MyAssistantServer" )


@mcp.tool()
def add (a: float, b: float)  -> float: 
    """Addition operation

    parameter:
    a: the first number
    b: the second number

    return:
    The sum of two numbers
    """

    return  a + b


@mcp.tool()
def subtract (a: float, b: float)  -> float: 
    """Subtraction operation

    parameter:
    a: the first number
    b: the second number

    return:
    The difference between two numbers (a - b)
    """

    return  a - b


@mcp.tool()
def multiply (a: float, b: float)  -> float: 
    """Multiplication operation

    parameter:
    a: the first number
    b: the second number

    return:
    Product of two numbers
    """

    return  a * b


@mcp.tool()
def divide (a: float, b: float)  -> float: 
    """Division operation

    parameter:
    a: dividend
    b: divisor

    return:
    Quotient of two numbers (a / b)

    abnormal:
    ValueError: when the divisor is zero
    """

    if  b ==  0 :
        raise  ValueError( "divisor cannot be zero" )
    return  a / b


if  __name__ ==  "__main__" :
    mcp.run(transport= 'sse' , host= "127.0.0.1" , port= 8001 )

Client Implementation

An MCP client can be created with just one line of code (specifying how to connect to the server):

async def main () :  
    # Test the functionality of the mcp client
    async with  Client( "http://127.0.0.1:8001/sse"as  mcp_client: 
        tools =  await  mcp_client.list_tools()
        print( f"Available tools:  {tools} " )
        result =  await  mcp_client.call_tool( "add" , { "a"5"b"3 })
        print( f"Result:  {result[ 0 ].text} " )

Mathematical operation intelligent question and answer application

The mathematical operation intelligent question and answer application based on FastMCP is implemented as follows (for detailed design ideas, please read the previous article: MCP: Programming Practice, Hand-in-hand Teaching You to Implement Mathematical Operation Intelligent Question and Answer Application)

class LLMClient : 
    """LLM client, responsible for communicating with the Large Language Model API"""

    def __init__ (self, model_name: str, url: str, api_key: str)  ->  None : 
        self.model_name: str = model_name
        self.url: str = url
        self.client = OpenAI(api_key=api_key, base_url=url)

    def get_response (self, messages: list[dict[str, str]])  -> str: 
        """Send a message to LLM and get a response"""
        response = self.client.chat.completions.create(
            model=self.model_name,
            messages=messages,
            stream = False
        )
        return  response.choices[ 0 ].message.content


class ChatSession : 
    """Chat session, handles user input and LLM responses, and interacts with MCP tools"""

    def __init__ (self, llm_client: LLMClient, mcp_client: Client, )  ->  None : 
        self.mcp_client: Client = mcp_client
        self.llm_client: LLMClient = llm_client

    async def process_llm_response (self, llm_response: str)  -> str: 
        """Process LLM response, parse tool call and execute"""
        try :
            # Try to remove possible markdown formatting
            if  llm_response.startswith( ''``json' ):
                llm_response = llm_response.strip( '```json' ).strip( '```' ).strip()
            tool_call = json.loads(llm_response)
            if "tool" in  tool_call  and "arguments" in  tool_call:
                # Check if the tool is available
                tools =  await  self.mcp_client.list_tools()
                if  any(tool.name == tool_call[ "tool"for  tool  in  tools):
                    try :
                        # Execute tool call
                        result =  await  self.mcp_client.call_tool(
                            tool_call[ "tool" ], tool_call[ "arguments" ]
                        )

                        return f"Tool execution result:  {result} "
                    except  Exception  as  e:
                        error_msg =  f"Error executing tool:  {str(e)} "
                        logging.error(error_msg)
                        return  error_msg
                return f"No server found with tool:  {tool_call[ 'tool' ]} "
            return  llm_response
        except  json.JSONDecodeError:
            # If it is not in JSON format, return the original response directly
            return  llm_response

    async def start (self, system_message)  ->  None : 
        """Start the main loop of the chat session"""
        messages = [{ "role""system""content" : system_message}]
        while True :
            try :
                # Get user input
                user_input = input( "User: " ).strip().lower()
                if  user_input  in  [ "quit""exit""exit" ]:
                    print( 'AI assistant exits' )
                    break
                messages.append({ "role""user""content" : user_input})

                # Get the initial response from LLM
                llm_response = self.llm_client.get_response(messages)
                print( "Assistant: " , llm_response)

                # Handle possible tool calls
                result =  await  self.process_llm_response(llm_response)

                # If the processed result is different from the original response, it means that the tool call was executed and further processing is required
                while  result != llm_response:
                    messages.append({ "role""assistant""content" : llm_response})
                    messages.append({ "role""system""content" : result})

                    # Send the tool execution results back to LLM to get a new response
                    llm_response = self.llm_client.get_response(messages)
                    result =  await  self.process_llm_response(llm_response)
                    print( "Assistant: " , llm_response)

                messages.append({ "role""assistant""content" : llm_response})

            except  KeyboardInterrupt:
                print( 'AI assistant exits' )
                break


async def main () : 
    async with  Client( "http://127.0.0.1:8001/sse"as  mcp_client:
        # Initialize the LLM client and use the Tongyi Qianwen model
        llm_client = LLMClient(model_name= 'qwen-plus-latest' , api_key=os.getenv( 'DASHSCOPE_API_KEY' ),
                               url= 'https://dashscope.aliyuncs.com/compatible-mode/v1' )

        # Get a list of available tools and format it as part of the system prompt
        tools =  await  mcp_client.list_tools()
        dict_list = [tool.__dict__  for  tool  in  tools]
        tools_description = json.dumps(dict_list, ensure_ascii= False )

        # System prompts to guide LLM on how to use the tool and return responses
        system_message =  f'''
                You are an intelligent assistant that strictly follows the following protocol to return responses:

                Available tools: {tools_description}

                Response rules:
                1. When calculation is required, return pure JSON that strictly conforms to the following format:
                {{
                    "tool": "tool-name",
                    "arguments": {{
                        "argument-name": "value"
                    }}
                }}
                2. The following contents are prohibited:
                 - Markdown tags (such as ```json)
                 - Natural language explanation (e.g. "Result: ")
                 - Format the value (must keep the original precision)
                 - Unit symbol (such as yuan, kg)

                Verification process:
                ✓ The number of parameters is consistent with the tool definition
                ✓ The value type is number
                ✓ JSON format validity check

                Correct example:
                User: How much does it cost to buy 235 pieces at 88.5 each?
                Response: {{"tool":"multiply","arguments":{{"a":88.5,"b":235}}}}

                Error example:
                User: What is the total amount?
                Error response: Total price 500 yuan → Contains natural language
                Error response: ```json{{...}}``` → contains Markdown

                3. After receiving the response from the tool:
                 - Transform raw data into natural, conversational responses
                 - Keep your responses brief but informative
                 - Focus on the most relevant information
                 - Use the appropriate context from the user's question
                 - Avoid simply reusing raw data
                '''

        # Start a chat session
        chat_session = ChatSession(llm_client=llm_client, mcp_client=mcp_client)
        await  chat_session.start(system_message=system_message)

if  __name__ ==  "__main__" :
    asyncio.run(main())

Run verification

  1. Run the server: python fast_mcp_server.py
  1. Run the math quiz app
D:\python_project\mcp_learning\.venv\Scripts\python.exe D:\python_project\mcp_learning\fast_mcp\fast_mcp_client.py 
User: I want to buy a batch of goods now. The unit price is 1034.32423 and the quantity is 235326. The merchant later said that he can give me a 95% discount on this basis. What is the total price after the discount?
Helper: {
  "tool": "multiply",
  "arguments": {
    "a": 1034.32423,
    "b": 235326
  }
}
Helper: {
  "tool": "multiply",
  "arguments": {
    "a": 243403383.74898,
    "b": 0.95
  }
}
Assistant: The total price after discount is 231233214.56.
User: I have a good relationship with the merchant. The merchant said that based on the above, he can return two more points. What will be the final total price?
Helper: {
  "tool": "multiply",
  "arguments": {
    "a": 231233214.56153098,
    "b": 0.98
  }
}
Assistant: The final total price is 226608550.27.
User: quit
AI assistant exits

Process finished with exit code 0