掘金 人工智能 04月27日 11:22
FastMCP 教程:从零开始用 Python 构建 MCP 服务器
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文深入探讨了如何构建和使用 MCP(模型控制平台)服务器,特别是使用 Python 和 FastMCP 库来处理文档和 PDF 文件。文章首先介绍了 MCP 服务器的优势和基本用法,然后详细阐述了如何搭建一个自定义的 MCP 服务器,使其能够读取 PDF 和 DOCX 文件。通过提供工具、资源和提示,MCP 服务器能够增强 AI 在特定用例中的能力和实用性,为 AI 应用提供更强大的功能。

💡 **MCP 服务器的核心功能**:MCP 允许以统一的方式访问和“插件化”外部工具和资源到 LLMs,通过 FastMCP 库,开发者可以构建自定义 MCP 服务器,以增强 AI 在特定用例中的能力和实用性。

🛠️ **构建文档解析服务器**:使用 Python 和 FastMCP,可以创建一个文档解析服务器,使 MCP 主机能够理解 PDF 和 DOCX 文件,这通常是大多数 AI 主机默认无法解析的格式。通过定义工具、资源和提示,服务器能够处理文档读取、提供示例文件和调试。

⚙️ **服务器组件详解**:MCP 服务器由三个主要组件构成:工具(LLM 调用的功能)、资源(主应用程序提供的数据源)和提示(用户调用的模板)。文章详细介绍了如何定义这些组件,包括工具的注解、文档字符串、错误处理、资源 URI、路径参数以及提示的返回结构。

🚀 **FastMCP 库的应用**:文章详细介绍了如何使用 FastMCP 库来构建 MCP 服务器,包括安装、定义工具、添加资源和创建提示。FastMCP 提供了简洁的 API,简化了底层协议的复杂性,使得开发者能够专注于功能实现。

为什么搭建一个 MCP 服务器?

MCP 允许您以统一的方式访问和“插件化”外部工具和资源到 LLMs,这或许可以通过注册在 mcp.so 上的成千上万的服务器得到最佳体现。

尽管开箱即用的 MCP 服务器具有高可用性,但自己搭建 MCP 服务器仍然是一个关键技能,因为你可能会遇到需要自定义服务器的特殊工作流程。

说到这,本教程将构建一个使用 Python 和 FastMCP(Python 中构建 MCP 的首选库)读取文档/PDF 的简单 MCP 服务器。到此为止,您将获得构建、测试和部署您自己的 MCP 服务器的基础知识和更广泛的理解,以增强您特定用例中 AI 的能力和实用性。

如何使用现有的 MCP 服务器与 Cursor 或 Claude Desktop

对于 MCP 服务器的新手来说,让我们快速了解一下如何使用现有的服务器,然后再构建一个自定义的服务器。

好吧,首先,你需要一个 MCP 主机应用程序,比如 Claude Desktop,或者任何其他每周出现的基于 AI 的 IDE。我更喜欢 Cursor,因为它是最快速成长且频繁更新的 IDE 之一。

步骤 1: 安装 Node.js 和 UV Python

选择您的主机后,您必须在机器上安装 Node.js 和 UV Python 包管理器,因为这两种方式是 MCP 服务器的主要安装方式。以下是安装说明:

macOS:

# Using Homebrewbrew install node# Or download installer from https://nodejs.org/

Windows:

# Download installer from https://nodejs.org/# Or using wingetwinget install OpenJS.NodeJS

macOS:

# Using curlcurl -sSf https://install.python-uv.org | bash# Or using Homebrewbrew install uv

Windows:

# Using PowerShell (run as Administrator)powershell -c "irm https://install.python-uv.org | iex"# Or using pippip install uv

使用 node --versionnpx --version 和 uv --version 验证安装。

第2步:选择一个可以立即开始使用的服务器

你可以访问各种资源来找到适合你需求的服务器,包括 mcp.so“Awesome MCP 服务器”GitHub 仓库 ,或我们最近的文章 ,其中挑选了社区中最好的 15 个开放 MCP 服务器。

在这里,我们演示如何将 Firecrawl MCP 添加到 Cursor 中,这使得你的 IDE 可以直接在聊天线程中抓取网页、爬取整个网站,或基本上做任何 Firecrawl 提供的事情 

要开始,你需要从 Firecrawl 获取一个 API 密钥 ,它提供了慷慨的免费套餐。然后,在你的主目录中创建一个 ~/.cursor/mcp.json 文件,以便在 Cursor 工作区中提供 MCP 服务器:

mkdir ~/.cursortouch ~/.cursor/mcp.json

然后,在 JSON 文件中添加以下内容:

{  "mcpServers": {    "firecrawl-mcp": {      "command": "npx",      "args": ["-y", "firecrawl-mcp"],      "env": {        "FIRECRAWL_API_KEY": "YOUR-API-KEY"      }    }  }}

之后,重启你的 IDE 应该会让服务器在 Cursor 设置中可见:

然后,尝试让 Cursor 爬取任何公开可访问的网页。例如,你可以让它爬取 GitHub 的热门仓库页面,MCP 服务器会自动确定需要爬取 https://github.com/trending 页面上的所有仓库。

使用 FastMCP 构建 MCP 服务器的逐步指南

自行构建 MCP 服务器为扩展 LLM 功能提供了无限可能,可以使用自定义工具、资源和提示。FastMCP 是创建 MCP 服务器推荐的 Python 库,提供了一个干净的、基于装饰器的 API,可以处理底层协议的复杂性。在本节中,我们将构建一个文档解析服务器,使 MCP 主机能够理解 PDF 和 DOCX 文件——这是大多数 AI 主机默认无法解析的文件格式。

为了探索我们即将构建的服务器的代码,您可以访问我们的 GitHub 仓库 

0. 安装 FastMCP

第一步是安装 MCP Python SDK。我们将使用 UV,这是一个比 pip 更快且依赖解析更好的现代 Python 包管理器:

uv add "mcp[cli]"

[cli] 部分是可选的额外功能,包括命令行界面工具,如 MCP 检查器用于调试。使用 UV 的 add 命令可以自动将此包添加到您的环境中,而无需手动创建虚拟环境 - 它会在后台为您处理这些操作。

1. 定义工具、资源和提示

MCP 服务器由三个主要组件组成:

首先,我们需要导入必要的模块并创建 FastMCP 实例:

from mcp.server.fastmcp import FastMCPfrom mcp.server.fastmcp.prompts import base

FastMCP 是与 MCP 协议交互的高级接口。它负责连接管理、协议合规性以及代码与宿主应用程序之间的消息路由。

接下来,我们将初始化 FastMCP 实例:

mcp = FastMCP("DocumentReader", dependencies=["markitdown[all]"])

我们将服务器命名为“DocumentReader”,并指定“markitdown[all]”作为依赖项。Markitdown 是一个轻量级的库,可以将各种文档格式转换为 markdown,这是 LLMs 的理想格式。

在深入实现之前,重要的是要规划我们的服务器将提供哪些功能。对于我们的文档阅读器,我们将定义:

@mcp.tool()def read_pdf(file_path: str) -> str:    """Read a PDF file and return the text."""    return "This is a test"@mcp.tool()def read_docx(file_path: str) -> str:    """Read a DOCX file and return the text."""    return "This is a test"@mcp.resource("file://resource-name")def always_needed_pdf():    # Return the file path    return "This PDF file is always needed."@mcp.prompt()def debug_pdf_path(error: str) -> list[base.Message]:    return f"I am debugging this error: {error}"

上述代码定义了我们的 MCP 服务器的结构,其中包含占位实现:

每个组件使用装饰器模式(@mcp.tool(),@mcp.resource(),@mcp.prompt())来将函数注册到 MCP 服务器。文档字符串至关重要,因为它们提供了描述,帮助 LLM 理解何时以及如何使用每个组件。

2. 将工具添加到服务器

现在我们已经了解了 MCP 服务器的结构,让我们使用实际的功能实现我们的文档读取工具:

from mcp.server.fastmcp import FastMCPfrom mcp.server.fastmcp.prompts import basefrom markitdown import MarkItDownimport osmcp = FastMCP("DocumentReader", dependencies=["markitdown[all]"])md = MarkItDown()@mcp.tool(    annotations={        "title": "Read PDF Document",        "readOnlyHint": True,        "openWorldHint": False    })def read_pdf(file_path: str) -> str:    """Read a PDF file and return the text content.        Args:        file_path: Path to the PDF file to read    """    try:        # Expand the tilde (if part of the path) to the home directory path        expanded_path = os.path.expanduser(file_path)                # Use markitdown to convert the PDF to text        return md.convert(expanded_path).text_content    except Exception as e:        # Return error message that the LLM can understand        return f"Error reading PDF: {str(e)}"@mcp.tool(    annotations={        "title": "Read Word Document",        "readOnlyHint": True,        "openWorldHint": False    })def read_docx(file_path: str) -> str:    """Read a DOCX file and return the text content.        Args:        file_path: Path to the Word document to read    """    try:        expanded_path = os.path.expanduser(file_path)                # Use markitdown to convert the DOCX to text        return md.convert(expanded_path).text_content    except Exception as e:        return f"Error reading DOCX: {str(e)}"

@mcp.tool() 装饰器将每个函数注册为一个工具,LLM 可以调用这些工具。让我们看看关键组件:

    工具注解 : 我们添加了注解来提供关于工具的元数据:

      title: 在 UI 显示中该工具的人类可读名称readOnlyHint: 由于这些工具只读取文件而不进行任何修改,因此设置为 TrueopenWorldHint: 设置为 False,因为这些工具处理的是本地文件,而不是外部系统

    描述性文档字符串 : 文档字符串至关重要,它们帮助 LLM 理解何时以及如何使用该工具。在 Args: 部分包含参数描述可以提供更多的上下文。

    错误处理 : 我们将转换包裹在 try/except 块中,以捕获并以 LLM 可以理解的格式返回任何错误。这遵循了 MCP 工具规范的最佳实践。

    路径处理 : 我们使用 os.path.expanduser() 安全地展开文件路径中的任何波浪号字符,这对于支持类似于 ~/Documents/file.pdf 的路径非常重要。

    Markitdown 使用MarkItDown 库提供了一个简单的 .convert() 方法,可以处理各种文档格式并返回文本内容。.text_content 属性可以给我们纯文本,方便传递给 LLM。

这些工具非常简单,但它们遵循一个你可以扩展以实现更复杂功能的模式。例如,你可以添加工具来:

请记住,工具应该专注于执行单一明确的操作。这使得它们更容易被 LLM 理解和正确使用。

要了解 MCP 中工具的更多信息,请参阅官方文档 

将资源添加到服务器

在 MCP 中,资源允许服务器暴露静态或动态数据,这些数据可以被宿主应用程序访问。与由模型控制的工具不同,资源是由应用程序控制的,这意味着它们通常作为上下文提供给模型,而不是由模型直接调用。

让我们为我们的文档阅读器服务器实现一些资源:

# document_reader.py@mcp.resource("file://document/pdf-example")def provide_example_pdf():    """Provide the content of an example PDF document.        This resource makes a sample PDF available to the model without requiring    the user to specify a path.    """    try:        # Use an absolute path with the file:// schema        pdf_path = "file:///Users/bexgboost/Downloads/example.pdf"        # Convert the PDF to text using markitdown        return md.convert(pdf_path).text_content    except Exception as e:        return f"Error providing example PDF: {str(e)}"@mcp.resource("file://document/recent/{filename}")def provide_recent_document(filename: str):    """Provide access to a recently used document.        This resource shows how to use path parameters to provide dynamic resources.    """    try:        # Construct the path to the recent documents folder        recent_docs_folder = os.path.expanduser("~/Documents/Recent")        file_path = os.path.join(recent_docs_folder, filename)                # Validate the file exists        if not os.path.exists(file_path):            return f"File not found: {filename}"                    # Convert to text using markitdown        return md.convert(file_path).text_content    except Exception as e:        return f"Error accessing document: {str(e)}"

让我们来检查一下这些资源的关键组成部分:

    资源 URI: MCP 中的资源通过类似于 URI 的路径来标识,带有特定的模式。在我们的示例中:

      file://document/pdf-example 是一个静态资源路径file://document/recent/{filename} 使用路径参数来表示动态资源

    路径参数 : 第二个资源展示了如何使用路径参数(用花括号包围)来创建动态资源。参数 {filename} 作为参数传递给函数。

    URI 模式file:// 模式表示这些资源代表文件内容。其他常见的模式包括 http://data://,或与您的应用程序相关的自定义模式。

    返回值 : 资源可以直接返回文本内容,这就是我们通过返回从文档中提取的文本来实现的方式。

    错误处理 : 就像使用工具时一样,我们包含了适当的错误处理,以便在资源无法访问时提供有用的反馈。

资源不仅限于文件 - 数据库、API 和其他数据源也可以作为资源进行暴露。例如:

# A sample - don't add this to our MCP server@mcp.resource("db://customer/{customer_id}")def get_customer_record(customer_id: str):    """Retrieve a customer record from the database."""    try:        # In a real application, you would query your database        connection = database.connect("customer_database")        result = connection.query(f"SELECT * FROM customers WHERE id = {customer_id}")        return json.dumps(result.to_dict())    except Exception as e:        return f"Error retrieving customer data: {str(e)}"@mcp.resource("api://weather/current/{location}")def get_current_weather(location: str):    """Get current weather for a specific location."""    try:        # In a real application, you would call a weather API        response = requests.get(f"https://weather-api.example/current?location={location}")        return response.text    except Exception as e:        return f"Error retrieving weather data: {str(e)}"

我们在这里定义的资源只是占位示例 - 在实际应用中,你可能会创建资源,这些资源:

在设计资源 URI 时,重要的是创建一个逻辑的、分层的结构,使每个资源的目的清晰明了。路径应该表明该资源提供了什么类型的数据,以及它如何与其他系统中的资源相关联。

要了解 MCP 中的资源更多信息,请参阅官方文档 

4. 向服务器添加提示

在 MCP 中,提示允许您定义可重用的提示模板,这些模板可以在客户端应用程序中呈现给用户。与工具(模型控制)和资源(应用程序控制)不同,提示是用户控制的,用户可以通过明确提及它们在主机 UI 中明确选择它们。

让我们为我们的文档阅读器服务器实现一个提示:

from mcp.server.fastmcp.prompts import base@mcp.prompt()def debug_pdf_path(error: str) -> list[base.Message]:    """Debug prompt for PDF issues.        This prompt helps diagnose issues when a PDF file cannot be read.        Args:        error: The error message encountered    """    return [        base.Message(            role="user",            content=[                base.TextContent(                    text=f"I'm trying to read a PDF file but encountered this error: {error}. "                    f"How can I resolve this issue? Please provide step-by-step troubleshooting advice."                )            ]        )    ]

让我们来了解一下这个提示实现中的关键组件:

    提示定义 : 提示使用 @mcp.prompt() 装饰器定义,该装饰器将其注册到 MCP 服务器。

    函数参数error 参数成为用户在调用提示时可以提供的参数。参数可以是必需的或可选的(带有默认值)。

    返回结构 : 提示返回一个 base.Message 对象列表,这些对象代表要发送给 LLM 的对话。每个消息包含:

      角色 (通常是“用户”或“助手”)内容 ,包含一个或多个内容项(通常是 TextContent

    文档字符串 :就像工具和资源一样,清晰的文档字符串有助于解释提示的目的并记录其参数。

这个提示会在客户端应用程序的用户界面中出现,使用户能够轻松获取与 PDF 相关的错误帮助。例如,当遇到读取文件的错误时,他们可以从 UI 中选择“调试 PDF 路径”,并提供错误消息以获取故障排除建议。

在编写提示时,请记住它们旨在创建可重用的标准交互,使常见任务更容易完成。设计良好的提示可以显著提高 MCP 服务器的易用性,为用户完成特定目标提供清晰的路径。

要了解有关 MCP 中提示使用的更多信息,请参阅官方文档 

5. 调试 MCP 服务器

FastMCP 附带了一个内置的调试工具,称为 MCP Inspector,它提供了一个干净的 UI,用于测试您的服务器组件,而无需连接到 MCP 主机应用程序。此调试器在部署之前验证您的实现方面非常有价值。

要启动调试器,请使用 mcp dev 命令,后跟您的脚本名称:

mcp dev document_reader.py

这将启动您的 MCP 服务器并在浏览器中打开 Inspector 界面。如果它没有自动打开,您通常可以通过 http://127.0.0.1:6274 访问它。

加载 Inspector 后,点击“连接”按钮以与您的服务器建立连接。界面将显示用于探索您的 MCP 服务器三大组件的选项卡:

在工具选项卡中,您应该看到我们之前创建的两个工具:“读取 PDF 文档”和“读取 Word 文档”。您可以点击任何工具以查看其详细信息并提供参数进行测试。例如,点击 PDF 工具将显示一个文件路径的输入字段。在您的机器上输入一个 PDF 文件的完整路径,该工具将执行并显示提取的文本内容。

同样,在资源选项卡中,您可以测试您的资源,通过选择它们并提供任何所需的路径参数。对于我们的动态资源,带有 {filename} 参数,您将被提示输入一个文件名以测试该资源。

对于提示,检查员将显示一个表单,其中包含每个提示参数的字段。您可以填写这些字段以查看提示在发送给 LLM 之前将如何呈现。

检查员特别适用于:

    验证参数处理 :确保您的工具、资源和提示能够正确处理不同的参数值,包括边界情况测试错误处理 :故意提供无效输入以验证您的错误处理逻辑检查内容格式 : 确保文本提取和格式化按预期工作调试路径问题 : 在不同操作系统上解决文件路径处理问题

在实际工作流程中,一旦你使用检查器验证了你的 MCP 服务器,你就可以将其添加到像 Claude 桌面或 Cursor 这样的 MCP 主机中。然后,用户可以像“总结我位于~/Downloads/test.pdf 的 PDF 内容”这样的自然语言问题,LLM 会自动识别合适的工具,提取文件路径参数,并提供所需的信息。

有关 Inspector 和 MCP 调试技术的更多细节,您可以参考官方文档中的 MCP Inspector 页面 和 调试指南 

6. 本地连接到 MCP 服务器

现在,真相时刻 - 使用主机应用程序本地测试我们的服务器。对于 Claude 桌面,设置非常简单。在你的服务器脚本所在的同一目录中,调用 mcp install 命令:

mcp install document_reader.py

重启 Claude 桌面,服务器必须可见:

让我们让它总结我们的 Word 文档:

我们的服务器在 Claude 中已经完全运行。现在,让我们通过将以下 JSON 配置粘贴到 ~/.cursor/mcp.json 文件中将其添加到 Cursor 中:

{    "mcpServers": {        ..., // Other servers        "document-reader-mcp": {            "command": "uv",            "args": [            "--directory",            "/path/to/server/directory",            "run",            "document_reader.py"            ]        }    }}

之后重启 Cursor,服务器必须在你的设置中可见:

让我们在我当前目录中的一个 PDF 文档上进行测试:

如你所见,我没有提供 PDF 文件的路径。在当前环境中,提示本身已经足够让 Cursor 确定需要使用完整路径,以便 MCP 工具能够正确运行。

如果你的服务器仅用于个人使用,那么我们的工作就完成了。然而,如果你想让它对外公开供外部用户使用,请继续阅读下一节。

7. 部署 MCP 服务器

在成功在本地构建并测试完 MCP 服务器后,下一步是将其部署以供更广泛的使用。MCP 服务器部署主要有两种主要方法:使用 stdio 传输的本地托管和使用 SSE 传输的远程托管。在本节中,我们将探讨如何通过 PyPI 分发我们的文档读取服务器。

将 MCP 服务器打包为 Python 包可以让其他人通过简单的命令轻松安装和使用它。以下是如何结构化并发布您的 MCP 服务器的方法:

    首先,将现有的 document_reader.py 文件重命名为 server.py,以与包结构保持一致,然后创建必要的目录结构:
mcp-document-reader/├── src/│   └── document_reader/│       ├── __init__.py│       └── server.py├── pyproject.toml├── README.md└── LICENSE

2. 在 __init__.py 文件中,导入并暴露服务器的主要组件:

# src/document_reader/__init__.pyfrom .server import read_pdf, read_docx, mcp

3. 定义您的包元数据在 pyproject.toml 中(将名称改为独一无二的名称,因为我已经使用了下面的名称):

[build-system]requires = ["setuptools>=61.0", "wheel"]build-backend = "setuptools.build_meta"[project]name = "mcp-document-reader"version = "0.1.0"description = "MCP server for reading and extracting text from PDF and DOCX files"readme = "README.md"authors = [    {name = "Your Name", email = "your.email@example.com"}]license = {text = "MIT"}classifiers = [    "Programming Language :: Python :: 3",    "License :: OSI Approved :: MIT License",    "Operating System :: OS Independent",]dependencies = [    "mcp>=1.2.0",    "markitdown[all]",]requires-python = ">=3.9"[project.scripts]mcp-document-reader = "document_reader.server:main"[tool.setuptools]package-dir = {"" = "src"}

4. 在你的 server.py 文件中添加一个 main() 函数使其可执行:

# src/document_reader/server.pydef main():    mcp.run()if __name__ == "__main__":    main()

5. 使用构建工具构建你的包:

uv pip install buildpython -m build

这将在 dist/ 目录中生成分发文件。

    创建一个 PyPI 账户并上传您的包:

在上传到 PyPI 之前,您需要在 PyPI.org 创建一个账户。注册后,您必须验证您的电子邮件,添加 2FA 验证并生成您的设置中的 API 令牌。然后,您可以使用 twine 安全地上传您的包。Twine 是一个用于将 Python 包发布到 PyPI 的工具,通过 HTTPS 提供更好的安全性,并支持现代身份验证方法。

uv pip install twine

在首次上传到 PyPI 时,您将被提示输入 PyPI 账户凭据。为了更好的安全性,建议使用 API 令牌而不是密码:

twine upload dist/*

或者,您可以在家目录中的 .pypirc 文件中存储凭据,或者使用环境变量。

发布后,用户可以安装您的 MCP 服务器:

uv add mcp-document-reader

并直接运行:

mcp-document-reader

例如,我的服务器现在可以在 pypi.org/project/mcp… 访问。

要使用打包的服务器与 Claude Desktop 或其他 MCP 客户端,用户可以在配置文件中使用 uv 添加它:

"pypi-document-reader-mcp": {      "command": "uv",      "args": [        "--directory",        "/Users/bexgboost/miniforge3/lib/python3.12/site-packages/document_reader",        "run",        "server.py"      ]    }

注意:为了在您的主机上正确安装服务器,您必须像上面那样提供安装目录。

这种部署方法非常适合与他人共享您的 MCP 服务器,同时保持本地托管的简单性。服务器运行在用户的机器上,因此无需担心托管费用或管理远程基础设施的问题。

如需了解其他托管选项的详细信息,包括使用 SSE 传输进行远程部署,请参阅 Aravind Putrevu 关于托管 MCP 服务器的指南 

结论

FastMCP 使构建扩展特定任务的 LLM 能力的自定义工具变得容易。我们的文档阅读器示例展示了如何通过简单的代码启用 AI 助手处理 PDF 和 DOCX 文件。此模式适用于许多应用程序,包括网络爬虫、数据分析和 API 集成。对于 AI 应用程序中的网络爬虫,Firecrawl 提供了一个与 Claude Desktop 和 Cursor 兼容的现成 MCP 服务器。

MCPs 允许开发人员以最少的努力创建语言模型的标准扩展。本教程涵盖了使用 Python 构建和部署简单 MCP 服务器的基础知识。要获取更多详细信息,请参阅官方 MCP 文档以获取完整的参考资料。访问 Firecrawl 博客以获取将网络爬虫与 AI 结合的实用教程,并在实际项目中实现这些技术。

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

MCP FastMCP Python AI 文档处理
相关文章