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

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
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:
Introduction to FastMCP 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
Run the server: python fast_mcp_server.py
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