首页 › tutorials › FastMCP造日志分析MCP

概述

本教程带你用 Python 的 FastMCP 框架从零开发一个完整的 MCP 服务器——LogScope 日志分析工具,让 Claude Code 能直接读取、搜索、统计你的应用日志。适合已会用 Claude Code、想把自己的工具喂给 AI 的开发者。学完你将掌握 MCP 的三大原语(工具/资源/提示词)、FastMCP 的装饰器开发范式、用 MCP Inspector 调试、以及把自研服务器接入 Claude Code 的完整闭环。

前置条件


一、先搞懂:为什么是 FastMCP

MCP(Model Context Protocol,模型上下文协议) 是 Anthropic 于 2024 年开源的标准协议,专门解决一个问题:让 AI 用统一的方式调用外部工具和数据。没有 MCP 之前,每接一个外部系统(数据库、监控、Jira……)都得手写一套对接逻辑;有了 MCP,你只要按协议暴露「工具」「资源」「提示词」,任何支持 MCP 的客户端(Claude Code、Claude Desktop、Cursor 等)都能直接调用。

FastMCP 则是 MCP 生态里最主流的 Python 框架。它的核心卖点一句话概括:把一个普通 Python 函数变成 MCP 工具,只需一个装饰器。类型注解自动生成 JSON Schema,docstring 自动变成给 AI 看的工具说明,参数校验、协议握手、传输层细节全部自动处理。据官方数据,某种版本的 FastMCP 驱动着全网约 70% 的 MCP 服务器,每天下载量百万级——FastMCP 1.0 甚至在 2024 年被合并进了官方 MCP Python SDK。

MCP 服务器对外暴露三类「原语」,理解这三者你就抓住了 MCP 的全部:

原语 作用 FastMCP 写法 给 AI 的语义
工具 Tool 可执行的函数(有副作用/返回值) @mcp.tool 「我可以帮你做这件事」
资源 Resource 可读取的数据(文件、DB 内容) @mcp.resource("uri") 「我可以帮你读这份资料」
提示词 Prompt 可复用的对话模板 @mcp.prompt 「这里有套开场白模板」

关键区分:工具是动词,资源是名词。让 AI「搜索日志」用工具,让 AI「看一眼某个日志文件」用资源。本教程两者都会用到。


二、动手:搭建 FastMCP 开发环境

新建项目目录并安装依赖:

mkdir logscope-mcp && cd logscope-mcp
uv init --python 3.12          # 初始化项目,生成 pyproject.toml
uv add fastmcp                 # 安装最新 FastMCP(当前 v3.x)
uv add --dev ruff              # 可选:代码风格检查

验证安装成功:

uv run python -c "import fastmcp; print(fastmcp.__version__)"
# 预期输出类似:3.x.x

常见问题

现象 原因 解决
import fastmcp 失败,提示 No module pip 升级后残留旧版 跑 pip install --force-reinstall fastmcp(用 uv 则不受影响)
uv init 报找不到命令 未安装 uv pip install uv 后重试
版本号显示 1.x 装到了旧官方 SDK uv add fastmcp@latest 升到独立版

三、第一个工具:体会装饰器的魔法

创建 logscope_mcp.py,先写最小可运行版本:

from fastmcp import FastMCP

mcp = FastMCP("LogScope")   # 实例化服务器,名字会展示给客户端

@mcp.tool
def greet(name: str) -> str:
    """向指定用户打招呼。"""
    return f"你好,{name}!我是 LogScope 日志助手。"

if __name__ == "__main__":
    mcp.run()               # 默认走 stdio 传输,适配 Claude Code

运行:uv run python logscope_mcp.py

预期结果:进程不会退出,它在 stdin/stdout 上监听 JSON-RPC 消息——这就是「服务器在运行」的样子(看起来像卡住了,其实正常)。

此时 FastMCP 已经自动帮你做了三件事:

  1. 用函数名 greet 作为工具名
  2. 解析 docstring「向指定用户打招呼」作为给 AI 的描述
  3. 根据 name: str 类型注解生成参数 schema(AI 必须传一个字符串才能调用)

这就是 FastMCP 的核心红利:你写的是普通 Python,得到的是协议完备的 MCP 工具。


四、实战项目:LogScope 日志分析 MCP

现在构建真正的工具集。假设你有一堆应用日志(Nginx、应用服务日志等),想让 Claude 直接帮你查错。最终 AI 能说:「帮我看下 app.log 最近 50 行里所有 ERROR」并自动调用我们的工具。

先准备测试数据目录:

mkdir -p logs
# 造一个测试日志(也可换成你真实日志)
cat > logs/app.log <<'EOF'
2026-07-12 09:01:12 INFO  用户 u_8821 登录成功
2026-07-12 09:03:44 WARN  缓存命中率降至 41%
2026-07-12 09:10:08 ERROR 数据库连接超时 (host=db-2)
2026-07-12 09:10:09 ERROR 重试 1/3 失败
2026-07-12 09:11:30 ERROR 重试 2/3 失败
2026-07-12 09:12:50 INFO  数据库连接恢复
2026-07-12 09:45:00 WARN  磁盘使用率 88%
2026-07-12 10:02:17 ERROR 订单 ord_55231 支付回调签名校验失败
EOF

完整的 logscope_mcp.py:

from pathlib import Path
from collections import Counter
import re

from fastmcp import FastMCP, Context

LOG_DIR = Path("logs").resolve()   # 日志根目录,按需改

mcp = FastMCP("LogScope")

LEVELS = ("DEBUG", "INFO", "WARN", "WARNING", "ERROR", "FATAL", "CRITICAL")


def _resolve(name: str) -> Path:
    """安全地解析日志路径,禁止目录穿越。"""
    base = LOG_DIR
    target = (base / name).resolve()
    if not str(target).startswith(str(base)):
        raise ValueError(f"非法路径:{name}")   # 防止 ../../../etc/passwd
    return target


# ---------- 工具 1:列出可用日志 ----------
@mcp.tool
def list_logs() -> list[dict]:
    """列出 logs 目录下所有 .log 文件,返回文件名、大小、行数。

    当用户不确定有哪些日志可查时,先调用此工具获取清单。
    """
    result = []
    for f in sorted(LOG_DIR.glob("*.log")):
        with f.open(encoding="utf-8", errors="replace") as fh:
            lines = sum(1 for _ in fh)
        result.append({
            "name": f.name,
            "size_bytes": f.stat().st_size,
            "lines": lines,
        })
    return result


# ---------- 工具 2:按正则 + 级别搜索 ----------
@mcp.tool
def search_logs(
    filename: str,
    pattern: str = "",
    level: str = "",
    last_n: int = 0,
) -> str:
    """在指定日志文件中搜索匹配的行。

    参数:
        filename: 文件名,如 "app.log"(不含目录)
        pattern: 正则表达式,为空则匹配所有行
        level: 日志级别过滤,如 "ERROR" / "WARN"(不区分大小写)
        last_n: 仅在最后 N 行中搜索,0 表示全文

    返回匹配行(最多 200 条),每行带行号前缀。
    """
    try:
        path = _resolve(filename)
    except ValueError as e:
        return f"[错误] {e}"          # 预期内的错误,返回友好文本给 AI

    if not path.exists():
        return f"[错误] 文件不存在:{filename}"

    try:
        regex = re.compile(pattern, re.IGNORECASE) if pattern else None
    except re.error as e:
        return f"[错误] 正则非法:{e}"

    level = level.upper()
    matches = []
    with path.open(encoding="utf-8", errors="replace") as fh:
        all_lines = fh.readlines()
    scope = all_lines[-last_n:] if last_n > 0 else all_lines
    for i, line in enumerate(scope, start=len(all_lines) - len(scope) + 1):
        up = line.upper()
        if level and level not in up:
            continue
        if regex and not regex.search(line):
            continue
        matches.append(f"{i}: {line.rstrip()}")
        if len(matches) >= 200:       # 截断,避免撑爆上下文
            break
    return "\n".join(matches) if matches else "[无匹配行]"


# ---------- 工具 3:按级别统计 ----------
@mcp.tool
def log_stats(filename: str) -> dict:
    """统计指定日志文件各级别的行数。

    用于快速判断日志整体健康度,如 ERROR 是否激增。
    """
    path = _resolve(filename)
    if not path.exists():
        return {"error": f"文件不存在:{filename}"}
    counter: Counter = Counter()
    with path.open(encoding="utf-8", errors="replace") as fh:
        for line in fh:
            for lv in LEVELS:
                if lv in line.upper():
                    counter[lv] += 1
                    break
            else:
                counter["OTHER"] += 1
    return {"filename": filename, "total": sum(counter.values()), "by_level": dict(counter)}


# ---------- 工具 4:尾随(异步 + Context 进度上报)----------
@mcp.tool
async def tail_logs(filename: str, n: int = 20, ctx: Context) -> str:
    """返回日志文件最后 N 行(默认 20)。

    演示异步工具与 MCP Context:通过 ctx 上报进度日志。
    """
    await ctx.info(f"开始读取 {filename} 的最后 {n} 行")
    path = _resolve(filename)
    if not path.exists():
        return f"[错误] 文件不存在:{filename}"
    with path.open(encoding="utf-8", errors="replace") as fh:
        lines = fh.readlines()
    tail = lines[-n:] if n < len(lines) else lines
    await ctx.info(f"读取完成,共 {len(tail)} 行")
    return "".join(tail).rstrip() or "[文件为空]"


# ---------- 资源:暴露整份日志给 AI 直读 ----------
@mcp.resource("logs://{filename}")
def read_log(filename: str) -> str:
    """以资源形式暴露日志全文,AI 可直接引用读取。"""
    path = _resolve(filename)
    if not path.exists():
        return f"[错误] 文件不存在:{filename}"
    return path.read_text(encoding="utf-8", errors="replace")


if __name__ == "__main__":
    mcp.run()

这段代码的几个设计要点:

  1. 路径安全:_resolve() 强制目标必须落在 LOG_DIR 内,挡住 ../../etc/passwd 这类目录穿越攻击——给 AI 的工具必须有边界。
  2. 错误降级:文件不存在、正则非法这类「预期错误」返回字符串而非抛异常,AI 读到 [错误] xxx 会自己换思路重试;真正的 bug 才让它崩。
  3. 结果截断:search_logs 最多 200 行,防止几万行日志撑爆模型上下文窗口。
  4. 类型即文档:返回 list[dict]、dict 等结构化类型,FastMCP 会生成 output schema,AI 更易解析。
  5. Context 进度上报:tail_logs 用 await ctx.info() 把进度发回客户端,长任务不会显得「没反应」。

五、调试:用 MCP Inspector 可视化测试

写完代码先别急着接 Claude Code,用官方调试器 MCP Inspector 验证每个工具。FastMCP 自带一条捷径——fastmcp dev 命令会自动拉起 Inspector:

uv run fastmcp dev logscope_mcp.py

执行后会自动打开浏览器(默认 http://localhost:6274),界面分几栏:

手动测试 search_logs:在 Tools 栏选中它,参数填 filename=app.log、level=ERROR,点 Run,预期返回三行 ERROR 记录(含行号)。

也可以用 Inspector 通用启动器:npx @modelcontextprotocol/inspector uv run python logscope_mcp.py,效果相同,适合非 FastMCP 服务器。

常见问题

现象 解决
Inspector 打不开、端口占用 fastmcp dev --port 6280 logscope_mcp.py 换端口
工具列表为空 检查 @mcp.tool 是否拼对(v3 可不带括号),重启 dev
Run 报「正则非法」 你在 Inspector 里手填的 pattern 可能含特殊字符,先空着试
中文乱码 文件读取已加 errors="replace",Inspector 控制台显示编码问题可忽略

六、接入 Claude Code:打通最后一公里

调试通过后,把 LogScope 注册成 Claude Code 的本地 stdio 服务器。

方式 A:命令行注册(推荐,进 user 作用域)

# 注意 -- 之后是启动命令;用 uv run 保证用对环境
claude mcp add logscope -- uv run --project /home/you/logscope-mcp python logscope_mcp.py

方式 B:项目级 .mcp.json(团队共享,随仓库提交)

在项目根目录建 .mcp.json:

{
  "mcpServers": {
    "logscope": {
      "command": "uv",
      "args": ["run", "--project", "/home/you/logscope-mcp", "python", "logscope_mcp.py"]
    }
  }
}

团队成员 git pull 后,首次在项目里启动 Claude Code 会提示信任该 MCP 配置,确认即生效。

验证连接:

claude mcp list          # 应看到 logscope
claude mcp get logscope  # 查看详细配置与当前状态

进入 Claude Code 交互界面,输入 /mcp 能看到 logscope 显示 connected,并列出四个工具。

⚠️ 最大坑:stdio 服务器卡在 connecting 永不就绪

这是 2026 年被频繁报告的问题(Claude Code 2.x 尤甚)。根因几乎都是 stdout 被污染:MCP stdio 协议要求 stdout 只能走 JSON-RPC,一旦你在代码里写了 print("启动成功") 这类调试输出,客户端就解析不了握手包,表现为永久 connecting。

解决清单:

  1. 删掉所有 print()——调试输出改用 ctx.info() 经 MCP 日志通道上报,或写文件/stderr
  2. 确认 mcp.run() 没传错 transport(接 Claude Code 必须是默认 stdio)
  3. 用 claude --mcp-debug 启动查看详细握手日志定位
  4. 路径用绝对路径(--project 参数),避免工作目录不一致导致找不到文件

七、实战对话:让 Claude 读你的日志

接通后在 Claude Code 里直接用自然语言提问,它会自动选工具:

你:app.log 里最近有哪些 ERROR?给我总结下原因。

Claude(自动调用 search_logs filename=app.log level=ERROR):
找到 3 条 ERROR:
- 09:10 数据库连接超时(host=db-2),重试两次失败后 09:12 恢复
- 10:02 订单 ord_55231 支付回调签名校验失败
建议优先排查支付回调的密钥配置。
你:统计下 app.log 各级别分布。

Claude(自动调用 log_stats filename=app.log):
INFO 2 条,WARN 2 条,ERROR 4 条,整体 ERROR 占比较高,建议关注。
你:把 app.log 整份读出来给我看看格式。

Claude(自动通过 logs://app.log 资源读取):
[返回全文并说明日志格式]

注意第三条用的是资源而非工具——AI 自己判断「读全文」是名词性操作,选了资源原语。这就是双原语设计的妙处。


八、整体架构与开发流程

flowchart LR
    U[用户自然语言] --> CC[Claude Code\nMCP 客户端]
    CC <-->|stdio / JSON-RPC| FS[FastMCP 服务器\nLogScope]
    FS -->|读| L1[(logs/app.log)]
    FS -->|读| L2[(logs/nginx.log)]
    FS -->|读| L3[(logs/...)]
    FS -->|ctx.info 进度| CC

    style FS fill:#fde68a,stroke:#b45309,stroke-width:2px
    style CC fill:#bfdbfe,stroke:#1d4ed8
flowchart TD
    A[1. uv add fastmcp] --> B[2. 写工具/资源\[email protected] / @mcp.resource]
    B --> C[3. fastmcp dev 调试\nMCP Inspector 验证]
    C --> D{工具行为正确?}
    D -- 否 --> B
    D -- 是 --> E[4. claude mcp add\n或 .mcp.json]
    E --> F[5. /mcp 确认 connected]
    F --> G[6. 自然语言对话\n驱动日志分析]
    G --> H{要加新能力?}
    H -- 是 --> B
    H -- 否 --> DONE[完成]

九、进阶优化方向

掌握基础后,LogScope 还能这样扩展:


延伸阅读

训练营适用性评估

参考来源

  1. FastMCP GitHub (PrefectHQ/fastmcp) — 官方仓库与 README — FastMCP 三大支柱、安装方式、生态地位与版本说明
  2. FastMCP 官方文档:Quickstart — 创建服务器、@mcp.tool 装饰器、stdio/HTTP 传输运行方式
  3. FastMCP 官方文档:Tools — 工具自动 schema 生成、docstring 解析、Context 与错误处理细节
  4. Claude Code 文档:Connect Claude Code to tools via MCP — 四种传输方式、三大安装作用域、.mcp.json 配置与 MCP 资源用法
  5. MCP Inspector — Model Context Protocol — 官方交互式调试工具的使用与导出配置功能

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入