如果你一直在關注 2025 年 AI 工具領域,你可能已經注意到模型上下文協議 (MCP) 已經從「Anthropic 的有趣規範」演變成「每個認真的 SaaS 產品都需要支持的東西」。這是有充分理由的。MCP 為 AI 代理 -- Claude、基於 GPT 的助手、自訂代理 -- 提供了一個標準化的方式來發現和呼叫你的 API。可以將其視為智能體時代的 OpenAPI,但內置了實時雙向通訊。

我在過去幾個月為多個 SaaS 產品構建了 MCP 伺服器,我想分享什麼真正有效、文檔沒有告訴你的內容,以及重要的架構決策。這不是對規範的重新陳述。這是我開始時希望擁有的指南。

目錄

什麼是模型上下文協議 (MCP)?

MCP 是由 Anthropic 最初創建的開放協議,定義了 AI 模型如何與外部工具和數據源進行通訊。在 2024 年底發布,並在 2025 年初達到 v1.0 穩定版,現在受到 Claude Desktop、Cursor、Windsurf、OpenAI Agents SDK 和數十個其他 AI 客戶端的支持。

核心概念:不是每個 AI 集成都是具有專有格式的自訂插件,而是 MCP 提供了一個單一協議,任何兼容的客戶端都可以用它來發現你的服務提供什麼並與之交互。

以下是 MCP 定義的內容:

  • 工具 -- AI 可以呼叫的函數(如 create_ticketsearch_usersgenerate_report
  • 資源 -- AI 可以讀取的數據(如文檔、資料庫記錄、配置文件)
  • 提示 -- 你的伺服器可以公開的可重用提示範本
  • 採樣 -- 你的伺服器可以從客戶端請求 LLM 完成的能力

傳輸層使用 JSON-RPC 2.0,可通過 stdio(用於本地伺服器)或 HTTP 與服務器發送事件 (SSE)(用於遠程伺服器)。在 2025-03 規範修訂版中引入的更新的 Streamable HTTP 傳輸是你在任何生產 SaaS 部署中想要的。

為什麼你的 SaaS 產品需要 MCP 伺服器

讓我直言:如果你的 SaaS 產品有 API,你現在應該構建 MCP 伺服器。原因如下。

AI 代理正在成為主要的 API 消費者。 在 2025 年第一季度,Anthropic 報告稱 Claude Desktop 用戶每天調用 MCP 工具超過 200 萬次。這個數字增長很快。你的客戶已經在嘗試使用 AI 代理與你的產品交互 -- 問題是你是否讓這變得容易或令人沮喪。

這是一個發行渠道。 當某人安裝 Claude Desktop 並輸入「幫我管理我的項目」時,AI 可以發現並使用用戶配置的 MCP 伺服器。你的產品可通過自然語言訪問。這不是噱頭 -- 它是一個真正的新產品表面積。

你的競爭對手正在做。 Stripe、Linear、Notion、GitHub、Sentry 和 Supabase 都在 2025 年上半年發布了 MCP 伺服器。如果你在 B2B SaaS 中,而你沒有一個,你就落後了。

因素 僅 REST API REST API + MCP 伺服器
AI 代理可訪問性 需要為每個代理進行自訂集成 任何 MCP 客戶端自動運作
發現 開發者閱讀文檔 AI 在運行時發現功能
首次集成時間 數小時到數天 數分鐘
自然語言交互 未經包裝器不可能 內置
維護負擔 一個程式碼庫 兩個程式碼庫(但 MCP 包裝 REST)

MCP 架構:它實際上是如何運作的

在我們編寫程式碼之前,讓我們先理清架構。MCP 部署有三個部分:

  1. MCP 客戶端 -- AI 應用程式(Claude Desktop、Cursor、你的自訂代理)。它發現你的伺服器的功能並呼叫它們。
  2. MCP 伺服器 -- 你的程式碼。它通過 MCP 協議公開工具、資源和提示。這是我們正在構建的。
  3. 你的 SaaS API -- 你的 MCP 伺服器調用的實際後端以完成任務。

流程如下所示:

用戶 → AI 客戶端 → MCP 客戶端 → MCP 伺服器 → 你的 SaaS API
                                      ↓
                              回應流回

你的 MCP 伺服器本質上是一個協議適配器。它在 MCP 協議(AI 客戶端使用)和你現有的 REST/GraphQL API 之間進行轉換。這意味著你根本不需要修改現有的 API。MCP 伺服器在它旁邊。

傳輸選項

對於 SaaS 產品,你有兩個實際的傳輸選項:

  • Streamable HTTP(推薦):使用標準 HTTP 請求和可選的 SSE 流。可在負載均衡器、CDN 和標準基礎設施後面運作。這是你對遠程/託管 MCP 伺服器想要的。
  • SSE(舊版):原始遠程傳輸。仍然有效,但規範為新實現推薦 Streamable HTTP。

Stdio 傳輸對本地工具很好,但不適用於 SaaS 產品的 MCP 伺服器。

設置你的 MCP 伺服器項目

讓我們用 TypeScript 構建這個。官方 @modelcontextprotocol/sdk 軟件包維護良好,是生產使用的正確選擇。Python 具有 mcp(官方 Python SDK),如果那是你的堆棧,但我將專注於 TypeScript,因為我使用的大多數 SaaS 後端都使用 Node.js。

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

設置你的 tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"]
}

現在讓我們創建基本的伺服器結構:

// src/server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import express from "express";

const app = express();
app.use(express.json());

const server = new McpServer({
  name: "my-saas-mcp",
  version: "1.0.0",
  description: "MCP server for My SaaS Product",
});

// 我們將在這裡添加工具、資源和提示

app.post("/mcp", async (req, res) => {
  const transport = new StreamableHTTPServerTransport({
    sessionIdGenerator: () => crypto.randomUUID(),
  });
  
  await server.connect(transport);
  await transport.handleRequest(req, res);
});

app.get("/mcp", async (req, res) => {
  // SSE 端點用於流式回應
  const transport = new StreamableHTTPServerTransport({
    sessionIdGenerator: () => crypto.randomUUID(),
  });
  
  await server.connect(transport);
  await transport.handleRequest(req, res);
});

const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
  console.log(`MCP server running on port ${PORT}`);
});

那是你的骨架。讓我們填充它。

定義工具、資源和提示

工具

工具是最重要的原始物件。每個工具都是 AI 可以使用結構化參數呼叫的函數。以下是如何定義一個:

import { z } from "zod";

server.tool(
  "create_project",
  "Create a new project in the workspace",
  {
    name: z.string().describe("The project name"),
    description: z.string().optional().describe("Project description"),
    team_id: z.string().describe("ID of the team that owns this project"),
  },
  async ({ name, description, team_id }) => {
    // 在這裡呼叫你的 SaaS API
    const project = await apiClient.createProject({ name, description, team_id });
    
    return {
      content: [
        {
          type: "text",
          text: JSON.stringify(project, null, 2),
        },
      ],
    };
  }
);

我對工具設計學到了一些東西:

對描述要具體。 AI 使用你的工具描述和參數描述來決定何時以及如何呼叫它。模糊的描述導致錯誤的工具選擇。「創建一個新項目」可以。「在用戶的工作區中創建新項目。需要 team_id,可以從 list_teams 工具獲取」要好得多。

保持工具數量可管理。 我見過人們公開 50 多個工具,想知道為什麼 AI 會感到困惑。從 10-15 個核心操作開始。你始終可以添加更多。

返回結構化數據。 始終在文本內容中返回 JSON。AI 解析得更好。

資源

資源公開可讀數據。將它們視為 AI 消費的 GET 端點:

server.resource(
  "project-list",
  "projects://list",
  "List of all projects in the workspace",
  async () => {
    const projects = await apiClient.listProjects();
    return {
      contents: [
        {
          uri: "projects://list",
          mimeType: "application/json",
          text: JSON.stringify(projects, null, 2),
        },
      ],
    };
  }
);

提示

提示是可重用的範本。它們使用不足但功能強大:

server.prompt(
  "weekly-report",
  "Generate a weekly status report for a project",
  {
    project_id: z.string().describe("The project ID to report on"),
  },
  async ({ project_id }) => {
    const stats = await apiClient.getProjectStats(project_id);
    return {
      messages: [
        {
          role: "user",
          content: {
            type: "text",
            text: `Generate a weekly status report based on this data: ${JSON.stringify(stats)}. Include completed tasks, blockers, and upcoming deadlines.`,
          },
        },
      ],
    };
  }
);

連接到你的 SaaS API

你的 MCP 伺服器需要與你現有的 API 通訊。我建議創建一個有類型的 API 客戶端類:

// src/api-client.ts
class SaaSApiClient {
  private baseUrl: string;
  private apiKey: string;

  constructor(baseUrl: string, apiKey: string) {
    this.baseUrl = baseUrl;
    this.apiKey = apiKey;
  }

  private async request<T>(path: string, options?: RequestInit): Promise<T> {
    const response = await fetch(`${this.baseUrl}${path}`, {
      ...options,
      headers: {
        "Authorization": `Bearer ${this.apiKey}`,
        "Content-Type": "application/json",
        ...options?.headers,
      },
    });

    if (!response.ok) {
      const error = await response.text();
      throw new Error(`API error ${response.status}: ${error}`);
    }

    return response.json() as T;
  }

  async createProject(data: CreateProjectInput): Promise<Project> {
    return this.request<Project>("/api/v1/projects", {
      method: "POST",
      body: JSON.stringify(data),
    });
  }

  async listProjects(): Promise<Project[]> {
    return this.request<Project[]>("/api/v1/projects");
  }

  // ... 更多方法
}

保持此客戶端精簡。它是一個直通,不是業務邏輯層。

認證和多租戶

這是事情變得有趣的地方 -- 以及大多數教程忽略的難題。

你的 MCP 伺服器需要以兩種方式認證請求:

  1. MCP 客戶端向你的 MCP 伺服器認證(「這是有效用戶嗎?」)
  2. 你的 MCP 伺服器向你的 SaaS API 認證(「代表此用戶行動」)

OAuth 2.0(推薦用於生產)

MCP 規範包括一個基於 OAuth 2.1 的授權框架。對於 SaaS 產品,這是正確的方法:

// 中間件以提取和驗證 OAuth 令牌
app.use("/mcp", async (req, res, next) => {
  const authHeader = req.headers.authorization;
  if (!authHeader?.startsWith("Bearer ")) {
    return res.status(401).json({ error: "Missing authorization" });
  }

  const token = authHeader.slice(7);
  
  try {
    const user = await validateOAuthToken(token);
    req.user = user;
    next();
  } catch {
    return res.status(401).json({ error: "Invalid token" });
  }
});

然後將用戶上下文傳遞到你的工具處理程序中。這對多租戶至關重要 -- 每個 API 呼叫都應該限制到經過認證的用戶的工作區。

API 密鑰方法(更簡單,用於內部/早期階段)

如果你處於早期階段或這是僅內部的,API 密鑰運作良好:

const apiKey = req.headers["x-api-key"] as string;
const client = new SaaSApiClient(process.env.API_BASE_URL!, apiKey);

用戶在配置其客戶端中的 MCP 伺服器時提供他們的 API 密鑰,它被傳遞到你的 API。

錯誤處理和驗證

AI 代理需要清晰的錯誤消息。當工具失敗時,AI 需要理解原因,以便它可以修復輸入或向用戶解釋問題。

server.tool(
  "get_project",
  "Get project details by ID",
  { project_id: z.string().uuid().describe("The project's UUID") },
  async ({ project_id }) => {
    try {
      const project = await apiClient.getProject(project_id);
      return {
        content: [{ type: "text", text: JSON.stringify(project, null, 2) }],
      };
    } catch (error) {
      const message = error instanceof Error ? error.message : "Unknown error";
      return {
        isError: true,
        content: [
          {
            type: "text",
            text: `Failed to get project: ${message}. Make sure the project_id is a valid UUID and the project exists in your workspace.`,
          },
        ],
      };
    }
  }
);

注意 isError: true 標誌。這告訴 AI 客戶端工具呼叫失敗,因此它可以適當地處理它。始終在錯誤消息中包含可行的指導。

測試你的 MCP 伺服器

測試 MCP 伺服器在 2025 年變得容易得多。以下是你的選項:

MCP 檢查器

官方 MCP Inspector 是開發過程中你最好的朋友:

npx @modelcontextprotocol/inspector

這為你提供了一個網路 UI,你可以在其中連接到你的伺服器、瀏覽工具/資源並以互動方式調用它們。經常使用它。

自動化測試

對於 CI/CD,將你的工具作為常規異步函數進行測試:

import { describe, it, expect } from "vitest";

describe("create_project tool", () => {
  it("should create a project with valid input", async () => {
    const result = await createProjectHandler({
      name: "Test Project",
      team_id: "team-123",
    });

    expect(result.isError).toBeUndefined();
    const data = JSON.parse(result.content[0].text);
    expect(data.name).toBe("Test Project");
  });

  it("should return error for missing team_id", async () => {
    // Zod 驗證應在處理程序運行前捕獲這個
    // 測試驗證層
  });
});

使用 Claude Desktop 進行集成測試

伺服器運行後,將其添加到 Claude Desktop 的配置中:

{
  "mcpServers": {
    "my-saas": {
      "url": "http://localhost:3001/mcp",
      "headers": {
        "Authorization": "Bearer your-test-token"
      }
    }
  }
}

然後只需與 Claude 交談並嘗試自然地使用你的工具。你將快速找到自動化測試遺漏的邊界情況。

部署和生產考慮

在哪裡部署

你的 MCP 伺服器只是一個 Express 應用程式。將其部署到任何部署 Node.js 服務的地方。一些不錯的選項:

平台 冷啟動 成本(估計) 最適合
Railway ~$5-20/月 中小型 SaaS
Fly.io <500ms ~$5-15/月 全球分佈
AWS ECS/Fargate ~$15-50/月 企業、現有 AWS
Vercel (Edge) <100ms $0-20/月 如果你已在 Vercel 上
Cloudflare Workers <5ms $0-5/月 性能關鍵

速率限制

AI 代理可能很活躍。單個用戶對話可能會觸發 20-30 個工具呼叫。實現足夠寬鬆以用於正常 AI 使用但防止濫用的速率限制:

import rateLimit from "express-rate-limit";

const limiter = rateLimit({
  windowMs: 60 * 1000, // 1 分鐘
  max: 60, // 每分鐘每個用戶 60 個請求
  keyGenerator: (req) => req.user?.id || req.ip,
});

app.use("/mcp", limiter);

監控

記錄每個工具調用的用戶 ID、工具名稱和延遲。你希望能夠看到最常使用的工具、最常失敗的工具以及延遲瓶頸在哪裡。Datadog、Axiom 或甚至結構化 JSON 日誌到 CloudWatch 都可以。

版本控制

你的 MCP 伺服器將演變。在你的伺服器元數據中使用 version 欄位,並考慮在轉換期間在路徑前綴後面運行多個版本(/mcp/v1/mcp/v2)。

真實示例:為項目管理 SaaS 構建 MCP 伺服器

讓我走過一個真實的例子。假設你正在為項目管理工具(想想簡化的 Linear 或 Asana)構建 MCP 伺服器。

以下是我公開的工具集:

// 核心 CRUD 工具
server.tool("list_projects", ...);
server.tool("get_project", ...);
server.tool("create_project", ...);
server.tool("update_project", ...);

// 任務管理
server.tool("list_tasks", ...);  // 使用狀態、受託人、項目的過濾器
server.tool("create_task", ...);
server.tool("update_task", ...); // 更新狀態、受託人、優先級
server.tool("add_comment", ...);

// 搜索和報告
server.tool("search", ...);      // 跨項目和任務的全文搜索
server.tool("get_project_stats", ...); // 項目的摘要統計

// 資源
server.resource("workspace-info", ...); // 工作區配置、團隊成員

// 提示
server.prompt("standup-report", ...);   // 從最近活動生成一個站會
server.prompt("sprint-planning", ...);  // 幫助規劃衝刺

那是 12 個工具、1 個資源和 2 個提示。足夠有用,而不會淹沒 AI 的工具選擇。

用戶體驗看起來像這樣:某人打開 Claude Desktop 並說「後端重寫項目中有哪些任務超期?」Claude 調用帶有狀態篩選器和項目名稱的 list_tasks,獲取結果,並以自然語言呈現它們。用戶說「將身份驗證遷移任務分配給 Sarah,並將其提升為高優先級。」Claude 調用 update_task。它感覺神奇,它真的只是協議管道。

如果你正在構建類似的東西,並希望在 Next.js 前端或通常伴隨這些項目的無頭 CMS 層上獲得幫助,那是我們在 Social Animal 做很多的事情。但 MCP 伺服器本身?這是你可以用這個指南在內部構建的絕對東西。

常見問題

MCP 和函數呼叫之間有什麼區別? 函數呼叫(如 OpenAI 的函數呼叫或 Claude 的工具使用)是 AI 模型決定在單個 API 呼叫中調用函數的方式。MCP 是允許 AI 客戶端發現從外部伺服器可用的函數的協議。它們一起運作 -- AI 客戶端在內部使用函數呼叫來決定何時調用 MCP 工具。將 MCP 視為系統之間的管道,將函數呼叫視為模型的決策過程。

構建和運行 MCP 伺服器需要花費多少? 伺服器本身是輕量級的。對於具有 10-20 個工具的典型 SaaS 產品,你查看幾百行 TypeScript。託管成本 $5-50/月,取決於你的流量和平台。真正的成本是開發者時間 -- 預算 2-4 週用於生產質量的 MCP 伺服器,包括認證、錯誤處理、監控和測試。如果這感覺很多,我們已經幫助團隊更快地運行這些。檢查我們的定價頁面了解詳情。

我可以使用 Python 而不是 TypeScript 嗎? 絕對。官方 Python SDK(pip install mcp)非常好,可能對工具定義有更好的人體工程學。使用你的團隊知道的任何東西。協議是不可知的語言。如果你的 SaaS 後端是 Python(Django、FastAPI),在 Python 中構建 MCP 伺服器更有意義,因為你可以共享模型和驗證邏輯。

我需要修改我的現有 API 嗎? 不。你的 MCP 伺服器是一個分別的服務,調用你的現有 API。它是一個適配器層。也就是說,你可能會發現自己想要添加一些 API 端點,特別是為 AI 消費 -- 比如返回比你的 UI 需要更多上下文的搜索端點。沒關係。但它是附加的,不是修改。

我如何處理長時間運行的操作? 某些工具可能觸發需要數分鐘的操作(如生成報告或處理大型導入)。使用 MCP 的進度通知功能讓客戶端始終知道。你的工具處理程序可以在等待操作完成時發出進度更新。對於非常長的操作(>30 秒),考慮立即返回作業 ID 並提供單獨的 check_job_status 工具。

MCP 對生產足夠穩定嗎? 是的,從 2025 年中期。規範在 2025 年 3 月達到 v1.0,2025-03-26 修訂版(添加了 Streamable HTTP)是主要客戶端採用的。Anthropic、Microsoft、Google 和 OpenAI 都在投資協議。它不會消失。也就是說,請關注規範更新 -- 有關於更好的認證流和服務器到服務器通訊的活躍提案。

在 MCP 工具中處理分頁的最佳方式是什麼? 默認返回合理數量的結果(20-50 項),並接受 cursorpage 參數。在你的回應中包含分頁元數據,以便 AI 知道有更多結果。類似:{ results: [...], next_cursor: "abc123", total_count: 342 }。如果用戶需要更多數據,AI 將自然地要求下一頁。

一個 MCP 伺服器可以同時支持多個 AI 客戶端嗎? 是的,應該。你的 MCP 伺服器只是一個 Express 應用程式處理並發請求。每個請求都包括用戶的認證令牌,因此你將所有內容限制在正確的租戶。如果你正確使用 Streamable HTTP 傳輸,就沒有要擔心的客戶端特定狀態。像任何其他無狀態 API 伺服器一樣對待它。