返回市场
模板MCP服务器

模板MCP服务器

作者:aashari61 星标更新:2025-11-20

项目介绍

Boilerplate MCP Server

一个用于开发自定义模型上下文协议(MCP)服务器的基础框架,支持TypeScript。提供完整的分层架构模式、工作示例实现以及全面的开发者基础设施,以连接AI助手与外部API和数据源。

NPM 版本 许可证: ISC

功能

  • 双传输支持:STDIO和可流式传输HTTP传输,自动回退
  • 五层架构:命令行界面(CLI)、工具、控制器、服务和实用工具之间的清晰分离
  • 类型安全:完全使用TypeScript实现,并带有Zod模式验证
  • 完整的IP地址示例:工具、资源和CLI命令用于IP地理位置查询
  • 全面测试:单元测试和集成测试,附带覆盖率报告
  • 生产工具:ESLint、Prettier、语义化发布和MCP Inspector集成
  • 错误处理:带有上下文日志的结构化错误处理

什么是MCP?

模型上下文协议(MCP)是一个开放标准,用于安全地将AI系统连接到外部工具和数据源。此样板实现了MCP规范,具有干净的分层架构,可以扩展以构建适用于任何API或数据源的自定义MCP服务器。

先决条件

  • Node.js (>=18.x): 下载
  • Git: 用于版本控制

快速开始

# 克隆仓库
git clone https://github.com/aashari/boilerplate-mcp-server.git
cd boilerplate-mcp-server

# 安装依赖
npm install

# 构建项目
npm run build

# 在不同模式下运行:

# 1. CLI模式 - 直接执行命令
npm run cli -- get-ip-details 8.8.8.8
npm run cli -- get-ip-details              # 获取当前IP
npm run cli -- get-ip-details 1.1.1.1 --include-extended-data

# 2. STDIO传输 - 用于AI助手集成(Claude Desktop, Cursor)
npm run mcp:stdio

# 3. HTTP传输 - 用于基于Web的集成
npm run mcp:http

# 4. 使用MCP Inspector进行开发
npm run mcp:inspect                         # 自动打开浏览器调试UI

传输模式

STDIO传输

  • 通过stdin/stdout进行JSON-RPC通信
  • 被Claude Desktop、Cursor AI和其他本地AI助手使用
  • 运行方式:TRANSPORT_MODE=stdio node dist/index.js

可流式传输HTTP传输

  • 基于HTTP的传输,带有服务器发送事件(SSE)
  • 支持多个并发连接和Web集成
  • 默认在3000端口运行(可通过PORT环境变量配置)
  • MCP端点:http://localhost:3000/mcp
  • 健康检查:http://localhost:3000/ → 返回服务器版本
  • 运行方式:TRANSPORT_MODE=http node dist/index.js

架构概述

<details> <summary><b>项目结构(点击展开)</b></summary>
src/
├── cli/                    # 命令行接口
│   ├── index.ts            # 带有Commander设置的CLI入口点
│   └── ipaddress.cli.ts    # IP地址CLI命令
├── controllers/            # 商业逻辑编排  
│   ├── ipaddress.controller.ts    # IP查找商业逻辑
│   └── ipaddress.formatter.ts     # 响应格式化
├── services/               # 外部API交互
│   ├── vendor.ip-api.com.service.ts  # ip-api.com服务
│   └── vendor.ip-api.com.types.ts    # 服务类型定义
├── tools/                  # MCP工具定义(AI接口)
│   ├── ipaddress.tool.ts   # AI助手使用的IP查找工具
│   └── ipaddress.types.ts  # 工具参数模式
├── resources/              # MCP资源定义
│   └── ipaddress.resource.ts # IP查找资源(URI: ip://address)
├── types/                  # 全局类型定义
│   └── common.types.ts     # 共享接口(ControllerResponse等)
├── utils/                  # 共享实用工具
│   ├── logger.util.ts      # 上下文日志系统
│   ├── error.util.ts       # MCP特定的错误格式化
│   ├── error-handler.util.ts # 错误处理实用工具
│   ├── config.util.ts      # 环境配置
│   ├── constants.util.ts   # 版本和包常量
│   ├── formatter.util.ts   # Markdown格式化
│   └── transport.util.ts   # HTTP传输实用工具
└── index.ts                # 服务器入口点(双传输)
</details>

五层架构

样板遵循一个干净的分层架构,促进维护性和明确的关注点分离:

1. CLI层(src/cli/

  • 目的:直接工具使用和测试的命令行接口
  • 实现:基于Commander的参数解析,带有上下文错误处理
  • 示例get-ip-details [ipAddress] --include-extended-data --no-use-https
  • 模式:注册命令 → 解析参数 → 调用控制器 → 处理错误

2. 工具层(src/tools/

  • 目的:AI助手可以调用的MCP工具定义
  • 实现:带有结构化响应的Zod模式验证
  • 示例:带有可选IP地址和配置选项的ip_get_details工具
  • 模式:定义模式 → 验证参数 → 调用控制器 → 格式化MCP响应

3. 资源层(src/resources/

  • 目的:通过URI访问的提供上下文数据的MCP资源
  • 实现:响应基于URI请求的资源处理器
  • 示例:提供IP地理位置数据的ip://8.8.8.8资源
  • 模式:注册URI模式 → 解析请求 → 返回格式化内容

4. 控制器层(src/controllers/

  • 目的:带有全面错误处理的业务逻辑编排
  • 实现:选项验证、回退逻辑、响应格式化
  • 示例:带有HTTPS回退的IP查找、测试环境检测、API令牌验证
  • 模式:验证输入 → 应用默认值 → 调用服务 → 格式化响应

5. 服务层(src/services/

  • 目的:带有最少业务逻辑的直接外部API交互
  • 实现:带有结构化错误处理的HTTP传输实用工具
  • 示例:带有身份验证和字段选择的ip-api.com API调用
  • 模式:构建请求 → 发起API调用 → 验证响应 → 返回原始数据

6. 实用工具层(src/utils/

  • 目的:跨所有层共享的功能
  • 关键组件
    • logger.util.ts:上下文日志(文件:方法上下文)
    • error.util.ts:MCP特定的错误格式化
    • transport.util.ts:带有重试逻辑的HTTP/API实用工具
    • config.util.ts:环境配置管理

开发者指南

开发脚本

# 构建和清理
npm run build               # 将TypeScript构建到dist/
npm run clean               # 删除dist/和coverage/
npm run prepare             # 构建+确保可执行权限(用于npm publish)

# CLI测试
npm run cli -- get-ip-details 8.8.8.8                    # 测试特定IP
npm run cli -- get-ip-details --include-extended-data    # 测试扩展数据
npm run cli -- get-ip-details --no-use-https             # 测试HTTP

# MCP服务器模式
npm run mcp:stdio           # 用于AI助手的STDIO传输
npm run mcp:http            # 在3000端口上的HTTP传输
npm run mcp:inspect         # HTTP + 自动打开MCP Inspector

# 带调试的开发
npm run dev:stdio           # 带有MCP Inspector集成的STDIO
npm run dev:http            # 启用调试日志的HTTP

# 测试
npm test                    # 运行所有测试(Jest)
npm run test:coverage       # 生成覆盖率报告
npm run test:cli            # 运行CLI特定测试

# 代码质量
npm run lint                # 带TypeScript规则的ESLint
npm run format              # Prettier格式化
npm run update:deps         # 更新依赖

环境变量

核心配置

  • TRANSPORT_MODE:传输模式(stdio | http,默认:stdio
  • PORT:HTTP服务器端口(默认:3000
  • DEBUG:启用调试日志(true | false,默认:false

IP API配置

  • IPAPI_API_TOKEN:ip-api.com扩展数据的API令牌(可选,免费层级可用)

示例.env文件

# 基本配置
TRANSPORT_MODE=http
PORT=3001
DEBUG=true

# 扩展数据(需要ip-api.com账户)
IPAPI_API_TOKEN=your_token_here

调试工具

  • MCP Inspector:用于测试您的MCP工具的可视化工具

    • 使用npm run mcp:inspect运行服务器
    • 在终端中打开显示的URL
    • 交互式测试您的工具
  • 调试日志:通过DEBUG=true环境变量启用

<details> <summary><b>配置(点击展开)</b></summary>

创建~/.mcp/configs.json

{
  "boilerplate": {
    "environments": {
      "DEBUG": "true",
      "TRANSPORT_MODE": "http",
      "PORT": "3000"
    }
  }
}
</details>

构建自定义工具

<details> <summary><b>逐步工具实现指南(点击展开)</b></summary>

1. 定义服务层

src/services/中创建一个新的服务,遵循供应商特定的命名模式:

// src/services/vendor.example-api.service.ts
import { Logger } from '../utils/logger.util.js';
import { fetchApi } from '../utils/transport.util.js';
import { ExampleApiResponse, ExampleApiRequestOptions } from './vendor.example-api.types.js';
import { createApiError, McpError } from '../utils/error.util.js';

const serviceLogger = Logger.forContext('services/vendor.example-api.service.ts');

async function get(
	param?: string,
	options: ExampleApiRequestOptions = {}
): Promise<ExampleApiResponse> {
	const methodLogger = serviceLogger.forMethod('get');
	methodLogger.debug(`Calling Example API with param: ${param}`);

	try {
		const url = `https://api.example.com/${param || 'default'}`;
		const rawData = await fetchApi<ExampleApiResponse>(url, {
			headers: options.apiKey ? { 'Authorization': `Bearer ${options.apiKey}` } : {}
		});

		methodLogger.debug('Received successful response from Example API');
		return rawData;
	} catch (error) {
		methodLogger.error('Service error fetching data', error);
		
		if (error instanceof McpError) {
			throw error;
		}
		
		throw createApiError(
			'Unexpected service error while fetching data',
			undefined,
			error
		);
	}
}

export default { get };

2. 创建控制器

src/controllers/中添加一个控制器来处理带有错误上下文的业务逻辑:

// src/controllers/example.controller.ts
import { Logger } from '../utils/logger.util.js';
import exampleService from '../services/vendor.example-api.service.js';
import { formatExample } from './example.formatter.js';
import { handleControllerError, buildErrorContext } from '../utils/error-handler.util.js';
import { ControllerResponse } from '../types/common.types.js';
import { config } from '../utils/config.util.js';

const logger = Logger.forContext('controllers/example.controller.ts');

export interface GetDataOptions {
	param?: string;
	includeMetadata?: boolean;
}

async function getData(
	options: GetDataOptions = {}
): Promise<ControllerResponse> {
	const methodLogger = logger.forMethod('getData');
	methodLogger.debug(`Getting data for param: ${options.param || 'default'}`, options);

	try {
		// 应用业务逻辑和默认值
		const apiKey = config.get('EXAMPLE_API_TOKEN');
		
		// 调用服务层
		const data = await exampleService.get(options.param, {
			apiKey,
			includeMetadata: options.includeMetadata ?? false
		});
		
		// 格式化响应
		const formattedContent = formatExample(data);
		return { content: formattedContent };
		
	} catch (error) {
		throw handleControllerError(
			error,
			buildErrorContext(
				'ExampleData',
				'getData',
				'controllers/example.controller.ts@getData',
				options.param || 'default',
				{ options }
			)
		);
	}
}

export default { getData };

3. 实现MCP工具

src/tools/中创建一个工具定义,遵循注册模式:

// src/tools/example.tool.ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { Logger } from '../utils/logger.util.js';
import { formatErrorForMcpTool } from '../utils/error.util.js';
import exampleController from '../controllers/example.controller.js';

const logger = Logger.forContext('tools/example.tool.ts');

// 定义Zod模式用于工具参数
const GetDataSchema = z.object({
	param: z.string().optional().describe('API调用的可选参数'),
	includeMetadata: z.boolean().optional().default(false)
		.describe('是否在响应中包含额外元数据')
});

async function handleGetData(args: Record<string, unknown>) {
	const methodLogger = logger.forMethod('handleGetData');
	
	try {
		methodLogger.debug('Tool example_get_data called', args);

		// 使用Zod验证参数
		const validatedArgs = GetDataSchema.parse(args);

		// 调用控制器
		const result = await exampleController.getData({
			param: validatedArgs.param,
			includeMetadata: validatedArgs.includeMetadata
		});

		// 返回MCP格式化的响应
		return {
			content: [
				{
					type: 'text' as const,
					text: result.content
				}
			]
		};
	} catch (error) {
		methodLogger.error('Tool example_get_data failed', error);
		return formatErrorForMcpTool(error);
	}
}

// 按照现有工具使用的模式注册函数
function registerTools(server: McpServer) {
	const registerLogger = logger.forMethod('registerTools');
	registerLogger.debug('Registering example tools...');

	server.tool(
		'example_get_data',
		`从Example API获取数据,带有可选参数。
使用此工具获取示例数据。返回格式化的Markdown数据。`,
		GetDataSchema.shape,
		handleGetData
	);

	registerLogger.debug('Example tools registered successfully');
}

export default { registerTools };

4. 添加CLI支持

src/cli/中按照Commander模式创建一个CLI命令:

// src/cli/example.cli.ts
import { Command } from 'commander';
import { Logger } from '../utils/logger.util.js';
import exampleController from '../controllers/example.controller.js';
import { handleCliError } from '../utils/error.util.js';

const logger = Logger.forContext('cli/example.cli.ts');

function register(program: Command) {
	const methodLogger = logger.forMethod('register');
	methodLogger.debug('Registering example CLI commands...');

	program
		.command('get-data')
		.description('从Example API获取数据')
		.argument('[param]', 'API调用的可选参数')
		.option('-m, --include-metadata', '在响应中包含额外元数据')
		.action(async (param, options) => {
			const actionLogger = logger.forMethod('action:get-data');
			
			try {
				actionLogger.debug('CLI get-data called', { param, options });

				const result = await exampleController.getData({
					param,
					includeMetadata: options.includeMetadata || false
				});

				console.log(result.content);
			} catch (error) {
				handleCliError(error);
			}
		});

	methodLogger.debug('Example CLI commands registered successfully');
}

export default { register };

5. 注册组件

更新入口点以注册新的组件:

// 1. 在src/cli/index.ts中注册CLI
import exampleCli from './example.cli.js';

export async function runCli(args: string[]) {
	// ... 现有的设置代码 ...
	
	// 注册CLI命令
	exampleCli.register(program);  // 添加这一行
	
	// ... 函数其余部分
}

// 2. 在src/index.ts中注册工具
import exampleTools from './tools/example.tool.js';

// 在startServer函数中,现有注册之后:
exampleTools.registerTools(serverInstance);
</details>

IP地址示例实现

样板包括一个完整的IP地址地理位置示例,演示了所有层次:

可用工具及命令

CLI命令:

npm run cli -- get-ip-details                           # 获取当前公共IP
npm run cli -- get-ip-details 8.8.8.8                  # 获取特定IP的详细信息
npm run cli -- get-ip-details 1.1.1.1 --include-extended-data   # 包含扩展数据
npm run cli -- get-ip-details 8.8.8.8 --no-use-https   # 强制使用HTTP(免费层级)

MCP工具:

  • ip_get_details - AI助手使用的IP地理位置查找

MCP资源:

  • `ip