Agent工坊

【Agent工坊】MCP Server 从零开发实战:用 Python 构建你自己的 Agent 工具,30 分钟上线

别再只会用现成的 MCP Server 了——2026 年 AI 创业者的分水岭,是能不能自己写 MCP 工具让 Agent 调用你的业务系统。本文从零带你构建一个完整 MCP Server,含可复制代码、测试方法和生产部署清单。

为什么「用 MCP」和「写 MCP」是两个完全不同的层级

打开任何一个 AI Agent 工具(Claude Code、Hermes Agent、Cursor),你都会看到 MCP 配置选项。大多数人的做法:去 GitHub 搜「awesome-mcp-servers」,找到现成的配置,复制粘贴,完事。

这没问题——直到你需要 Agent 操作你自己的业务系统

这时候你会发现:

  • ❌ 没有现成的 MCP Server 能访问你公司内部的 CRM 数据
  • ❌ 没有人写过你那个小众 SaaS 工具的 MCP 接口
  • ❌ 你需要的「查本月收入」「列出待处理订单」这些操作,只有你自己知道数据库结构

这就是「用 MCP」和「写 MCP」的分水岭。

2026 年 6 月,MCP 生态已经有 200+ 个公开 Server,但真正让 Agent 产生商业价值的,永远是那些连接你私有业务系统的自定义 MCP 工具。

今天这篇文章,我用一个完整案例带你走完全流程:用 Python 写一个「创业公司数据看板」MCP Server,让 Claude Code 能直接回答「这个月收入多少」「哪个渠道转化最好」这类业务问题。


MCP 协议速览:你需要知道的 3 个概念

写 MCP Server 之前,先理解协议核心。MCP 本质上是一个 JSON-RPC 2.0 协议,Server 通过标准输入/输出(stdio)与 Client(你的 Agent)通信。

核心概念 1:Tools(工具)

Tool 是 MCP 最基本的能力单元。每个 Tool 就是 Agent 可以调用的一个函数:

{
  "name": "get_monthly_revenue",
  "description": "获取指定月份的营业收入",
  "inputSchema": {
    "type": "object",
    "properties": {
      "month": {"type": "string", "description": "月份,格式 YYYY-MM"}
    },
    "required": ["month"]
  }
}

当 Agent 决定调用这个工具时,MCP Client 会发送一个 tools/call 请求,Server 返回执行结果。

核心概念 2:Resources(资源)

Resource 是 Agent 可以读取的数据源。和 Tool 的区别:

Tool Resource
触发方式 Agent 主动调用 Agent 订阅/读取
有副作用? 是(可能修改数据) 否(只读)
典型场景 发邮件、创建订单 读取配置文件、查日志
类比 REST API 的 POST/PUT REST API 的 GET

核心概念 3:Prompts(提示模板)

Prompts 是预定义的提示词模板,Agent 可以按需加载。比如你可以定义一个「数据分析师」角色 Prompt,让 Agent 在需要时切换到数据分析模式。


实战:构建「创业公司数据看板」MCP Server

场景设定

你经营一家 SaaS 创业公司,核心数据在 PostgreSQL 数据库里。你希望 Claude Code 能直接回答:

  • 「这个月 MRR 是多少?环比增长多少?」
  • 「上周新增了多少付费用户?」
  • 「哪个渠道的转化率最高?」

这些查询逻辑你都清楚,但没有现成的 MCP Server 能对接你的数据库结构。

第一步:项目初始化

mkdir mcp-dashboard && cd mcp-dashboard
python3 -m venv venv && source venv/bin/activate
pip install mcp psycopg2-binary python-dotenv

创建项目结构:

mcp-dashboard/
├── server.py          # MCP Server 主入口
├── db.py              # 数据库查询逻辑
├── tools/
   ├── revenue.py     # 收入查询工具
   ├── users.py       # 用户指标工具
   └── channels.py    # 渠道分析工具
├── requirements.txt
└── .env               # 数据库连接信息

第二步:编写 MCP Server 主入口

# server.py
"""
创业公司数据看板 MCP Server
让 AI Agent 能直接查询你的业务数据
"""
import asyncio
import os
from mcp.server import Server, NotificationOptions
from mcp.server.models import InitializationCapabilities
from mcp.server.stdio import stdio_server
from dotenv import load_dotenv

# 加载环境变量
load_dotenv()

# 导入工具模块
from tools.revenue import register_revenue_tools
from tools.users import register_user_tools
from tools.channels import register_channel_tools

# 创建 Server 实例
server = Server("startup-dashboard")

@server.list_tools()
async def list_tools():
    """注册所有工具"""
    tools = []
    tools.extend(register_revenue_tools())
    tools.extend(register_user_tools())
    tools.extend(register_channel_tools())
    return tools

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    """路由工具调用到对应的处理函数"""
    # 工具路由表
    handlers = {
        "get_monthly_revenue": handle_revenue,
        "get_user_growth": handle_user_growth,
        "get_channel_conversion": handle_channel_conversion,
        "get_active_subscriptions": handle_active_subscriptions,
    }

    handler = handlers.get(name)
    if not handler:
        raise ValueError(f"未知工具: {name}")

    result = await handler(**arguments)

    # 返回结果(MCP 要求返回 ContentBlock 列表)
    from mcp.types import TextContent
    return [TextContent(type="text", text=result)]

async def main():
    """启动 MCP Server"""
    async with stdio_server() as (read_stream, write_stream):
        await server.run(
            read_stream,
            write_stream,
            InitializationCapabilities(
                sampling={},
                experimental={},
            ),
            notification_options=NotificationOptions(
                tools_changed=False,
                resources_changed=False,
                prompts_changed=False,
            ),
        )

if __name__ == "__main__":
    asyncio.run(main())

第三步:实现收入查询工具

# tools/revenue.py
"""
收入查询工具 — Agent 可以通过此工具查询公司营收数据
"""
from mcp.types import Tool
import json


def register_revenue_tools():
    """注册收入相关工具"""
    return [
        Tool(
            name="get_monthly_revenue",
            description="查询指定月份的营业收入(MRR、一次性收入、总收入),返回JSON格式数据",
            inputSchema={
                "type": "object",
                "properties": {
                    "month": {
                        "type": "string",
                        "description": "查询月份,格式 YYYY-MM,例如 2026-06。不填默认为当前月份"
                    }
                },
                "required": []
            }
        ),
        Tool(
            name="get_revenue_trend",
            description="查询最近N个月的收入趋势,包含环比增长率",
            inputSchema={
                "type": "object",
                "properties": {
                    "months": {
                        "type": "integer",
                        "description": "查询最近几个月的趋势,默认6个月",
                        "default": 6
                    }
                },
                "required": []
            }
        )
    ]


async def handle_revenue(month: str = None):
    """处理收入查询"""
    import psycopg2
    import os
    from datetime import datetime

    if month is None:
        month = datetime.now().strftime("%Y-%m")

    conn = psycopg2.connect(os.getenv("DATABASE_URL"))
    cur = conn.cursor()

    # 查询 MRR 和一次性收入
    cur.execute("""
        SELECT 
            SUM(CASE WHEN plan_type = 'recurring' THEN amount ELSE 0 END) as mrr,
            SUM(CASE WHEN plan_type = 'one_time' THEN amount ELSE 0 END) as one_time,
            COUNT(DISTINCT user_id) as paying_users
        FROM revenue
        WHERE to_char(created_at, 'YYYY-MM') = %s
    """, (month,))

    row = cur.fetchone()
    cur.close()
    conn.close()

    mrr, one_time, users = row
    total = (mrr or 0) + (one_time or 0)

    return json.dumps({
        "month": month,
        "mrr": round(mrr or 0, 2),
        "one_time_revenue": round(one_time or 0, 2),
        "total_revenue": round(total, 2),
        "paying_users": users or 0,
        "arpu": round(total / (users or 1), 2)
    }, ensure_ascii=False, indent=2)


async def handle_revenue_trend(months: int = 6):
    """处理收入趋势查询"""
    import psycopg2
    import os

    conn = psycopg2.connect(os.getenv("DATABASE_URL"))
    cur = conn.cursor()

    cur.execute("""
        SELECT 
            to_char(created_at, 'YYYY-MM') as month,
            SUM(amount) as total_revenue
        FROM revenue
        WHERE created_at >= date_trunc('month', now()) - interval '%s months'
        GROUP BY 1
        ORDER BY 1
    """, (months,))

    rows = cur.fetchall()
    cur.close()
    conn.close()

    # 计算环比增长率
    result = []
    prev = None
    for month, rev in rows:
        growth = None
        if prev and prev > 0:
            growth = round((rev - prev) / prev * 100, 1)
        result.append({
            "month": month,
            "revenue": round(rev, 2),
            "mom_growth_pct": growth
        })
        prev = rev

    return json.dumps(result, ensure_ascii=False, indent=2)

第四步:用户增长工具

# tools/users.py
from mcp.types import Tool
import json

def register_user_tools():
    return [
        Tool(
            name="get_user_growth",
            description="查询指定时间段内的用户增长数据(新增、活跃、流失)",
            inputSchema={
                "type": "object",
                "properties": {
                    "period": {
                        "type": "string",
                        "enum": ["week", "month", "quarter"],
                        "description": "统计周期"
                    }
                },
                "required": ["period"]
            }
        ),
        Tool(
            name="get_active_subscriptions",
            description="查询当前活跃订阅数及分计划统计",
            inputSchema={
                "type": "object",
                "properties": {},
                "required": []
            }
        )
    ]

async def handle_user_growth(period: str):
    """处理用户增长查询"""
    import psycopg2, os

    period_days = {"week": 7, "month": 30, "quarter": 90}
    days = period_days[period]

    conn = psycopg2.connect(os.getenv("DATABASE_URL"))
    cur = conn.cursor()

    cur.execute("""
        SELECT 
            COUNT(*) FILTER (WHERE status = 'active' AND created_at >= now() - interval '%s days') as new_users,
            COUNT(*) FILTER (WHERE status = 'active') as active_users,
            COUNT(*) FILTER (WHERE status = 'churned' AND churned_at >= now() - interval '%s days') as churned_users
        FROM users
    """, (days, days))

    row = cur.fetchone()
    cur.close()
    conn.close()

    new, active, churned = row
    churn_rate = round(churned / (active + churned) * 100, 1) if (active + churned) > 0 else 0

    return json.dumps({
        "period": f"最近{days}天",
        "new_users": new,
        "active_users": active,
        "churned_users": churned,
        "churn_rate_pct": churn_rate
    }, ensure_ascii=False, indent=2)

连接到 Claude Code:一键配置

写完 Server,接下来让 Claude Code 能调用它。在项目根目录创建 .claude/mcp.json

{
  "mcpServers": {
    "startup-dashboard": {
      "command": "python3",
      "args": ["server.py"],
      "cwd": "/path/to/mcp-dashboard",
      "env": {
        "DATABASE_URL": "postgresql://user:pass@localhost:5432/startup_db"
      }
    }
  }
}

Hermes Agent 用户,配置方式类似,在 ~/.hermes/config.yaml 中:

mcp_servers:
  startup-dashboard:
    command: python3
    args: ["server.py"]
    cwd: /path/to/mcp-dashboard

配置完成后,重启 Claude Code,在对话中试试:

> 查一下这个月的收入情况

Claude Code 会自动调用 get_monthly_revenue,返回:

📊 2026年6月收入概览:
• MRR: ¥48,500
• 一次性收入: ¥12,300
• 总收入: ¥60,800
• 付费用户: 127人
• ARPU: ¥478.74

本地测试:不接 Agent 也能验证

在接入 Claude Code 之前,可以用 MCP 官方提供的 Inspector 工具本地测试:

# 安装 MCP Inspector
npx @modelcontextprotocol/inspector python3 server.py

这会在浏览器打开一个调试界面,你可以:
- 查看所有注册的 Tool 列表
- 手动调用每个 Tool 并查看返回结果
- 检查 JSON-RPC 通信日志

或者更简单的方式——写一个测试脚本

# test_server.py
"""不依赖 Agent 的 MCP Server 单元测试"""
import asyncio
import json

async def test_tools():
    # 直接导入工具函数测试
    from tools.revenue import handle_revenue
    from tools.users import handle_user_growth

    print("=== 测试收入查询 ===")
    result = await handle_revenue(month="2026-06")
    print(json.loads(result))

    print("\n=== 测试用户增长 ===")
    result = await handle_user_growth(period="month")
    print(json.loads(result))

asyncio.run(test_tools())

生产部署清单:从开发到上线的 7 个检查点

写完代码只是第一步。以下是让 MCP Server 在生产环境稳定运行的检查清单:

✅ 1. 超时控制

# 在 call_tool 中添加超时
import asyncio
result = await asyncio.wait_for(handler(**arguments), timeout=30.0)

✅ 2. 错误处理

# 统一错误返回格式
try:
    result = await handler(**arguments)
except Exception as e:
    return [TextContent(type="text", text=json.dumps({
        "error": str(e),
        "type": type(e).__name__
    }))]

✅ 3. 连接池复用

# db.py — 不要每次查询都新建连接
from psycopg2.pool import SimpleConnectionPool

_pool = None

def get_pool():
    global _pool
    if _pool is None:
        _pool = SimpleConnectionPool(1, 5, os.getenv("DATABASE_URL"))
    return _pool

✅ 4. 敏感数据脱敏

# 查询结果自动脱敏邮箱/手机号
import re

def mask_pii(data: dict) -> dict:
    """脱敏个人身份信息"""
    text = json.dumps(data)
    text = re.sub(r'[\w\.-]+@[\w\.-]+', '***@***.***', text)
    text = re.sub(r'1[3-9]\d{9}', '1**********', text)
    return json.loads(text)

✅ 5. 日志记录

import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("mcp-dashboard")

# 在 call_tool 中记录
logger.info(f"Tool called: {name}, args: {arguments}")

✅ 6. 工具权限分级

# 在 MCP Server 配置中分组
tools:
  read_only:
    - get_monthly_revenue
    - get_user_growth
    - get_channel_conversion
  read_write:
    - update_subscription_status  # 生产环境谨慎开放

✅ 7. Docker 化部署

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python3", "server.py"]

总结:2026 年 Agent 创业者的核心竞争力

回顾这一年 AI Agent 工具的发展,有一个趋势越来越清晰:

会用 AI Agent 是基本功,能让 AI Agent 调用你的业务系统才是护城河。

MCP Server 开发不是「高级话题」——它是每个 AI 创业者都应该掌握的基础技能。因为:

  1. 现成的 MCP Server 解决通用问题(GitHub、Slack、数据库),但只有你自己写的 MCP Server 能解决你的特殊业务逻辑
  2. Agent 的价值 = 它能接触的数据 × 它能执行的操作,自定义 MCP Server 直接扩大这个乘积
  3. 每次重复性人工操作被 MCP Tool 替代,你就离「一人公司」更近一步

今天这篇文章的完整代码可以在你的项目中直接使用——替换数据库查询逻辑为你的业务 SQL,10 分钟内你就能让 Claude Code 或 Hermes Agent 变成你的业务数据分析师。

下一步行动
1. 列出你每天要手动查询的 3 个业务指标
2. 把查询逻辑写成 MCP Tool
3. 接入 Claude Code / Hermes Agent,以后直接对话获取答案


Agent工坊 #MCP开发 #ClaudeCode #HermesAgent #一人公司 #AI创业