文章 · 2024-01-01

从混乱到清晰:一次MCP服务器开发的反思与总结

初稿被批评"乱写一通"。回顾来看,问题很清楚:对FastMCP的解释显得过于复杂,而实际实现只是一个Python脚本。

选择Python和FastMCP的理由充分。该框架的优势很快显现:

核心实现

最终设计将一切整合为单一的publish_blog_post工具:

@app.tool()
async def publish_blog_post(
    directory: str, 
    content: str, 
    filename: str,
    commit_message: str = None,
    deploy: bool = True
) -> str:
    """Save article, commit changes, and optionally deploy - all in one command."""
    
    # 1. 保存文章(自动添加frontmatter)
    # 2. Git提交
    # 3. 可选的部署

关键特性:

遇到的问题

技术写作的清晰性

初稿使FastMCP看起来比实际更复杂。技术写作必须准确、直接。把简单的事情复杂化以显得高深会适得其反。

解决方案很直接:围绕实际发生的情况重新框架——一个Python脚本,不到一百行代码,功能完整。

装饰器函数的测试

对MCP工具进行测试时立即遇到一个障碍:

TypeError: 'FunctionTool' object is not callable

@app.tool()装饰器将函数包装为FunctionTool对象,无法直接调用。

解决方案是在MCP服务器本身添加测试模式:

if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] == "--test":
        asyncio.run(test_mode())
    else:
        app.run()

测试边界

第一次尝试通过直接操作文件系统进行测试,绕过了MCP服务器。这违反了一个基本原则:应该按系统实际使用的方式进行测试,而不是通过简化的旁路。

解决方案是将测试构建到服务器中,作为适当的MCP工具,确保所有测试都通过实际接口运行。

最终成果

已发布的文章:

可用的服务器:

简化的工作流:

技术实现细节

环境配置

ASTRO_DIR = pathlib.Path(os.getenv("ASTRO_DIR", "./astro")).expanduser().resolve()

命令执行

def _run(cmd: List[str]) -> str:
    proc = subprocess.run(cmd, cwd=ASTRO_DIR, capture_output=True, text=True)
    banner = f"$ {' '.join(cmd)}\n"
    return banner + proc.stdout + proc.stderr

时间戳格式

从简单日期格式到完整时间戳的演进:

pubDate: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

关键收获

传达价值与实现代码同样重要。能工作但无法展示其用途的代码是不完整的。

简洁本身就很强大。当FastMCP用不到一百行Python代码完成发布工作流时,这不是约束——这是成就。

测试必须尊重系统边界。当装饰器包装函数时,测试必须通过这些边界,而不是绕过它们。

迭代会产生复合效应。从初始版本的每次改进都产生了更清晰的代码和更敏锐的思考。

可能的改进方向

当前实现满足核心需求。潜在的改进方向包括:

© 2026 Yuxu Ge ·