Initialize an MCP project using uv
mainuv to initialize the directory, create a virtual environment, and install the necessary dependencies including mcp[cli], httpx, and openai.repository·main·Indexed 25 days ago
https://github.com/liaokongvfx/mcp-chinese-getting-started-guideA quick-start guide for programming with the Model Context Protocol (MCP) in Chinese. It covers building MCP servers using Python and the FastMCP framework, developing clients via stdio and SSE transport protocols, and integrating tools with LLMs like OpenAI and DeepSeek. The guide includes instructions for using the MCP Inspector, configuring servers in Claude Desktop and Cursor, and implementing advanced features like sampling, prompt templates, and LangChain integration.
uv to initialize the directory, create a virtual environment, and install the necessary dependencies including mcp[cli], httpx, and openai.You can test your SSE-based MCP server using the mcp dev command. When prompted or configuring the tool:
Transport Type to SSE.http://localhost:9000/sse).To use a custom MCP server with the Claude Desktop application, you must edit the claude_desktop_config.json file. Access this by opening Claude Desktop, navigating to the Developer menu, and selecting Edit Config.
Add your server under the mcpServers key. Each server configuration requires a command, args, and optionally env. These parameters match the StdioServerParameters object used in the SDK.
Alternatively, you can install a server directly from its directory using the CLI.
{
"mcpServers": {
"web-search-server": {
"command": "uv",
"args": [
"--directory",
"D:/projects/mcp_getting_started",
"run",
"web_search.py"
]
}
}
}mcp install web_search.pynpx or the mcp dev command. Once running, open the provided URL in your browser, click Connect, and use the Tools tab to List Tools and test them.To call tools from an MCP server in a client application, use stdio_client and ClientSession. You must define StdioServerParameters specifying the command (e.g., uv) and arguments (e.g., ['run', 'web_search.py']) to launch the server process.
Workflow:
StdioServerParameters.stdio_client(server_params) as an async context manager to get the transport.ClientSession(stdio, write) to manage the session.session.initialize().session.list_tools() to discover available tools.session.call_tool(name, arguments) to execute a tool.import asyncio
from mcp.client.stdio import stdio_client
from mcp import ClientSession, StdioServerParameters
server_params = StdioServerParameters(
command='uv',
args=['run', 'web_search.py'],
)
async def main():
async with stdio_client(server_params) as (stdio, write):
async with ClientSession(stdio, write) as session:
await session.initialize()
# List tools
response = await session.list_tools()
print(response)
# Call a tool
response = await session.call_tool('web_search', {'query': 'today weather'})
print(response)
if __name__ == '__main__':
asyncio.run(main())To deploy an MCP service to the cloud and avoid local setup, use the Server-Sent Events (SSE) protocol. In FastMCP, you can enable this by setting the transport parameter to 'sse' in the app.run() method.
host: The service address (defaults to 0.0.0.0).port: The service port (defaults to 8000).sse_path: The SSE route (defaults to /sse).from mcp.server import FastMCP
app = FastMCP('web-search', port=9000)
@app.tool()
async def web_search(query: str) -> str:
# ... tool implementation ...
pass
if __name__ == "__main__":
app.run(transport='sse')mcp.server.FastMCP class for a high-level implementation of an MCP server. You can define tools by decorating asynchronous functions with @app.tool(). The function name becomes the tool name, arguments become tool parameters, and docstrings/type hints are used to describe the tool and its parameters to the LLM.The Sampling feature allows an MCP server to request user input or LLM generation during tool execution. This is useful for sensitive operations like file deletion.
Server Side:
Use app.get_context().session.create_message() with a SamplingMessage to trigger a callback in the client.
Client Side:
When initializing ClientSession, provide a sampling_callback function. This function receives CreateMessageRequestParams and must return a CreateMessageResult containing the user's response.
You can bridge an LLM with MCP tools by converting MCP tool definitions into the LLM's function-calling format.
Steps:
session.list_tools().tool.name, tool.description, and tool.inputSchema to the LLM's tools parameter (e.g., OpenAI/DeepSeek format).tool_calls finish reason:
a. Parse the tool name and arguments.
b. Execute the tool via session.call_tool().
c. Append the tool result to the message history with role: 'tool' and the corresponding tool_call_id.# Mapping MCP tools to OpenAI/DeepSeek function format
available_tools = [{
"type": "function",
"function": {
"name": tool.name,
"description": tool.description,
"input_schema": tool.inputSchema
}
} for tool in response.tools]
# After tool execution, append to messages
messages.append({
"role": "tool",
"content": result.content[0].text,
"tool_call_id": tool_call.id,
})To use a remote MCP server (deployed via SSE) in Cursor, add the server configuration to your settings using the following JSON format:
{
"mcpServers": {
"web-search": {
"url": "https://your-deployed-service-url/sse"
}
}
}{
"mcpServers": {
"web-search": {
"url": "https://mcp-test-whhergsbso.cn-hangzhou.fcapp.run/sse"
}
}
}When using stdio_client, tool output (like print() statements) may not appear in the terminal. For easier debugging, use mcp.shared.memory.create_connected_server_and_client_session to run the server and client in the same process via memory.
from mcp.shared.memory import create_connected_server_and_client_session as create_session
from file_server import app # Import your FastMCP app
async def main():
async with create_session(
app._mcp_server,
sampling_callback=sampling_callback
) as client_session:
await client_session.call_tool('delete_file', {'file_path': 'test.txt'})To consume an MCP service running over SSE, use the sse_client from mcp.client.sse to establish a connection, then wrap the streams in a ClientSession.
import asyncio
from mcp.client.sse import sse_client
from mcp import ClientSession
async def main():
# Replace with your server's SSE URL
async with sse_client('http://localhost:9000/sse') as streams:
async with ClientSession(*streams) as session:
await session.initialize()
res = await session.call_tool('web_search', {'query': '杭州今天天气'})
print(res)
if __name__ == '__main__':
asyncio.run(main())