MCP实战开发:从零编写Python Server——Echo工具、数据库查询到异步任务

399 阅读2分钟

1. Hello World:开发第一个Echo工具

1.1 基础代码解析

from mcp.server.fastmcp import FastMCP
# Create an MCP server instance with a custom name.

# 创建MCP服务器实例
mcp = FastMCP("EchoDemo") 

# 注册工具
@mcp.tool()
def echo(text: str) -> str:
    """回显输入文本 | text: 需要回显的文本"""
    return text

# 启动服务器
mcp.run()

1.2 关键点说明

  • MCPServer初始化参数是服务器名称,会显示在客户端
  • @mcp.tool()装饰器将普通函数转换为MCP工具
  • 函数docstring中的|分隔工具描述和参数说明

1.3 测试方法

  1. 保存为echo_server.py
  2. 启动服务器:python echo_server.py
  3. 在客户端(如Cursor)输入:@echo 你好MCP
  4. 应收到回复:你好MCP

2. 数据库查询工具实战

2.1 连接SQLite数据库

import sqlite3
from typing import List, Dict

@mcp.tool()
def query_users(limit: int = 100) -> List[Dict]:
    """查询用户列表 | limit: 返回结果最大数量"""
    conn = sqlite3.connect('example.db')
    cursor = conn.cursor()
    
    try:
        cursor.execute("SELECT * FROM users LIMIT ?", (limit,))
        columns = [desc[0] for desc in cursor.description]
        return [dict(zip(columns, row)) for row in cursor.fetchall()]
    finally:
        conn.close()

2.2 最佳实践

  1. 自动LIMIT处理:默认限制返回数量,防止大数据量
  2. 标准化返回:将结果转为字典列表,便于JSON序列化
  3. 错误处理:使用try-finally确保连接关闭

2.3 高级版本(带过滤条件)

@mcp.tool()
def query_users_by_age(min_age: int = 0, max_age: int = 100, limit: int = 100):
    """按年龄筛选用户 | min_age: 最小年龄 | max_age: 最大年龄 | limit: 返回数量"""
    # 实现代码类似上面,SQL增加WHERE条件

3. 进阶功能开发

3.1 动态资源注册

@mcp.resource("greeting://{name}")
def generate_greeting(name: str):
    """动态生成问候语"""
    return f"Hello, {name}!"

客户端调用方式:

@resource greeting://John

3.2 异步工具开发

import asyncio

@mcp.tool()
async def long_running_task():
    """长时间运行的任务"""
    await asyncio.sleep(10)
    return "任务完成"

3.3 带进度反馈的异步任务

@mcp.tool()
async def process_data(data: str):
    """处理数据并反馈进度"""
    for i in range(1, 6):
        await asyncio.sleep(1)
        mcp.notify_progress(i/5, f"处理中... {i*20}%")
    return "处理完成"

4. 完整示例:用户统计服务


import sqlite3
from typing import List, Dict


from mcp.server.fastmcp import FastMCP

# 创建MCP服务器实例
mcp = FastMCP("UserStats") 

@mcp.tool()
def user_age_stats() -> Dict[str, int]:
    """统计用户年龄分布"""
    conn = sqlite3.connect('users.db')
    try:
        cursor = conn.cursor()
        cursor.execute("""
            SELECT 
                CASE
                    WHEN age < 20 THEN 'Under 20'
                    WHEN age BETWEEN 20 AND 29 THEN '20s'
                    WHEN age BETWEEN 30 AND 39 THEN '30s'
                    ELSE '40+'
                END as age_group,
                COUNT(*) as count
            FROM users
            GROUP BY age_group
        """)
        return dict(cursor.fetchall())
    finally:
        conn.close()

if __name__ == "__main__":
    mcp.run()

5. 调试技巧

  1. 使用MCP Inspector监控工具调用
  2. 打印调试日志:
@mcp.tool()
def debug_tool():
    print("调试信息")  # 会在服务器控制台显示
    return "OK"

附录:常见错误处理

错误类型解决方案
工具未注册检查是否添加了@mcp.tool()装饰器
参数解析失败确保docstring中有参数说明
数据库连接泄漏使用try-finally确保连接关闭
异步任务卡死设置超时机制