# 4.5 从零开发自定义 MCP Server

虽然社区提供了很多现成的 Server，但在企业应用中，往往需要让 Claude 连接 **内部 API**、**私有数据库** 或者 **自研系统**。

本节将介绍如何用 Python 和 TypeScript 两种语言，快速开发一个自定义的 MCP Server。

## 4.5.1 极速入门：FastMCP

如果是 Python 开发者，Anthropic 官方提供了一个名为 `fastmcp`（包含在 `mcp` 库中）的高级封装，它可以像写 FastAPI 一样简单地写 MCP Server。

### 环境准备

```bash
# 安装 mcp 库
pip install mcp
```

## 编写代码：`weather_server.py`

下面做一个简单的“虚拟天气服务”。

```python
from mcp.server.fastmcp import FastMCP

# 1. 创建 Server 实例
mcp = FastMCP("My Weather Server")

# 2. 定义工具 (使用装饰器)
# docstring 会自动变成工具的 description
# 类型注解会自动变成 input_schema
@mcp.tool()
def get_weather(city: str) -> str:
    """获取指定城市的当前天气情况。"""
    # 这里可以填写真实的 API 调用逻辑
    # 例如: requests.get(f"https://api.weather.com/{city}")
    return f"{city} 今天晴朗，气温 25度。"

# 3. 定义资源
# 让 Claude 可以直接读取这种格式的数据: weather://beijing
@mcp.resource("weather://{city}")
def get_weather_resource(city: str) -> str:
    """以资源形式获取天气数据"""
    return f"Latest weather report for {city}: Sunny, 25C, Humidity 40%."

if __name__ == "__main__":
    # 启动 Server
    mcp.run()
```

## 本地测试

可以使用 MCP Inspector 直接运行它：

```bash
npx @modelcontextprotocol/inspector python weather_server.py
```

这会弹出一个网页，可以在上面看到 `get_weather` 工具，点击运行，还能看到 JSON 交互日志。

## 4.5.2 企业级开发：TypeScript SDK

如果是 Node.js 开发者，可以使用官方的 TypeScript SDK。这通常用于构建生产环境级别、基于 Docker 的 Server。

### 初始化项目

```bash
mkdir my-mcp-server
cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript tsx @types/node
```

### 编写代码：`index.ts`

```typescript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";

// 1. 初始化 Server
const server = new Server(
  {
    name: "my-ts-server",
    version: "1.0.0",
  },
  {
    capabilities: {
      tools: {}, // 声明支持 Tools 能力
    },
  }
);

// 2. 实现 Tools 列表
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: "calculate_sum",
        description: "计算两个数字的和",
        inputSchema: {
          type: "object",
          properties: {
            a: { type: "number" },
            b: { type: "number" },
          },
          required: ["a", "b"],
        },
      },
    ],
  };
});

// 3. 实现工具调用逻辑
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === "calculate_sum") {
    const args = request.params.arguments as { a: number; b: number };
    const sum = args.a + args.b;

    return {
      content: [
        {
          type: "text",
          text: `Result is ${sum}`,
        },
      ],
    };
  }
  throw new Error("Tool not found");
});

// 4. 连接 Transport
const transport = new StdioServerTransport();
await server.connect(transport);
```

开发时可直接运行：

```bash
npx tsx index.ts
```

## 4.5.3 设计原则与最佳实践

开发 MCP Server 时，请遵循以下原则：

### 保持原子性

不要做一个名为 `do_everything` 的工具。

* *Bad*: `manage_user(action="create", ...)`
* *Good*: `create_user(...)`, `delete_user(...)`, `update_user(...)` 工具越小，模型越容易组合使用。

### 丰富的描述

`docstring` 或 `description` 字段非常重要。 告诉 Claude：

* 这个工具是干嘛的？
* 参数的单位是什么？（秒？毫秒？）
* 返回值的格式是什么？

### 错误处理

如果 API 调用失败，**不要抛出异常让进程崩溃**。 应该捕获异常，并返回一个包含错误信息的文本结果，或者使用 `isError: true` 标志（如果 SDK 支持）。这样 Claude 可以读取错误信息，并尝试自我修正（比如换个参数重试）。

## 4.5.4 部署与分发

当写好 Server 后，如何分享给团队？

* **NPM 发布**: 将 TypeScript Server 发布为 npm 包。
  * 配置 command: `npx -y my-mcp-server`
* **Docker 部署**: 封装为 Docker 镜像，通过 Streamable HTTP 暴露远程 MCP 端点。
  * 适用于云端部署，供远程 Client 连接；如果必须兼容旧客户端，可同时保留旧 HTTP+SSE 传输。
* **Git 仓库**: 直接分享源码。
  * 配置 command: `python /path/to/repo/main.py`

***

Claude 现在已经连接上了数据。接下来，将介绍更强大的功能——让 Claude 自己看屏幕，像人一样操作电脑。

➡️ [第五章：Computer Use 计算机操控](/claude_guide/di-er-bu-fen-gong-ju-pian/05_computer_use.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yeasy.gitbook.io/claude_guide/di-er-bu-fen-gong-ju-pian/04_mcp/4.5_custom.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
