从 0 开发一个 MCP Server
手把手教你构建 AI 工具调用能力

若你的 Claude / GPT / Cursor 只能聊天、无法查数据库、读文件或调 API——你缺的不是更长的 Prompt,而是可复用的工具层。本文面向后端与全栈开发者,从 Hello World 到 ChromaDB 知识库 MCP Server,覆盖 Tools / Resources / Prompts 三大能力、stdio 与 HTTP+SSE 双传输、调试测试与 Docker 生产部署;读完后你将拥有一套可在 Cursor 里直接调用的自定义工具,并理解如何把 Server 放到远程 Mac 7×24 常驻(协议背景可先读 MCP 协议解读篇)。

01

为什么 AI 需要 MCP Server:没有工具的 LLM 只是「会说话的大脑」

🧠 大模型训练数据有截止日期,无法访问你的 CRM、Git 仓库或内网 API。2024 年前,给 Claude 写一套 Function Calling、给 GPT 写 Plugins、给 Cursor 写另一套格式——换模型 = 推倒重来。MCP Server 让你把「工具能力」封装成独立进程,写一次、Claude Desktop / Cursor / Gemini 都能用

典型场景:Claude Desktop 里让 AI 查 Postgres 销售数据;Cursor 里让 Agent 读项目文档并改代码;GPT 通过 HTTP MCP 调用你公司的工单系统——底层都是同一个 Server。

本文价值:不是讲概念,而是带你从 say_hello 走到带向量检索的生产级知识库 Server。目标读者:有 Python 或 TypeScript 基础、希望在 IDE 或 Desktop 里扩展 AI 能力的开发者。

六大痛点:为什么「直接写 API 包装」不够

  1. 01

    模型供应商锁定:OpenAI Function Calling 与 Claude Tool Use 格式不同,每换一家就要重写适配层。

  2. 02

    工具无法被发现:REST API 靠静态文档,AI 无法在运行时调用 tools/list 自主发现能力清单。

  3. 03

    IDE 各自为政:Cursor、VS Code 扩展、JetBrains 插件的工具定义无法复用,维护 N×M 套集成。

  4. 04

    上下文与数据割裂:LLM 无法稳定读取配置文件、用户偏好或实时日志——需要标准化的 Resources 只读通道。

  5. 05

    Prompt 模板散落:Code Review、Incident 报告等复用模板没有统一注册机制,团队各自复制粘贴。

  6. 06

    本地 vs 远程部署混乱:stdio 子进程适合开发,生产 HTTP 网关、鉴权与监控缺少统一范式(参见 stdio 子进程治理篇)。

「给 AI 接上 MCP Server,就像给程序员装上 IDE 插件——能力边界瞬间从『聊天』扩展到『操作真实世界』。」

02

MCP 是什么:从 Function Calling 到开放协议

演进路径:Function Calling(2023)ChatGPT PluginsMCP(2024.11,Anthropic 开源)。Anthropic 设计 MCP 为 AI 与外部世界的「USB-C」:Host(Cursor/Claude Desktop)内嵌 MCP Client,与你的 MCP Server 建立 1:1 会话。

架构:Client / Server 与三大能力

  • Tools(工具):AI 可调用、有副作用的操作——查库、写文件、发 HTTP 请求。
  • Resources(资源):只读上下文——配置、文档片段、用户 profile,通过 URI 寻址。
  • Prompts(提示模板):预置多轮对话骨架,如 code review、incident postmortem。

底层通信基于 JSON-RPC 2.0initializetools/list / tools/callresources/read。传输层两种生命周期:

  • stdio:Host 以子进程启动 Server,stdin/stdout 交换 JSON-RPC,进程退出即会话结束。
  • HTTP + SSE:Client 连远程 URL,Server 推送事件流,适合团队共享与水平扩展。

完整规范见官方站点 modelcontextprotocol.io

MCP vs OpenAI FC vs LangChain 工具

维度MCPOpenAI Function CallingLangChain Tools
开放性跨厂商开放协议,AAIF 治理绑定 OpenAI API框架内抽象,非传输标准
发现机制运行时 tools/list请求内嵌 functions 数组代码注册,无标准发现
只读数据Resources + URI scheme无一等公民Retriever 概念,非协议级
Prompt 模板Prompts 标准接口PromptTemplate 类
传输stdio / HTTP+SSE / Streamable HTTPHTTPS API 一体取决于 Agent 运行时
复用性同一 Server 服务 Cursor + Claude + Gemini仅 OpenAI 生态跨框架需重写绑定
03

开发环境搭建:Python FastMCP vs TypeScript SDK

两条主流路线:Python mcp + FastMCP(数据/脚本友好)与 TypeScript @modelcontextprotocol/sdk(Web/API 集成、类型安全)。SDK 源码:python-sdktypescript-sdk

bash
# Python 路线
python -m venv .venv && source .venv/bin/activate
pip install "mcp[cli]" httpx pydantic

# TypeScript 路线
npm init -y && npm install @modelcontextprotocol/sdk zod
npm install -D typescript tsx @types/node

推荐项目结构

tree
my-mcp-server/
├── pyproject.toml          # 或 package.json
├── src/
│   ├── server.py           # FastMCP 入口
│   ├── tools/              # 各工具模块
│   ├── resources/          # Resource 提供者
│   └── prompts/            # Prompt 模板
├── tests/
│   └── test_tools.py       # pytest + ClientSession
├── Dockerfile
└── README.md

六步环境就绪清单

  1. 01

    选语言栈:数据/ML 团队优先 Python;Node 全栈选 TypeScript。

  2. 02

    创建虚拟环境并锁定依赖:pip freezepackage-lock.json,避免 schema 漂移。

  3. 03

    安装 MCP Inspector:npx @modelcontextprotocol/inspector,可视化调试 JSON-RPC。

  4. 04

    配置 Claude Desktop:编辑 ~/Library/Application Support/Claude/claude_desktop_config.json,添加 Server command/args。

  5. 05

    配置 Cursor:Settings → MCP → Add Server,stdio 填 python -m src.server 或绝对路径。

  6. 06

    验证 Inspector 连通:启动 Server → Inspector 连接 → 确认 tools/list 返回非空。

json
// Cursor / Claude Desktop MCP 配置示例
{
  "mcpServers": {
    "my-tools": {
      "command": "python",
      "args": ["-m", "src.server"],
      "env": { "API_KEY": "your-key" }
    }
  }
}
04

Hello World:30 秒跑通第一个 MCP Server

用 FastMCP 写一个 say_hello 工具,验证整条链路:代码 → Inspector → Cursor。

python
# src/server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("hello-server")

@mcp.tool()
def say_hello(name: str = "World") -> str:
    """向指定对象打招呼"""
    return f"Hello, {name}! MCP is working 🎉"

if __name__ == "__main__":
    mcp.run()  # 默认 stdio 传输
bash
# 用 Inspector 调试
npx @modelcontextprotocol/inspector python -m src.server

# 或直接 stdio 启动
python -m src.server

在 Cursor 里添加同一 command 后,对 Agent 说「用 say_hello 跟 NodeMini 打招呼」——若返回 JSON 结果,说明 Client ↔ Server 握手成功。🚀

info

提示:FastMCP 从函数 docstring 与类型注解自动生成 JSON Schema,无需手写参数描述。

05

Tools 深度实战:从 Schema 到五个生产级工具

Tool 是 MCP 最核心的能力:AI 通过 tools/call 执行带副作用的操作。每个 Tool 需暴露名称、描述、inputSchema;FastMCP 用 Pydantic 模型增强校验。

python
from pydantic import BaseModel, Field

class SearchInput(BaseModel):
    query: str = Field(..., description="搜索关键词")
    limit: int = Field(10, ge=1, le=100, description="返回条数")

@mcp.tool()
async def search_docs(params: SearchInput) -> str:
    """在文档库中搜索"""
    results = await index.search(params.query, params.limit)
    return json.dumps(results, ensure_ascii=False)

五个常用 Tool 模式

  • calculator:安全 eval 或 ast.literal_eval,禁止 exec
  • file_read / file_write:限制根目录白名单,防止路径遍历。
  • fetch_url(async):用 httpx 异步请求,设 timeout 与域名白名单。
  • db_query:只读 SQL + 参数化查询,禁止 DDL。
  • get_current_time:返回 ISO8601 时区时间,解决 LLM 时间幻觉。
python
import httpx
from datetime import datetime, timezone

@mcp.tool()
async def fetch_url(url: str) -> str:
    """HTTP GET 获取 URL 内容(域名白名单内)"""
    allowed = ("api.github.com", "nodemini.com")
    if not any(url.startswith(f"https://{d}") for d in allowed):
        raise ValueError(f"Domain not allowed: {url}")
    async with httpx.AsyncClient(timeout=10.0) as client:
        resp = await client.get(url)
        resp.raise_for_status()
        return resp.text[:8000]

@mcp.tool()
def get_current_time() -> str:
    """返回当前 UTC 时间"""
    return datetime.now(timezone.utc).isoformat()

错误处理最佳实践

  • 返回结构化错误:raise ValueError("human-readable msg"),Client 会把 message 回传给 LLM。
  • 区分可重试 vs 致命:网络超时标注 retryable;权限拒绝直接 fail。
  • 日志与脱敏:记录 tool 名与耗时,不 log 完整 API Key 或 PII。
  • 幂等设计:写操作带 idempotency_key,避免 Agent 重复调用造成脏数据。
06

Resources:只读上下文与 URI 方案

Tool vs Resource:Tool 有副作用、由 AI 主动调用;Resource 是只读上下文,Host 可在对话前注入或 AI 通过 resources/read 拉取。URI scheme 自定义,如 config://user://file://

python
@mcp.resource("config://app/settings")
def app_settings() -> str:
    """静态应用配置(text/plain)"""
    return open("config/settings.json").read()

@mcp.resource("user://{user_id}/profile")
def user_profile(user_id: str) -> str:
    """动态用户 profile(application/json)"""
    return json.dumps(get_user(user_id))

Resource 内容类型

MIME 类型用途示例
text/plain日志、READMEfile://logs/app.log
application/json配置、API 响应config://env
application/octet-stream二进制(base64)PDF 摘要
text/event-stream实时订阅日志 tail、metrics 流

文件系统 Resource Server 模式:实现 resources/list 扫描目录、resources/read 按 URI 读取、resources/subscribe 监听 watchfiles 变更并推送更新——适合把代码库文档暴露给 Cursor Agent。

07

Prompts:可复用的多轮对话模板

MCP Prompt 是注册在 Server 上的对话骨架,Client 通过 prompts/get 拉取消息列表,可含 user / assistant 角色与参数占位符——团队共享 Code Review、Incident 流程而不必每人维护 Prompt 文件。

python
from mcp.types import PromptMessage, TextContent

@mcp.prompt()
def code_review_prompt(language: str = "python") -> list[PromptMessage]:
    """标准化 Code Review 多轮模板"""
    return [
        PromptMessage(role="user", content=TextContent(
            type="text",
            text=f"你是资深 {language} 工程师。请按安全、性能、可读性三维审查以下 diff。"
        )),
        PromptMessage(role="assistant", content=TextContent(
            type="text",
            text="请粘贴 diff 或指定 PR 编号,我将按 CHECKLIST 输出结构化 review。"
        )),
    ]

多轮模板可嵌套变量({ticket_id}{severity}),Server 端统一版本管理,Client 升级 Server 即全员同步最新 review 标准。

08

HTTP 传输:从 stdio 到 Streamable HTTP 生产部署

维度stdioHTTP + SSE / Streamable HTTP
部署本地子进程,Host 启动独立服务,URL 连接
扩展单机,难水平扩展负载均衡、多副本
鉴权依赖 Host 环境变量Bearer Token / API Key / mTLS
调试Inspector 直连curl + SSE 客户端
适用个人开发、Cursor 本地团队共享、SaaS 集成
python
# Streamable HTTP 模式(FastMCP 2026)
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("prod-server", host="0.0.0.0", port=8080)

# ... 注册 tools ...

if __name__ == "__main__":
    mcp.run(transport="streamable-http")

生产必做:Bearer Token 校验中间件、CORS 白名单(仅允许自家 Host 域名)、rate limit(如 100 req/min/IP)、HTTPS 终止于反向代理。远程网关运维细节见 HTTP 网关治理篇

warning

注意:HTTP MCP 切勿裸奔公网——2026 年仍有大量未授权暴露的 Server,务必加鉴权与 IP 限制。

09

调试与测试:Inspector + pytest 单元测试

MCP Inspector 是官方可视化调试器:连接 stdio 或 URL 后,可手动发 tools/listtools/call,查看 JSON-RPC 往返,比盲猜 Cursor 日志高效十倍。

python
# tests/test_tools.py
import pytest
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

@pytest.mark.asyncio
async def test_say_hello():
    params = StdioServerParameters(command="python", args=["-m", "src.server"])
    async with stdio_client(params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            result = await session.call_tool("say_hello", {"name": "MCP"})
            assert "MCP" in result.content[0].text

常见错误对照表

现象原因修复
Server 启动后立即退出stdout 被 print 污染日志写 stderr;禁止 print 到 stdout
tools/list 为空装饰器未注册或 import 顺序错误确保 @mcp.tool()run() 前执行
Cursor 显示 disconnectedcommand 路径错误或 venv 未激活用绝对路径;config 里写完整 python 路径
JSON-RPC parse error非 JSON 输出混入 stdio关闭 debug banner;库日志级别 WARNING+
10

生产部署:Docker、云平台与可观测性

dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY pyproject.toml .
RUN pip install --no-cache-dir .
COPY src/ src/
EXPOSE 8080
HEALTHCHECK CMD curl -f http://localhost:8080/health || exit 1
CMD ["python", "-m", "src.server", "--transport", "streamable-http"]

部署平台选型:Railway / Render 适合快速验证;AWS ECS / GCP Cloud Run 适合企业合规;VPS + Docker Compose 成本最低但需自管补丁。

可引用生产参数(EEAT)

  • 协议版本:MCP 2025-03-26 spec 起支持 Streamable HTTP;Client 握手时 initialize 协商 protocolVersion,Server 应声明兼容范围。
  • 资源基线:轻量 Tool Server ~128MB RAM;带 ChromaDB 向量索引建议 ≥2GB RAM、SSD 持久卷。
  • 可观测性栈:结构化 JSON 日志 → Loki/CloudWatch;Prometheus 暴露 /metrics(tool 调用 QPS、P99 延迟);Sentry 捕获未处理异常;/health 供 K8s liveness。
11

实战项目:ChromaDB 知识库 MCP Server

📚 把内部 Wiki / Markdown 文档向量化,暴露 index_documentsearch_knowledgewrite_note 三个 Tool,让 Cursor Agent 能「查公司知识库再写代码」。

需求:支持增量索引(watchfiles 监听 docs/)、语义搜索 Top-K、可选写入 scratchpad。

python
import chromadb
from chromadb.utils.embedding_functions import SentenceTransformerEmbeddingFunction

client = chromadb.PersistentClient(path="./data/chroma")
collection = client.get_or_create_collection(
    "wiki", embedding_function=SentenceTransformerEmbeddingFunction()
)

@mcp.tool()
def search_knowledge(query: str, top_k: int = 5) -> str:
    """语义搜索内部知识库"""
    hits = collection.query(query_texts=[query], n_results=top_k)
    return json.dumps(hits["documents"][0], ensure_ascii=False)

@mcp.tool()
def index_document(path: str) -> str:
    """索引单个 Markdown 文件"""
    text = open(path).read()
    collection.upsert(ids=[path], documents=[text], metadatas=[{"path": path}])
    return f"Indexed: {path}"

Cursor 演示查询:「搜索知识库里关于 MCP HTTP 部署的文档,然后总结三步上线清单」——Agent 会先 search_knowledge,再基于检索结果回答。向量库也可换 Qdrant(远程 gRPC)以支持更大规模。

12

生态、2026 趋势与学习路径

官方与社区已有大量开箱即用 Server,不必重复造轮子:

  • mcp-server-filesystem — 安全沙箱文件读写
  • mcp-server-github — PR/Issue API
  • brave-search / postgres / slack — 搜索、SQL、团队协作

2026 趋势:MCP Marketplace 涌现、OAuth 2.1 工具授权进入 spec 路线图、Streamable HTTP 逐步取代纯 SSE。学习路径 checklist:① 读 spec → ② Hello World → ③ 写 3 个 Tool → ④ 加 Resource → ⑤ pytest → ⑥ Docker 上线 → ⑦ 接入 Cursor。

总结

say_hello 到 ChromaDB 知识库,你已掌握 MCP Server 全栈能力:Tools 执行、Resources 上下文、Prompts 模板、双传输、测试与生产运维。下一步可 fork 社区 Server 或把公司 API 封装为团队标准工具层。

纯本地 stdio 适合个人实验,但多 Server 并行、向量索引常驻、HTTP 长连接会让 16GB 笔电频繁 swap;廉价 Linux VPS 又难以跑 macOS 专属工具链。自建 HTTP 网关若缺少 session 亲和与鉴权,还容易出现连接泄漏与未授权暴露——长期稳定性往往不如预期。

对需要把 MCP 作为生产基础设施、同时跑 Cursor Agent 与 iOS/macOS CI 的团队,把 MCP Server 放在可独占的云端 Mac 上 7×24 运行,通常比押宝本地笔电或通用 VM 更可控。NodeMini Mac Mini 云端租赁可作为 MCP + Agent 执行层:换底层 LLM 时 SSH 节点与 Server 配置保持不变。规格见 租赁价格说明,接入见 帮助中心

FAQ

常见问题

Python FastMCP 上手最快,适合数据/脚本类工具;TypeScript SDK 类型安全、与 Node 生态无缝。两者协议完全兼容。若需 7×24 跑多个 Server,可参考 租赁价格说明 选择远程 Mac 配置。

Function Calling 绑定 OpenAI;MCP 是跨 Claude/GPT/Gemini/Cursor 的开放协议,支持 Resources 与 Prompts。背景详见 MCP 协议解读篇

轻量 stdio 可本地运行;多 Server + 向量库 + HTTP 长连接 建议独占远程 Mac。接入步骤见 帮助中心,运维参见 stdio 子进程治理篇