1. 项目概述一个基于MCP协议的图书管理API服务器最近在折腾AI开发工具链发现了一个挺有意思的东西叫模型上下文协议。简单来说它就像给AI大模型装上了一套标准化的“插件系统”。以前你想让ChatGPT或者Copilot去操作你的数据库、调用你的API得写一堆胶水代码现在有了MCP你可以把这些能力封装成一个标准的服务器AI助手就能像调用本地函数一样去使用它们。这个booksapi-mcp项目就是我为了彻底搞懂MCP怎么玩用Python和FastAPI搭的一个实战案例。它的核心功能很简单一个管理图书信息的CRUD API。但它的价值不在于功能本身而在于它被包装成了一个MCP服务器。这意味着你可以在VSCode或者Cursor里直接通过聊天窗口让Copilot帮你“添加一本叫《Python编程》的书”而Copilot会通过这个MCP服务器在背后悄无声息地完成数据库操作。整个项目麻雀虽小五脏俱全用FastAPI提供RESTful接口用MariaDB存储数据再用FastMCP这个库把API“转换”成MCP协议。对于想入门MCP开发尤其是Python栈的开发者来说这是一个非常清晰的样板工程。接下来我会带你从零开始拆解这个项目的每一部分分享我在搭建过程中踩过的坑和总结的经验让你不仅能复现更能理解背后的设计逻辑。2. 技术栈选型与核心思路解析2.1 为什么选择FastAPI FastMCP这个组合当你决定要做一个MCP服务器时第一个问题就是用什么框架社区里TypeScript的modelcontextprotocol/sdk非常活跃文档也最全。但对于一个Python开发者或者一个想快速验证想法的小项目从头学一套新的TS生态有点重。FastAPI成了我的首选原因有三点异步原生MCP协议基于JSON-RPC通信过程涉及大量I/O操作网络请求、数据库查询。FastAPI的异步支持能轻松处理这些并发请求避免阻塞这对于需要响应AI助手实时查询的场景至关重要。开发效率FastAPI的自动交互式文档、数据验证Pydantic能极大减少样板代码。在MCP开发中你需要严格定义工具Tools的输入输出格式Pydantic模型能帮你做编译时和运行时的双重保障。生态成熟围绕FastAPI的数据库ORM如SQLAlchemy、Tortoise-ORM、依赖注入等生态已经非常完善整合起来很顺手。而FastMCP这个库则是连接FastAPI和MCP世界的桥梁。它不是一个官方SDK而是一个社区实现的适配层。它的价值在于让你几乎不用改变原有的FastAPI开发习惯只需要用它的装饰器比如mcp.tool()把你的路由函数包装一下就自动暴露成了MCP工具。这大大降低了学习成本你可以把精力集中在业务逻辑而不是协议细节上。注意FastMCP是一个相对年轻的库如果遇到复杂需求比如资源订阅、增量更新可能需要你深入其源码或考虑直接使用更底层的MCP Python SDK。但对于绝大多数CRUD类的工具暴露它完全够用且非常高效。2.2 数据库选择MariaDB的考量项目里用了MariaDB其实就是MySQL的一个分支完全兼容。这里的选择其实比较随意核心需求就是一个关系型数据库。用SQLite行不行当然可以对于演示项目甚至更轻量。但为什么我还是选了MariaDB/MySQL更贴近生产环境很多线上项目用的就是MySQL相关的连接池、运维经验更通用。用这个项目练手积累的经验可以直接迁移。异步驱动成熟asyncmy或aiomysql这样的异步MySQL驱动已经非常稳定能和FastAPI的异步体系完美结合。虽然SQLite也有异步方案但相对小众。数据类型支持对于“图书”这种业务虽然当前字段简单标题、作者、年份但保不齐未来要加ISBN号、封面图片URL、分类等。关系型数据库的结构化查询和关联能力为后续扩展留了余地。在实际操作中你完全可以根据自己熟悉的环境替换成PostgreSQL用asyncpg甚至MongoDB。MCP服务器只关心“提供工具”不关心底层数据存哪儿。只要你的FastAPI接口能正确工作FastMCP就能把它暴露出去。2.3 核心架构MCP服务器如何工作理解架构是后续一切操作的基础。这个项目的核心数据流是这样的[AI助手 (如Copilot)] │ (通过MCP客户端发送JSON-RPC请求) ▼ [VSCode/Cursor MCP桥接层] │ (将请求转发给注册的MCP服务器) ▼ [本项目的FastAPI应用 (由FastMCP包装)] │ (FastMCP解析请求调用对应的工具函数) ▼ [你的业务逻辑函数 (如create_book)] │ (执行数据库操作) ▼ [MariaDB数据库]关键在于AI助手看到的不是“一个叫/api/books的POST接口”而是“一个叫create_book的工具它需要一个title和author参数”。这种抽象让AI的使用意图和你的后端实现解耦了。3. 环境准备与项目初始化实操3.1 系统级依赖安装详解原文档的步骤是对的但有些细节需要展开说明特别是对于不同操作系统的用户。Python确保你安装的是Python 3.8或更高版本。MCP和相关的异步库对Python版本有要求。在终端用python --version或python3 --version检查。我强烈建议使用pyenvMac/Linux或Python Launcher for Windows来管理多个Python版本避免污染系统环境。Git这个不用说克隆项目必备。MariaDB/MySQL这是最容易出错的环节。Windows用户可以去MariaDB官网下载安装包安装时记住你设置的root密码。也可以使用XAMPP、WAMP等集成环境它们自带MySQL。Mac用户推荐用Homebrew安装brew install mariadb然后用brew services start mariadb启动服务。Linux用户sudo apt install mariadb-server(Ubuntu/Debian) 或sudo yum install mariadb-server(RHEL/CentOS)安装后记得运行sudo systemctl start mariadb并执行安全初始化sudo mysql_secure_installation。安装后务必确保数据库服务在运行。可以打开一个命令行尝试用mysql -u root -p连接输入密码后能进入mysql提示符就成功了。3.2 使用uv打造可复现的Python环境项目用了uv这是一个用Rust写的、速度极快的Python包管理器和安装器可以看作是pip和virtualenv的现代替代品。用它来管理依赖能确保任何人在任何机器上都能得到完全一致的环境。安装uv 原文档的pipx安装uv是最佳实践。pipx专门用于安装全局的Python命令行工具能避免依赖冲突。如果你没有pipx也可以直接用pip安装pip install uv但更推荐pipx方式。创建虚拟环境并安装依赖 进入项目根目录有pyproject.toml的目录按顺序执行# 1. 创建虚拟环境。uv venv 会创建一个 .venv 目录。 uv venv # 2. 激活虚拟环境。 # Windows (PowerShell或CMD): .venv\Scripts\activate # Windows (Git Bash): source .venv/Scripts/activate # Mac/Linux: source .venv/bin/activate # 3. 安装项目依赖。uv sync 会读取 pyproject.toml安装所有包并生成锁文件。 uv sync执行完uv sync后你的虚拟环境里就装好了fastapi,fastmcp,uvicorn,asyncmy,pydantic等所有需要的包。uv的一个巨大优势是它的依赖解析和安装速度非常快并且生成的uv.lock文件能实现真正的依赖锁定。实操心得如果你在uv sync时遇到网络问题或某个包安装失败可以尝试使用镜像源。为uv设置镜像不像pip那么简单一个更通用的方法是临时修改pyproject.toml里的依赖源或者使用uv pip install -r requirements.txt并提前配置好pip的镜像源。不过对于这个项目直接sync成功率很高。4. 数据库配置与模型定义4.1 数据库连接与.env文件安全项目里通过一个db.py脚本来初始化数据库连接和表结构。在这之前你需要配置数据库连接信息。原文档让你创建一个.env文件这是管理配置的常见做法但有几个关键点必须注意文件位置与名称.env文件必须放在你的项目根目录和db.py同级并且文件名就是.env前面有个点在有些文件管理器里它可能是隐藏文件。内容格式.env文件是简单的键值对但等号两边通常不应该有空格虽然有些解析库能容忍但最好遵循标准格式。另外如果密码中包含特殊字符如!,,#可能需要进行转义或用引号包裹。一个更健壮的.env文件示例USER_DBroot PASSWORD_DBYourStrongPassword123! HOST_DBlocalhost PORT_DB3306 NAME_DBbooksapi安全警告绝对不要将真实的.env文件提交到Git仓库你应该在项目的.gitignore文件中加入.env。仓库里应该只保留一个示例文件比如.env.example里面填写的是占位符用于说明需要哪些配置项。4.2 深入db.py连接池与表创建让我们看看db.py大概会做什么根据项目结构推断# 假设的 db.py 核心逻辑 import asyncmy import asyncio from dotenv import load_dotenv import os load_dotenv() # 加载 .env 文件中的环境变量 async def init_db(): # 1. 首先连接到一个存在的数据库如mysql来创建我们的业务数据库 admin_conn await asyncmy.connect( hostos.getenv(HOST_DB), portint(os.getenv(PORT_DB)), useros.getenv(USER_DB), passwordos.getenv(PASSWORD_DB), databasemysql # 先连到系统库 ) async with admin_conn.cursor() as cursor: # 创建数据库如果不存在 db_name os.getenv(NAME_DB) await cursor.execute(fCREATE DATABASE IF NOT EXISTS {db_name}) print(fDatabase {db_name} ensured.) await admin_conn.ensure_closed() # 2. 连接到我们刚创建或已存在的业务数据库 conn await asyncmy.connect( hostos.getenv(HOST_DB), portint(os.getenv(PORT_DB)), useros.getenv(USER_DB), passwordos.getenv(PASSWORD_DB), databaseos.getenv(NAME_DB) ) async with conn.cursor() as cursor: # 创建 books 表 await cursor.execute( CREATE TABLE IF NOT EXISTS books ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255) NOT NULL, author VARCHAR(255) NOT NULL, published_year INT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ) print(Table books ensured.) await conn.ensure_closed() print(Database initialization completed successfully.) if __name__ __main__: asyncio.run(init_db())关键点解析使用asyncmy这是一个纯Python的异步MySQL驱动性能不错且API和同步的mysql-connector类似。注意连接和游标操作都需要await。两层连接这是一个经典模式。先以高权限用户连接到默认数据库如mysql来创建目标数据库然后再连接到目标数据库去建表。这确保了脚本的幂等性可重复执行。表结构设计id设为主键且自增title和author设为非空NOT NULLpublished_year可为空因为有些古书年份不详created_at自动记录插入时间。这个设计简单但实用。运行这个脚本确保虚拟环境已激活且在项目根目录然后执行python db.py。如果看到成功的提示信息数据库和表就准备好了。5. FastAPI应用与MCP工具封装5.1 构建核心的FastAPI应用项目的核心在src/api.py根据文档推断。这里我们构建一个完整的、带错误处理的FastAPI应用。# src/api.py from fastapi import FastAPI, HTTPException, Depends from pydantic import BaseModel, Field from typing import Optional, List import asyncmy import os from dotenv import load_dotenv from contextlib import asynccontextmanager # --- 1. 加载配置和定义模型 --- load_dotenv() class BookCreate(BaseModel): 创建图书时接收的数据模型 title: str Field(..., min_length1, max_length255, description图书标题) author: str Field(..., min_length1, max_length255, description作者) published_year: Optional[int] Field(None, ge1000, le2100, description出版年份) class BookResponse(BookCreate): 返回给客户端的图书数据模型包含ID id: int created_at: str # --- 2. 数据库连接依赖 --- async def get_db_connection(): 依赖注入获取数据库连接 conn await asyncmy.connect( hostos.getenv(HOST_DB), portint(os.getenv(PORT_DB)), useros.getenv(USER_DB), passwordos.getenv(PASSWORD_DB), databaseos.getenv(NAME_DB), autocommitTrue # 设置自动提交简化操作 ) try: yield conn finally: await conn.ensure_closed() # --- 3. 应用生命周期与路由定义 --- asynccontextmanager async def lifespan(app: FastAPI): # 启动时可以做一些初始化比如检查数据库连通性 print(MCP Books API Server starting up...) yield # 关闭时可以做一些清理 print(MCP Books API Server shutting down...) app FastAPI(titleBooks API MCP Server, lifespanlifespan) app.post(/books/, response_modelBookResponse) async def create_book(book: BookCreate, connDepends(get_db_connection)): 创建一本新书 async with conn.cursor() as cursor: # 使用参数化查询防止SQL注入 sql INSERT INTO books (title, author, published_year) VALUES (%s, %s, %s) values (book.title, book.author, book.published_year) await cursor.execute(sql, values) new_id cursor.lastrowid # 查询并返回刚创建的书 await cursor.execute(SELECT * FROM books WHERE id %s, (new_id,)) result await cursor.fetchone() if not result: raise HTTPException(status_code500, detailFailed to retrieve created book) # 将数据库行转换为字典 keys [id, title, author, published_year, created_at] book_dict dict(zip(keys, result)) # 转换时间戳为字符串 if book_dict[created_at]: book_dict[created_at] book_dict[created_at].isoformat() return book_dict app.get(/books/, response_modelList[BookResponse]) async def list_books(connDepends(get_db_connection)): 获取所有图书列表 async with conn.cursor() as cursor: await cursor.execute(SELECT * FROM books ORDER BY created_at DESC) results await cursor.fetchall() books [] keys [id, title, author, published_year, created_at] for row in results: book_dict dict(zip(keys, row)) if book_dict[created_at]: book_dict[created_at] book_dict[created_at].isoformat() books.append(book_dict) return books # 这里还可以添加 GET /books/{id}, PUT /books/{id}, DELETE /books/{id} 等端点 # 为了简洁先省略...这个FastAPI应用提供了最基础的创建和列表功能。关键点在于使用Pydantic模型BookCreate定义了输入数据的结构和验证规则如字段长度、年份范围。BookResponse定义了返回数据的结构。这确保了API接口的健壮性。依赖注入get_db_connection函数被Depends()使用FastAPI会自动为每个请求创建并关闭数据库连接。这是管理资源如数据库连接的最佳实践。异步数据库操作所有数据库调用都使用await确保在I/O等待时不会阻塞事件循环能处理更高并发。SQL注入防护永远使用参数化查询%s占位符而不是字符串拼接这是安全底线。你可以用uvicorn src.api:app --reload启动这个API然后用浏览器打开http://127.0.0.1:8000/docs就能看到自动生成的交互式文档并测试接口了。5.2 使用FastMCP将API“转换”为MCP工具这是项目的精髓所在。我们有了REST API但AI助手不懂REST它只懂MCP工具。FastMCP的作用就是帮你把FastAPI的路由函数包装成MCP工具。# 在 src/api.py 的顶部添加FastMCP导入并修改部分代码 from fastmcp import FastMCP # 创建FastMCP实例。它实际上也是一个FastAPI应用但多了MCP的能力。 mcp FastMCP(Books API Server, description一个管理图书信息的MCP服务器) # --- 将之前的普通FastAPI路由改为用mcp.tool()装饰器注册 --- # 注意函数名和描述很重要它们会直接暴露给AI助手。 mcp.tool() async def create_book(title: str, author: str, published_year: Optional[int] None) - dict: 向数据库中添加一本新书。 Args: title: 图书的标题例如“Python编程从入门到实践”。 author: 图书的作者例如“John Doe”。 published_year: 图书的出版年份例如2020。这是一个可选参数。 Returns: 一个包含新书信息的字典包括自动生成的ID。 # 这里的实现逻辑和之前的POST /books/ 几乎一样 # 但注意参数现在是直接来自函数签名而不是一个Pydantic模型。 # 我们需要手动连接数据库或者可以复用之前的依赖但FastMCP装饰器对依赖注入的支持可能不同。 conn await asyncmy.connect( hostos.getenv(HOST_DB), portint(os.getenv(PORT_DB)), useros.getenv(USER_DB), passwordos.getenv(PASSWORD_DB), databaseos.getenv(NAME_DB), autocommitTrue ) try: async with conn.cursor() as cursor: sql INSERT INTO books (title, author, published_year) VALUES (%s, %s, %s) values (title, author, published_year) await cursor.execute(sql, values) new_id cursor.lastrowid await cursor.execute(SELECT * FROM books WHERE id %s, (new_id,)) result await cursor.fetchone() if not result: return {error: Failed to retrieve created book} keys [id, title, author, published_year, created_at] book_dict dict(zip(keys, result)) if book_dict[created_at]: book_dict[created_at] book_dict[created_at].isoformat() return {success: True, book: book_dict} except Exception as e: return {error: str(e)} finally: await conn.ensure_closed() mcp.tool() async def list_books() - list: 获取数据库中所有图书的列表。 Returns: 一个图书字典的列表按创建时间倒序排列。 conn await asyncmy.connect( hostos.getenv(HOST_DB), portint(os.getenv(PORT_DB)), useros.getenv(USER_DB), passwordos.getenv(PASSWORD_DB), databaseos.getenv(NAME_DB) ) try: async with conn.cursor() as cursor: await cursor.execute(SELECT * FROM books ORDER BY created_at DESC) results await cursor.fetchall() books [] keys [id, title, author, published_year, created_at] for row in results: book_dict dict(zip(keys, row)) if book_dict[created_at]: book_dict[created_at] book_dict[created_at].isoformat() books.append(book_dict) return books except Exception as e: return {error: str(e)} finally: await conn.ensure_closed() # 同样可以添加 get_book, update_book, delete_book 等工具 # ... # 最后需要运行MCP服务器 if __name__ __main__: mcp.run()发生了什么变化从app.post到mcp.tool()我们不再用FastAPI的路由装饰器而是用FastMCP的工具装饰器。这个函数不再通过HTTP端点调用而是通过MCP协议调用。函数即工具create_book和list_books这两个函数本身就是暴露给AI的工具。它们的函数名、参数名、类型注解和文档字符串...会被FastMCP自动提取并生成对应的MCP工具定义Tool。AI助手能看到这些信息并知道如何调用它们。独立的数据库连接由于MCP工具调用不经过FastAPI的请求生命周期我们不能直接使用之前的Depends(get_db_connection)。因此在每个工具函数内部我们手动创建和关闭数据库连接。对于生产环境你可能需要实现一个连接池来优化性能。运行方式最后我们调用mcp.run()来启动这个MCP服务器。它会启动一个进程通过标准输入输出stdio与MCP客户端如VSCode通信。现在这个api.py文件既是一个可以通过uvicorn运行的FastAPI应用如果你保留了那些路由也是一个可以通过mcp.run()启动的MCP服务器。在MCP上下文中我们主要使用后者。6. 在VSCode/Cursor中配置与调试MCP服务器6.1 配置MCP服务器以VSCode为例原文档的步骤很详细这里我补充一些关键细节和避坑指南打开命令面板CtrlShiftP(Windows/Linux) 或CmdShiftP(Mac)。搜索并选择MCP: Add New Server中文界面可能是MCP: 添加新服务器。选择Command (stdio)这表示我们的服务器是一个可以通过命令行启动的程序。填写命令详情Command:uvArgs:--directory,你的src文件夹的绝对路径,run,api.py例如--directory,C:\Users\YourName\projects\booksapi-mcp\src,run,api.pyName: 给你这个服务器起个名字比如booksapi。关于路径的坑绝对路径必须使用完整的绝对路径。你不能用相对路径如./src。Windows路径分隔符在Windows的args里使用反斜杠\或正斜杠/都可以但为了保险建议使用双引号包裹路径特别是路径中有空格时C:\My Projects\booksapi-mcp\src。uv的--directory参数这个参数告诉uv在哪个目录下寻找pyproject.toml和虚拟环境。它会把工作目录切换到那里然后执行api.py。这确保了api.py能正确找到.env文件假设.env在项目根目录而api.py在src子目录。如果.env在src目录下你可能需要调整路径或修改代码中load_dotenv()的路径。选择配置范围Global全局意味着这个MCP服务器对所有VSCode项目都可用Workspace工作区只对当前打开的项目文件夹可用。对于个人工具选Global更方便。配置完成后VSCode会在你的用户设置目录如~/.config/Code/User/globalStorage/mcp.json里生成或更新一个配置文件。内容就像文档里展示的那样。6.2 验证服务器是否运行成功配置好后VSCode会自动尝试启动你刚添加的MCP服务器。如何知道它成功了查看状态栏在VSCode窗口最底部的状态栏如果MCP服务器运行正常通常不会有特别提示。但如果有错误可能会短暂显示一个错误图标。打开输出面板CtrlShiftU打开输出面板。在面板右侧的下拉列表中寻找以MCP开头的选项特别是你命名的服务器如MCP: booksapi。如果找不到点击下拉列表旁边的...更多操作按钮选择MCP: Show All Outputs。解读输出日志在输出面板中你应该能看到类似以下的日志[INFO] Starting MCP server... [INFO] Registered tool: create_book [INFO] Registered tool: list_books [INFO] MCP server started successfully.如果看到Registered tool恭喜你说明FastMCP已经成功扫描了你的api.py找到了被mcp.tool()装饰的函数并把它注册为MCP工具。如果看到错误比如数据库连接失败、模块导入错误就需要根据错误信息逐一排查。6.3 在Chat中实际使用MCP工具这是最激动人心的部分让AI帮你操作数据库。在VSCode中打开Copilot Chat面板通常侧边栏有一个图标或者按CtrlI打开行内聊天CtrlShiftI打开面板聊天。关键一步切换代理模式。在Chat输入框的上方或附近找到选择聊天模式的按钮。默认可能是Ask问答模式。你需要将其切换到Agent代理模式。在某些版本中这个按钮可能显示为两个交叉的工具图标。切换到Agent模式后通常会弹出一个工具选择框。取消全选然后只勾选你刚配置的MCP服务器如booksapi。这告诉Copilot“你只能使用我授权的这个服务器里的工具”。现在在聊天框里输入自然语言指令例如“帮我添加一本书书名是《深入理解计算机系统》作者是Bryant出版年份是2016。”“列出数据库里所有的书。”“找一下作者是John Doe的书。”“删除ID为3的那本书。”Copilot的工作流程它理解你的自然语言请求。它发现自己在Agent模式并且被授权使用booksapi这个MCP服务器。它查看booksapi服务器提供了哪些工具create_book,list_books等以及每个工具需要什么参数。它将你的请求“翻译”成对相应工具的调用。例如对于“添加一本书...”它会调用create_book(title深入理解计算机系统, authorBryant, published_year2016)。关键一步请求权限。出于安全考虑Copilot在首次执行一个工具或每次执行取决于配置前会弹出一个对话框询问你是否允许它执行这个操作。你必须点击“允许”或“确定”。得到你的授权后Copilot通过MCP协议将调用请求发送给你的api.py进程。api.py执行对应的函数操作数据库并将结果返回。Copilot将结果以友好的格式呈现给你。这个过程几乎是无感的你感觉就是在和Copilot对话但它背后已经完成了一次完整的数据持久化操作。7. 常见问题、故障排查与进阶技巧7.1 安装与依赖问题问题1uv sync或pip install失败提示找不到某个包如fastmcp。原因fastmcp可能不在PyPI官方源或者你使用的镜像源没有同步这个包。解决检查pyproject.toml中fastmcp的版本。尝试使用pip install fastmcp直接安装看是否有更明确的错误。临时切换回官方PyPI源uv pip install -i https://pypi.org/simple -r pyproject.toml。如果fastmcp确实安装不了可以考虑使用MCP的官方Python SDKmcp但它的用法和FastMCP不同需要更多配置。问题2运行python db.py或uvicorn时提示ModuleNotFoundError: No module named asyncmy。原因虚拟环境未激活或者依赖没有正确安装到当前环境。解决确认终端提示符前面有(.venv)字样。如果没有回到项目根目录执行source .venv/bin/activate(Mac/Linux) 或.venv\Scripts\activate(Windows)。如果已激活但还报错在虚拟环境中重新安装依赖uv sync --force。7.2 数据库连接问题问题3db.py或API运行时报错Access denied for user rootlocalhost。原因.env文件中的用户名或密码错误或者该用户没有访问指定数据库的权限。排查用命令行工具mysql -u root -p尝试连接确保密码正确。如果连接成功在MySQL命令行里执行SHOW DATABASES;看booksapi数据库是否存在。如果不存在db.py应该会创建它。检查db.py是否有创建数据库的权限通常root用户有。检查.env文件是否在正确的位置并且变量名拼写正确USER_DB不是USER。问题4连接时出现Cant connect to MySQL server on localhost (10061)。原因MariaDB/MySQL服务没有启动。解决Windows打开“服务”管理器services.msc找到MariaDB或MySQL服务确保其状态为“正在运行”。Macbrew services start mariadb。Linuxsudo systemctl start mariadb。7.3 MCP服务器配置与通信问题问题5在VSCode的MCP输出中看不到我的服务器日志。原因服务器可能启动失败或者输出被重定向到了其他地方。排查首先手动在终端运行你的服务器看是否有错误。在src目录下执行uv run api.py。观察终端输出。如果手动运行成功但VSCode里不显示检查VSCode的MCP配置。打开命令面板执行MCP: Open Settings查看你的服务器配置是否正确特别是args里的路径。尝试重启VSCode。有时MCP客户端需要重启才能重新加载配置。问题6Copilot Chat中不显示我注册的工具或者点击工具图标后列表为空。原因MCP服务器启动成功但没有正确导出工具或者VSCode没有刷新工具列表。排查检查MCP输出日志确认有Registered tool: xxx的信息。在Chat面板确保已切换到Agent模式并且点击工具选择按钮时你的服务器在列表中且被勾选。尝试在Chat中输入一个明确的工具调用指令如“请使用booksapi服务器创建一个工具”。有时主动提及会触发客户端的刷新。检查你的api.py中工具函数是否正确定义了类型注解和文档字符串FastMCP依赖这些信息来生成工具定义。问题7调用工具时Copilot一直“思考”但没有反应或者提示“工具调用失败”。原因MCP服务器进程可能已崩溃或者工具函数内部有未处理的异常。排查立刻去查看MCP服务器的输出日志。错误信息通常在这里。常见错误包括数据库连接失败、SQL语法错误、Python代码运行时异常如变量未定义。在你的工具函数内部添加更详细的try...except和日志打印将错误信息返回方便调试。例如mcp.tool() async def create_book(...): try: # ... 你的逻辑 ... return {success: True, book: book_dict} except Exception as e: import traceback error_detail traceback.format_exc() print(fTool error: {error_detail}) # 这会输出到MCP日志 return {success: False, error: str(e)}7.4 性能与安全进阶考量连接池管理 目前每个工具调用都创建新的数据库连接这在频繁调用时效率很低。对于生产环境应该使用连接池。asyncmy支持连接池你可以这样改进get_db_connection函数from asyncmy import create_pool import os # 在应用启动时创建连接池 pool None async def init_db_pool(): global pool pool await create_pool( hostos.getenv(HOST_DB), portint(os.getenv(PORT_DB)), useros.getenv(USER_DB), passwordos.getenv(PASSWORD_DB), databaseos.getenv(NAME_DB), minsize1, # 最小连接数 maxsize10 # 最大连接数 ) # 在工具函数中从池中获取连接 mcp.tool() async def list_books(): global pool async with pool.acquire() as conn: async with conn.cursor() as cursor: # ... 查询逻辑 ... pass输入验证与错误处理 虽然Pydantic在FastAPI路由中帮我们做了验证但在MCP工具函数中我们需要手动验证。mcp.tool()装饰器会利用函数类型注解做一些基础的类型转换比如把字符串2020转成整数2020但更复杂的验证如字符串长度、数值范围需要在函数内部完成。务必做好验证避免无效或恶意数据进入数据库。工具权限与审计 目前任何能通过MCP客户端如Copilot发送请求的人都可以执行添加、删除等操作。在实际使用中你需要考虑身份验证MCP协议本身支持传输层安全但应用层身份验证需要你自己实现。一个简单的方法是在工具函数开头检查一个预共享的密钥通过环境变量传入但这并不安全。更复杂的方案需要集成OAuth等。操作审计可以考虑在数据库中创建一个audit_log表记录每个工具调用的时间、调用的工具名、参数脱敏后、执行结果等便于事后追溯。这个booksapi-mcp项目作为一个学习和原型验证工具已经非常出色。通过它你不仅学会了如何构建一个MCP服务器更理解了如何将现有的API能力安全、标准地暴露给AI助手为开发更复杂的AI赋能应用打下了坚实的基础。