Agent工坊

【Agent工坊】从零构建 MCP Server:让 AI Agent 学会调用你的 API

MCP(Model Context Protocol)是2026年AI Agent生态最重要的"连接器"标准——掌握了它,你就能让任何AI Agent直接读写你的数据库、调你的API、操控你的业务系统。本文手把手带你从零搭建一个完整的MCP Server。

MCP架构图▲ MCP架构图

为什么你需要一个MCP Server

2026年的AI Agent已经不只是"聊天机器人"了。Claude Code、Cursor、OpenClaw、Hermes Agent——这些Agent可以执行复杂任务,但它们的"手"很短:默认情况下,Agent只能读本地文件和运行shell命令。

MCP改变了这个局面。它的核心思想很朴素:给AI Agent一个标准化的接口,让它能像调用本地函数一样调用你的后端服务

按照Anthropic在2024年底推出的MCP规范,一个MCP Server只需要实现3种能力:

  • Tools:Agent可以调用的函数(如"查询用户订单""发送邮件")
  • Resources:Agent可以读取的数据(如"最近7天的销售报表")
  • Prompts:预定义的提示模板(如"帮我分析这份数据")

今天我们就来搭建一个实用的MCP Server——让AI Agent直接查询和操作你的业务数据库。

准备工作

你需要以下环境(截至2026年6月实测可用):

# Python 3.10+

python3 --version

# 安装MCP SDK

pip install mcp>=1.0.0

# 安装数据库驱动(以SQLite为例,零配置上手)

# SQLite是Python内置的,无需额外安装

验证安装:

python3 -c "from mcp.server import Server; print('MCP SDK OK')"

# 输出: MCP SDK OK

核心架构

一个MCP Server的架构非常简单:

AI Agent (Claude Code / Cursor / Hermes)

    │

    │ JSON-RPC over stdio

    │

    ▼

MCP Server (你的Python进程)

    │

    ├─── Tool: query_orders(user_id)

    ├─── Tool: update_order_status(order_id, status)

    ├─── Resource: sales_report://last_7_days

    └─── 数据库连接 (SQLite / PostgreSQL / MySQL)

关键点:MCP使用标准输入输出(stdio)通信,不需要HTTP端口、不需要Docker、不需要任何网络配置。这就是它极简但强大的原因。

Step 1:创建数据库和基础数据

# setup_db.py — 初始化一个模拟电商数据库

import sqlite3

conn = sqlite3.connect('ecommerce.db')

c = conn.cursor()

# 创建订单表

c.execute('''

CREATE TABLE IF NOT EXISTS orders (

    id INTEGER PRIMARY KEY,

    user_id TEXT NOT NULL,

    product TEXT NOT NULL,

    amount REAL NOT NULL,

    status TEXT DEFAULT 'pending',

    created_at TEXT DEFAULT (datetime('now'))

)

''')

# 插入测试数据

test_orders = [

    ('u001', 'AI Agent托管服务 月度版', 299.00, 'paid'),

    ('u001', 'MCP插件开发套件', 99.00, 'pending'),

    ('u002', '一人公司财务SOP模板', 49.00, 'paid'),

    ('u002', '公众号自动发布工具', 199.00, 'delivered'),

    ('u003', 'AI写作提示词库 Pro版', 79.00, 'paid'),

]

c.executemany(

    'INSERT INTO orders (user_id, product, amount, status) VALUES (?,?,?,?)',

    test_orders

)

conn.commit()

conn.close()

print("✅ 数据库初始化完成: ecommerce.db")

print(" 5条测试订单已插入")

运行它:

python3 setup_db.py

# 输出:

# ✅ 数据库初始化完成: ecommerce.db

# 5条测试订单已插入

Step 2:编写MCP Server核心代码

这是整个教程的核心——一个完整的MCP Server实现:

# mcp_server.py — 完整可运行的MCP Server

import sqlite3

import json

from mcp.server import Server

from mcp.server.stdio import stdio_server

from mcp.types import Tool, TextContent

# 创建MCP Server实例

server = Server("ecommerce-server")

# ─── 数据库连接 ───

def get_db():

    conn = sqlite3.connect('ecommerce.db')

    conn.row_factory = sqlite3.Row # 让结果可以按列名访问

    return conn

# ─── 工具1:查询用户订单 ───

@server.tool()

async def query_orders(user_id: str) -> list[TextContent]:

    """查询指定用户的所有订单

    Args:

        user_id: 用户ID,如 u001

    """

    conn = get_db()

    c = conn.cursor()

    c.execute(

        'SELECT id, product, amount, status, created_at FROM orders WHERE user_id = ?',

        (user_id,)

    )

    rows = c.fetchall()

    conn.close()

    if not rows:

        return [TextContent(type="text", text=f"用户 {user_id} 暂无订单记录")]

    result_lines = [f"用户 {user_id} 的订单列表:"]

    for r in rows:

        result_lines.append(

            f" #{r['id']} | {r['product']} | ¥{r['amount']} | {r['status']} | {r['created_at']}"

        )

    return [TextContent(type="text", text="\n".join(result_lines))]

# ─── 工具2:更新订单状态 ───

@server.tool()

async def update_order_status(order_id: int, new_status: str) -> list[TextContent]:

    """更新订单状态

    Args:

        order_id: 订单ID

        new_status: 新状态 (paid/pending/delivered/cancelled)

    """

    valid_statuses = ['paid', 'pending', 'delivered', 'cancelled']

    if new_status not in valid_statuses:

        return [TextContent(

            type="text",

            text=f"❌ 无效状态 '{new_status}'。有效值: {', '.join(valid_statuses)}"

        )]

    conn = get_db()

    c = conn.cursor()

    c.execute('SELECT * FROM orders WHERE id = ?', (order_id,))

    order = c.fetchone()

    if not order:

        conn.close()

        return [TextContent(type="text", text=f"❌ 订单 #{order_id} 不存在")]

    c.execute('UPDATE orders SET status = ? WHERE id = ?', (new_status, order_id))

    conn.commit()

    conn.close()

    return [TextContent(

        type="text",

        text=f"✅ 订单 #{order_id} 状态已更新: {order['status']} → {new_status}"

    )]

# ─── 启动服务器 ───

async def main():

    async with stdio_server() as (read_stream, write_stream):

        await server.run(read_stream, write_stream)

if __name__ == "__main__":

    import asyncio

    asyncio.run(main())

踩坑提醒 #1@server.tool() 装饰器的函数签名中的参数必须有类型注解(如 user_id: str)。MCP SDK 靠类型注解来生成 tool schema,不加注解Agent无法正确传参。

MCP Server核心代码结构▲ MCP Server核心代码结构

Step 3:配置Claude Code使用你的MCP Server

在Claude Code中,编辑配置文件 ~/.claude/claude_desktop_config.json

{

  "mcpServers": {

    "ecommerce": {

      "command": "python3",

      "args": ["/path/to/mcp_server.py"]

    }

  }

}

踩坑提醒 #2:路径必须用绝对路径!相对路径在Claude Code的沙箱环境下会找不到文件。用 pwd 获取当前目录完整路径。

重启Claude Code后,你就能在对话中直接使用这些工具了。

Step 4:实测效果

在Claude Code中输入:

"帮我查一下用户u001的所有订单"

Agent会自动:

  1. 发现 query_orders 工具可用
  2. 提取参数 user_id="u001"
  3. 调用你的MCP Server
  4. 返回格式化结果

实际输出:

用户 u001 的订单列表:

  #1 | AI Agent托管服务 月度版 | ¥299.00 | paid | 2026-06-12 01:00:00

  #2 | MCP插件开发套件 | ¥99.00 | pending | 2026-06-12 01:00:00

再试一个写操作:

"把订单#2的状态改为paid"

Agent会调用 update_order_status(order_id=2, new_status="paid")

✅ 订单 #2 状态已更新: pending → paid

Step 5:扩展到真实业务场景

上面的例子是SQLite,但你可以轻松替换为任何后端:

# 扩展1:连接生产数据库

import psycopg2 # PostgreSQL

conn = psycopg2.connect("postgresql://user:pass@host/dbname")

# 扩展2:调用外部API

@server.tool()

async def call_external_api(endpoint: str, payload: str) -> list[TextContent]:

    """调用任意外部API(替换endpoint为你的实际API地址)"""

    import requests, json

    resp = requests.post(

        endpoint, # 你的实际API URL

        json=json.loads(payload)

    )

    return [TextContent(type="text", text=f"API响应: {resp.json()}")]

# 扩展3:读取本地文件作为Resource

@server.resource("report://sales/{period}")

async def get_sales_report(period: str) -> str:

    """获取指定时间段的销售报表"""

    with open(f"reports/sales_{period}.json") as f:

        return f.read()

踩坑与排障

坑1:stdio通信被截断

症状:Agent调用工具后无响应,或返回"Connection closed"。

原因:你在MCP Server代码中用了 print() 调试输出。print() 的内容会污染stdio通道,导致JSON-RPC消息被破坏。

修复:用 logging 模块输出到文件,不要用 print()

import logging

logging.basicConfig(filename='/tmp/mcp_server.log', level=logging.DEBUG)

logging.debug("工具调用: query_orders, user_id=u001") # ✅ 正确

# print("debug info") # ❌ 这个会破坏MCP通信!

传统开发 vs MCP开发对比▲ 传统开发 vs MCP开发对比

坑2:MCP Server启动失败

症状:Claude Code配置里加了MCP Server但不显示工具。

排查

# 手动测试MCP Server是否能正常启动

echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | python3 mcp_server.py

如果报错 No module named 'mcp',检查 pip install mcp 是否在正确的Python环境。

坑3:数据库并发写入冲突

症状:多个Agent同时调用工具时报 database is locked

修复:SQLite默认不支持高并发写入。生产环境换PostgreSQL,或在SQLite连接上设置超时:

conn = sqlite3.connect('ecommerce.db', timeout=10) # 等10秒

进阶:MCP + Claude Skills 组合拳

掌握了MCP Server后,下一步就是把工具封装成Claude Skills——让非技术人员也能一键使用你构建的能力。

~/.claude/skills/ecommerce.md 中:

# Ecommerce Agent Skill

## 触发条件

用户提到"订单""发货""退款"等关键词时自动激活。

## 可用工具

- query_orders: 查订单

- update_order_status: 改状态

## 工作流程

1. 先查订单确认信息

2. 再执行用户指定的操作

3. 最后汇报结果

这样你就把一个纯技术工具,变成了业务团队可以自然语言交互的AI助手

常见问题

Q: MCP Server必须用Python写吗? A: 不。MCP有TypeScript、Python、Java、Go的官方SDK。甚至可以用任何语言——只要实现JSON-RPC over stdio即可。TypeScript是目前文档最全的。

Q: 一个MCP Server能被多个Agent同时使用吗? A: 不能(stdio模式)。每个MCP Server进程只服务一个Agent连接。如果需要多Agent共享,可以部署为HTTP+SSE模式(MCP 2025年新增的传输方式)。

Q: 工具的参数支持复杂类型吗(如数组、嵌套对象)? A: 支持。MCP使用JSON Schema定义参数,所以你可以在函数签名里用 items: list[dict] 这样的类型,Agent会自动理解。

Q: Agent会"滥用"工具吗?比如误删数据? A: 这是个真实风险。建议:① 工具加上确认逻辑(如"确定要删除订单#5吗?");② 写操作加日志审计;③ 敏感操作限制为只读。MCP规范本身不提供权限控制,安全需要在Server层实现。

总结

30分钟内,你完成了一个从零到一的MCP Server:

  1. 创建了数据库和测试数据
  2. 实现了2个工具(查询和更新)
  3. 配置Claude Code连接
  4. 验证了Agent可以自然语言操控数据库

这个能力的商业价值很直接:你不再需要为每个业务场景开发完整的Web后台。建好MCP Server,非技术同事通过AI Agent直接操作业务数据——查询订单、更新状态、生成报表,全部自然语言完成。

下一步:尝试把你的真实业务API封装成MCP Tool,看看Agent能替你省下多少手动操作的时间。


#AI创业 #Agent工坊 #MCP协议 #AI工具 #一人公司

本文由AI辅助创作,经人工审核编辑发布