一个用于开发自定义模型上下文协议(MCP)服务器的基础框架,支持TypeScript。提供完整的分层架构模式、工作示例实现以及全面的开发者基础设施,以连接AI助手与外部API和数据源。
模型上下文协议(MCP)是一个开放标准,用于安全地将AI系统连接到外部工具和数据源。此样板实现了MCP规范,具有干净的分层架构,可以扩展以构建适用于任何API或数据源的自定义MCP服务器。
# 克隆仓库
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
TRANSPORT_MODE=stdio node dist/index.jsPORT环境变量配置)http://localhost:3000/mcphttp://localhost:3000/ → 返回服务器版本TRANSPORT_MODE=http node dist/index.jssrc/
├── 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>
样板遵循一个干净的分层架构,促进维护性和明确的关注点分离:
src/cli/)get-ip-details [ipAddress] --include-extended-data --no-use-httpssrc/tools/)ip_get_details工具src/resources/)ip://8.8.8.8资源src/controllers/)src/services/)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)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运行服务器调试日志:通过DEBUG=true环境变量启用
创建~/.mcp/configs.json:
{
"boilerplate": {
"environments": {
"DEBUG": "true",
"TRANSPORT_MODE": "http",
"PORT": "3000"
}
}
}
</details>
在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 };
在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 };
在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 };
在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 };
更新入口点以注册新的组件:
// 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地址地理位置示例,演示了所有层次:
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资源: