#MCP#TypeScript

从零搭建一个 MCP Server:把你的能力接进所有 AI 工具

上一篇讲了 MCP 的协议原理,这篇动手——从零做一个生产可用的 MCP Server:选场景、用官方 SDK 实现 resources + tools、本地 stdio 与远程 HTTP 两种部署、用 MCP Inspector 调试,最后让 Claude Code 和 Cursor 都用上它。

本文要点

  • 主题聚焦:MCP、TypeScript
  • 适合人群:独立开发者、全栈工程师、AI 应用开发者
  • 核心价值:快速理解背景、实践路径、常见误区和工程建议
作者 @admin·发布于 2026年7月17日·更新于 2026年7月23日

上一篇《MCP 协议深度解析》讲清了"为什么"和"协议长什么样"。这篇讲"怎么做"——我们做一个真实的、能被 Claude Code 和 Cursor 直接调用的 MCP Server:一个"个人知识库检索"服务,暴露文档作为 resources、暴露"检索/总结"作为 tools。

跑通这一篇,你就掌握了把任意能力接入 AI 编程生态的标准方法。

一、选场景:什么样的东西适合做成 MCP

不是所有功能都值得做成 Server。值得封装的判断标准:

  • 会被反复调用(一次性的不需要)。
  • 有状态或有数据(连着某个库、某个 API、某组文件)。
  • 跨工具复用(不止一个 AI 客户端想用)。

我们选"个人知识库检索":把本地一批 markdown 笔记暴露给 AI,让模型能 search("MCP") 检索、read("notes/xxx.md") 读全文。这对任何编程助手都是刚需。

二、初始化项目

bash
mkdir kb-mcp && cd kb-mcp
npm init -y
npm i @modelcontextprotocol/sdk zeta zod
npm i -D typescript @types/node tsx

SDK 版本建议固定到具体版本(MCP 规范演进快)。目录结构:

text
kb-mcp/
  src/index.ts       // Server 入口
  src/search.ts      // 检索逻辑(简单 BM25 或 grep)
  notes/             // 被检索的 markdown

三、实现 Server:resources + tools

ts
// src/index.ts
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { listNotes, readNote, searchNotes } from "./search.js";

const server = new McpServer({ name: "kb-mcp", version: "0.1.0" });

// 1) Resources:把每条笔记作为可读资源
const notesResource = new ResourceTemplate("notes://{slug}", { list: undefined });
server.resource(
  "note",
  notesResource,
  async (uri, { slug }) => ({
    contents: [{ uri: uri.href, mimeType: "text/markdown", text: await readNote(slug as string) }],
  }),
);

// 2) Tools:检索 + 总结入口
server.tool(
  "search_notes",
  "Full-text search across local markdown notes. Returns matching slugs and snippets.",
  { query: z.string().describe("keywords to search") },
  async ({ query }) => {
    const hits = await searchNotes(query);
    return {
      content: [{ type: "text", text: JSON.stringify(hits, null, 2) }],
    };
  },
);

server.tool(
  "read_note",
  "Read the full content of a note by its slug.",
  { slug: z.string() },
  async ({ slug }) => ({
    content: [{ type: "text", text: await readNote(slug) }],
  }),
);

await server.connect(new StdioServerTransport());

检索逻辑这里用最朴素的全文匹配(够用、零依赖):

ts
// src/search.ts
import fs from "node:fs/promises";
import path from "node:path";

const NOTES_DIR = path.resolve("notes");

export async function listNotes() {
  return (await fs.readdir(NOTES_DIR)).filter((f) => f.endsWith(".md"));
}

export async function readNote(slug: string) {
  return fs.readFile(path.join(NOTES_DIR, `${slug}.md`), "utf8");
}

export async function searchNotes(query: string) {
  const files = await listNotes();
  const q = query.toLowerCase();
  const hits = [];
  for (const f of files) {
    const text = await readNote(f.replace(/\.md$/, ""));
    const idx = text.toLowerCase().indexOf(q);
    if (idx >= 0) {
      hits.push({ slug: f.replace(/\.md$/, ""), snippet: text.slice(Math.max(0, idx - 40), idx + 80) });
    }
  }
  return hits.slice(0, 10);
}

四、本地调试:MCP Inspector

官方提供的 Inspector 是调试利器,能可视化看到 resources/tools 列表并手动调用:

bash
npx @modelcontextprotocol/inspector npx tsx src/index.ts

它会起一个本地 web 界面,列出你 Server 暴露的所有 tools/resources,点一下就能测调用结果——比"配到 Claude Code 再问一句看它有没有调"快十倍。开发阶段全程用 Inspector,开发完再接客户端。

五、接入客户端

Claude Code(项目级 .mcp.json,随仓库共享):

jsonc
{
  "mcpServers": {
    "kb": { "command": "npx", "args": ["tsx", "/abs/path/kb-mcp/src/index.ts"] }
  }
}

Cursor(设置 → MCP → Add)填同样内容。重启后,问"我笔记里有没有关于 RAG 的"——模型会自动 search_notes(query:"RAG") → read_note(slug:"...") 并回答。

六、改成远程 Server(HTTP)

要被多个客户端共享、或部署到服务器,就上远程模式(Streamable HTTP):

ts
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { createServer } from "http";

const httpServer = createServer(async (req, res) => {
  const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
  await server.connect(transport);
  await transport.handleRequest(req, res);
});
httpServer.listen(3001);

客户端配置从 command/args 改成 url: "https://kb.example.com/mcp"。远程务必加鉴权——在 req 入口校验 Authorization: Bearer <token>,否则你的笔记对任何知道地址的人裸奔。

七、发布与版本管理

  • 固定版本:@modelcontextprotocol/sdk 升级偶尔会改 API,生产环境锁版本。
  • 提供一份 README:写清暴露了哪些 tools、每个 tool 的参数和用途——这既是给用户的文档,也是你写 tool description 时的素材库。
  • 语义化日志:每个 tool 调用打一行结构化日志(who/what/args/latency),远程 Server 出了问题才好查。

小结

做一个 MCP Server 的完整闭环:选场景 → SDK 实现 resources/tools → Inspector 调试 → 接客户端 → (可选)改远程 + 鉴权 → 发布。

整件事的关键不是代码量(核心就几十行),而是把"你独有的数据/能力"按 MCP 这份契约标准化的思路。一旦封装好,它就同时进入了 Claude Code、Cursor、Cline、Continue 的能力池——这是过去要做 N 份集成才能达到的覆盖。把你的产品/数据做成 MCP Server,是 AI 编程时代最低成本的"被集成"策略。

Related

相关推荐