10分钟打造本地MCP服务:让AI调用你的一切工具

前言

MCP(Model Context Protocol,模型上下文协议)让 AI 能够调用外部工具。但市面上的 MCP Server 有限,如果你想让 AI 访问你本地的数据、脚本、文件,自建一个本地 MCP 就是最好的选择。

本文手把手教你在 10 分钟内搭建一个本地 MCP 服务,并对接到 QwenPaw(同样适用于 Claude Desktop、Cline 等 MCP 客户端)。


一、MCP 是什么?

MCP 是 Anthropic 在 2024 年底开源的协议标准,目标是让 AI 模型与外部工具之间的连接标准化

你的 AI 应用 (QwenPaw / Claude Desktop / ...)
        ↕  MCP 协议
 MCP Server (本地 / 远程)
        ↕
 你的工具 (文件搜索、API 调用、本地脚本...)

只要实现了 MCP 协议,任何 AI 客户端都能无缝调用。


二、准备工作

1. Python 环境(推荐 3.10+)

python --version  # 确认 >= 3.10

2. 创建项目目录

mkdir ~/my-mcp-server && cd ~/my-mcp-server
python -m venv .venv
source .venv/bin/activate  # macOS/Linux
# .venv\Scripts\activate   # Windows

3. 安装依赖

pip install fastmcp python-dotenv requests

三、编写你的第一个 MCP 服务

这里用 文件搜索工具 作为示例——让 AI 能搜索本地指定目录下的文件。

创建文件 my_mcp.py

"""
我的第一个本地 MCP 服务
功能:搜索本地文件、读取文件内容
"""

import os
from pathlib import Path
from fastmcp import FastMCP

# 创建 MCP 服务,名称随意
mcp = FastMCP("my-local-mcp")

# 搜索文件工具
@mcp.tool()
def search_files(directory: str, pattern: str, max_results: int = 20) -> dict:
    """
    在指定目录下搜索包含关键词的文件

    Args:
        directory: 要搜索的目录路径
        pattern: 文件名或扩展名关键词(如 ".py" 或 "config")
        max_results: 最大返回数量
    """
    path = Path(directory).expanduser().resolve()
    if not path.exists():
        return {"success": False, "error": f"目录不存在: {path}"}

    results = []
    for item in path.rglob(f"*{pattern}*"):
        if item.is_file() and len(results) < max_results:
            try:
                size = item.stat().st_size
                results.append({
                    "name": item.name,
                    "path": str(item),
                    "size": size,
                    "size_str": f"{size/1024:.1f} KB" if size > 1024 else f"{size} B"
                })
            except PermissionError:
                continue

    return {
        "success": True,
        "count": len(results),
        "files": results
    }


# 读取文件内容工具
@mcp.tool()
def read_file(file_path: str, max_lines: int = 100) -> dict:
    """
    读取本地文件内容

    Args:
        file_path: 文件完整路径
        max_lines: 最大读取行数
    """
    path = Path(file_path).expanduser().resolve()
    if not path.exists():
        return {"success": False, "error": f"文件不存在: {path}"}

    try:
        with open(path, "r", encoding="utf-8") as f:
            lines = [f.readline() for _ in range(max_lines)]
            content = "".join(lines)

        return {
            "success": True,
            "name": path.name,
            "path": str(path),
            "content": content,
            "truncated": len(content) >= max_lines * 100
        }
    except Exception as e:
        return {"success": False, "error": f"读取失败: {str(e)}"}


# ★★★ 关键:关闭欢迎 banner,避免污染 JSON-RPC 流 ★★★
if __name__ == "__main__":
    mcp.run(show_banner=False)

⚠️ show_banner=False 非常重要! FastMCP 默认会往 stdout 打印 ASCII banner,这会破坏 MCP 的 JSON-RPC 通信,导致客户端无法解析响应。务必加上。


四、测试 MCP 服务

在终端中发送 MCP 协议消息,验证服务是否正常:

cd ~/my-mcp-server
source .venv/bin/activate

# 发送 initialize + tools/list 请求
printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}\n{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}\n' \
  | python my_mcp.py 2>/dev/null | grep '"name"'

正常情况下应该看到:

{"jsonrpc":"2.0","id":1,"result":{...}}
{"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"search_files",...},{"name":"read_file",...}]}}

五、接入 QwenPaw

1. 修改 QwenPaw 配置

找到你的 QwenPaw 工作目录下的 agent.json,添加 MCP 客户端配置:

{
  "mcp": {
    "clients": {
      "my_local_mcp": {
        "name": "my_local_mcp",
        "description": "本地文件搜索 MCP",
        "enabled": true,
        "transport": "stdio",
        "command": "/Users/你的用户名/my-mcp-server/.venv/bin/python",
        "args": ["/Users/你的用户名/my-mcp-server/my_mcp.py"],
        "env": {}
      }
    }
  }
}

2. 重启 QwenPaw

qwenpaw daemon restart

3. 验证

在 QwenPaw 中发送消息:

"请在 ~/Downloads 目录下搜索包含 '合同' 的文件"

如果返回了文件列表,说明 MCP 已经成功接入!


六、常见问题排查

Q1: MCP 连接成功但工具列表为空

原因: tools/list 请求前 MCP 服务崩溃了。
排查:

# 单独测试 initialize
printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}\n' \
  | python my_mcp.py 2>&1 | head -5

如果看到 ASCII banner(╭──...)或大量无关输出,说明 show_banner 问题,把 mcp.run() 改成 mcp.run(show_banner=False)

Q2: QwenPaw 日志显示 "lifecycle task exited"

原因: MCP 服务进程启动后立即退出。
排查:

python my_mcp.py  # 前台运行,看是否有报错

Q3: ImportError: No module named 'fastmcp'

原因: Python 路径没有找到虚拟环境中的包。
排查: 确认 agent.jsoncommand 指向的是 虚拟环境中的 Python,不是系统 Python。

Q4: Token 问题(调用外部 API 时)

在项目根目录创建 .env 文件:

echo 'API_KEY=your_api_key_here' >> .env

然后在 Python 中加载:

from dotenv import load_dotenv
load_dotenv()
import os
api_key = os.getenv("API_KEY", "")

七、进阶:更复杂的服务示例

调用本地脚本

@mcp.tool()
def run_script(script_path: str, args: str = "") -> dict:
    """执行本地 Python 脚本"""
    import subprocess
    result = subprocess.run(
        f"python {script_path} {args}",
        shell=True,
        capture_output=True,
        text=True,
        timeout=60
    )
    return {
        "success": result.returncode == 0,
        "stdout": result.stdout,
        "stderr": result.stderr,
        "returncode": result.returncode
    }

数据库查询

@mcp.tool()
def query_db(sql: str) -> dict:
    """执行 SQLite 查询"""
    import sqlite3
    conn = sqlite3.connect("/path/to/your/database.db")
    cur = conn.cursor()
    try:
        cur.execute(sql)
        rows = cur.fetchall()
        columns = [desc[0] for desc in cur.description] if cur.description else []
        return {"success": True, "columns": columns, "rows": rows}
    except Exception as e:
        return {"success": False, "error": str(e)}
    finally:
        conn.close()

八、总结

步骤 核心操作
1 pip install fastmcp
2 @mcp.tool() 装饰器定义工具函数
3 mcp.run(show_banner=False) 启动服务
4 在 QwenPaw agent.json 中添加客户端配置
5 重启 QwenPaw,开始使用

MCP 的强大之处在于,只要你能在 Python 里实现某个功能,AI 就能通过自然语言调用它。本地文件搜索、数据库查询、API 调用、脚本执行……一切皆可 MCP。


如果你觉得这篇文章有帮助,欢迎在博客园留言交流!

posted @ 2026-04-24 11:58  司野良  阅读(397)  评论(0)    收藏  举报