Skip to content

MCP 服务器模板

MCP(Model Context Protocol)服务器模板提供了简化的框架封装,降低使用门槛,方便创建可供 Cursor 等 AI 客户端使用的工具。

技术栈

  • Node.js - JavaScript 运行时
  • TypeScript - 类型安全的开发
  • Model Context Protocol SDK - 官方 MCP SDK
  • Fastify - 快速的 Web 框架
  • JWT - 认证支持

快速开始

1. 创建项目

bash
# 初始化项目
vup init my-mcp-project
cd my-mcp-project

# 添加 MCP 模板
vup add my-mcp

2. 安装依赖

bash
# 安装依赖
pnpm install

3. 启动开发

bash
# STDIO 模式(本地,供 Cursor 直接调用)
cd apps/my-mcp
pnpm dev

# SSE 模式(远程)
pnpm dev:remote

项目结构

apps/my-mcp/
├── src/
│   ├── framework/              # 框架核心
│   │   ├── defineTool.ts      # 工具定义辅助函数
│   │   ├── requireAuth.ts      # 认证处理
│   │   ├── toolRegistry.ts    # 工具注册表
│   │   ├── createServer.ts     # 服务器创建
│   │   ├── types.ts            # 类型定义
│   │   └── index.ts            # 框架入口
│   ├── tools/                  # 工具定义
│   │   ├── auth.ts             # 认证相关工具
│   │   ├── public.ts           # 公开工具(简单示例)
│   │   ├── demo.ts             # 示例工具(文档搜索)
│   │   └── index.ts            # 工具导出
│   └── server.ts               # 服务器入口
├── public/
│   └── login.html              # 登录页面
├── data/
│   └── docs.csv                # 示例数据
├── package.json
└── tsconfig.json

核心特性

简化 API

使用 defineTool() 一行代码定义工具:

typescript
// src/tools/my-tool.ts
import { defineTool } from '../framework';
import type { ToolContext } from '../framework';

export const my_tool = defineTool({
  name: 'my_tool',
  description: '我的工具描述',
  inputSchema: {
    properties: {
      param: { type: 'string', description: '参数说明' },
    },
    required: ['param'],
  },
  requiresAuth: true, // 一行代码启用认证
  handler: async (args, context: ToolContext) => {
    // context.userId 已自动注入,无需手动检查
    return {
      content: [{ type: 'text', text: `结果: ${args.param}` }],
    };
  },
});

自动认证

通过 requiresAuth: true 自动处理认证:

typescript
export const my_tool = defineTool({
  name: 'my_tool',
  requiresAuth: true, // 自动检查认证
  handler: async (args, context) => {
    // context.userId 已确保存在
  },
});

工具注册

src/tools/index.ts 中注册工具:

typescript
import { my_tool } from './my-tool';

export const TOOLS = [my_tool];

框架 API

defineTool()

定义工具的辅助函数,自动处理认证、类型转换等。

typescript
defineTool({
  name: string;
  description: string;
  inputSchema: {
    properties: Record<string, any>;
    required?: string[];
  };
  requiresAuth?: boolean;  // 是否需要认证
  handler: (args, context) => Promise<{ content: ... }>;
})

requireAuth()

手动包装工具处理器,自动检查认证。

typescript
import { requireAuth } from '../framework';

const handler = requireAuth(async (args, context) => {
  // 这里已确保 context.userId 存在
});

setAuthConfig()

设置认证配置。

typescript
import { setAuthConfig } from '../framework';

setAuthConfig({
  loginUrl: 'http://localhost:9316/login.html',
  checkAuth: (context) => !!context.userId,
});

createMcpServer()

创建 MCP 服务器。

typescript
import { createMcpServer, createToolRegistry } from './framework';
import { TOOLS } from './tools';

const registry = createToolRegistry();
TOOLS.forEach((tool) => registry.register(tool));

createMcpServer(
  {
    name: 'mcp-server',
    version: '1.0.0',
    mode: 'stdio', // 或 'sse'
    port: 9316,
    auth: {
      loginUrl: 'http://localhost:9316/login.html',
      checkAuth: (context) => !!context.userId,
    },
  },
  registry
);

开发模式

STDIO 模式(本地)

用于 Cursor 等 AI 客户端直接调用:

bash
pnpm dev

SSE 模式(远程)

用于远程服务器部署:

bash
pnpm dev:remote

访问:

  • MCP 端点: http://localhost:9316/mcp
  • 登录页: http://localhost:9316/login.html

工具示例

search_docs - 文档搜索工具

这是一个完整的示例工具(src/tools/demo.ts),展示如何实现一个实用的工具。

功能特性

  • 从 CSV 文件读取文档数据
  • 支持按标题、内容、作者、分类搜索
  • 返回格式化的搜索结果
  • 可限制返回数量(默认 10 条,最大 50 条)

数据来源

  • CSV 文件:data/docs.csv
  • 包含 50 条示例文档数据
  • 字段:id, title, content, author, category, created_at

代码示例

typescript
// src/tools/demo.ts
export const search_docs = defineTool({
  name: 'search_docs',
  description: '搜索文档库,支持按标题、内容、作者、分类搜索',
  inputSchema: {
    properties: {
      query: { type: 'string', description: '搜索关键词' },
      limit: { type: 'number', description: '返回数量限制(默认 10)' },
    },
    required: ['query'],
  },
  handler: async (args, context) => {
    // 从 CSV 读取数据
    const docs = loadDocs();
    // 搜索并返回结果
    // ...
  },
});

认证机制

自动认证

使用 requiresAuth: true 自动处理认证:

typescript
export const my_tool = defineTool({
  name: 'my_tool',
  requiresAuth: true, // 自动检查认证
  handler: async (args, context) => {
    // context.userId 已确保存在
  },
});

手动认证

使用 requireAuth() 包装处理器:

typescript
import { requireAuth } from '../framework';

const handler = requireAuth(async (args, context) => {
  // 认证已通过
});

认证流程

  1. 工具调用时,如果 requiresAuth: true,框架自动检查 context.userId
  2. 如果未认证,抛出 UrlElicitationRequiredError
  3. Cursor 等客户端识别错误码 -32042elicitations 数组
  4. 客户端自动打开登录页面
  5. 用户登录后,客户端携带 JWT token 重新调用工具

可用脚本

json
{
  "scripts": {
    "dev": "tsx watch src/server.ts",
    "dev:remote": "tsx watch src/server.ts --remote",
    "build": "tsc",
    "start": "node .output/server.js",
    "start:remote": "node .output/server.js --remote"
  }
}

最佳实践

错误处理

typescript
try {
  // 工具逻辑
} catch (error) {
  return {
    content: [
      {
        type: 'text',
        text: `错误: ${error.message}`,
      },
    ],
  };
}

类型安全

始终为工具参数定义正确的类型:

typescript
inputSchema: {
  properties: {
    query: { type: 'string', description: '搜索关键词' },
    limit: { type: 'number', description: '结果限制' },
  },
  required: ['query'],
}

工具组织

按功能组织工具:

src/tools/
├── auth.ts        # 认证工具
├── database.ts    # 数据库工具
├── file.ts        # 文件操作
└── index.ts       # 导出所有工具

相关资源