my_mcp_server.py 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. # my_mcp_server.py —— 一个极简的自定义 MCP 服务
  2. from mcp.server.fastmcp import FastMCP
  3. from datetime import datetime
  4. from urllib.parse import unquote
  5. # ============================================================
  6. # 第一步:创建 FastMCP 实例,相当于初始化一个 Server 框架
  7. # ============================================================
  8. mcp = FastMCP(name="my-tools")
  9. # ============================================================
  10. # 第二步:用 @mcp.tool() 装饰器注册工具
  11. # 装饰器会自动提取函数的 type hints 和 docstring 作为工具描述
  12. # ============================================================
  13. @mcp.tool()
  14. def get_current_time() -> str:
  15. """
  16. 获取当前日期和时间,返回格式友好的字符串。
  17. 适用场景:当用户问"现在几点"或"今天几号"时调用。
  18. """
  19. now = datetime.now()
  20. return f"现在是 {now.year}年{now.month}月{now.day}日,{now.strftime('%H:%M:%S')}"
  21. @mcp.tool()
  22. def calculate_bmi(weight_kg: float, height_cm: float) -> str:
  23. """
  24. 根据体重(公斤)和身高(厘米)计算 BMI 指数。
  25. 参数:
  26. - weight_kg: 体重,单位公斤
  27. - height_cm: 身高,单位厘米
  28. 返回:BMI 数值 + 健康评级
  29. """
  30. height_m = height_cm / 100
  31. bmi = weight_kg / (height_m ** 2)
  32. # 按中国标准分级
  33. if bmi < 18.5:
  34. level = "偏瘦,多吃点"
  35. elif bmi < 24:
  36. level = "正常,继续保持"
  37. elif bmi < 28:
  38. level = "偏胖,注意饮食"
  39. else:
  40. level = "该运动了兄弟"
  41. return f"BMI = {bmi:.1f},{level}"
  42. # ============================================================
  43. # 第三步:用 @mcp.resource() 注册静态资源
  44. # 资源是"模板化"的数据端点,适合暴露文章、配置、文件内容等
  45. # URI 中用 {变量名} 定义路径参数
  46. # ============================================================
  47. # 模拟一个简易知识库
  48. KNOWLEDGE_BASE = {
  49. "请假流程": "OA 系统提交 → 直属领导审批 → HR 备案。年假需提前 3 天申请。",
  50. "报销标准": "市内交通实报实销(上限 100/天),住宿标准 350/晚(一线城市 450/晚)。",
  51. "入职材料": "身份证原件 + 复印件、学历学位证、体检报告、离职证明、银行卡。",
  52. }
  53. @mcp.resource("knowledge://{topic}")
  54. def query_knowledge(topic: str) -> str:
  55. """
  56. 查询公司内部知识库,返回指定主题的说明文档。
  57. 调用方式(Client 端):session.read_resource("knowledge://报销标准")
  58. """
  59. # MCP 协议中 URI 的非 ASCII 字符会被 Pydantic AnyUrl 自动
  60. # URL 编码(如 "请假流程" → "%E8%AF%B7%E5%81%87%E6%B5%81%E7%A8%8B"),
  61. # 但 FastMCP 的 ResourceTemplate.matches() 提取参数时不会自动解码,
  62. # 所以这里需要手动 unquote。
  63. topic = unquote(topic)
  64. print(f"查询知识库:{topic}")
  65. result = KNOWLEDGE_BASE.get(topic, f"没找到关于「{topic}」的记录,请联系 HR 更新知识库。")
  66. return result
  67. # ============================================================
  68. # 第四步:启动 Server
  69. # ============================================================
  70. if __name__ == "__main__":
  71. # SSE 模式:作为独立 HTTP 服务运行,适合远程调用和调试
  72. mcp.settings.host = "0.0.0.0"
  73. mcp.settings.port = 8000
  74. mcp.run(transport="sse")