news 2026/5/15 3:16:07

基于TypeScript的MCP服务器开发脚手架:快速构建AI工具集成方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于TypeScript的MCP服务器开发脚手架:快速构建AI工具集成方案

1. 项目概述:一个为Claude和Cursor量身定制的MCP服务器开发脚手架

如果你正在为Claude、Cursor这类支持Model Context Protocol(MCP)的AI工具开发自定义服务器,并且厌倦了每次都要从零开始搭建项目结构、配置TypeScript、处理错误和日志,那么这个名为bsmi021/custom-mcp-template的模板项目,很可能就是你一直在找的“开箱即用”解决方案。

简单来说,这是一个高度结构化的MCP服务器开发脚手架。它不是一个功能完整的服务器,而是一个经过精心设计的项目骨架,里面已经预置了现代TypeScript项目所需的一切:从清晰的目录分层、统一的配置管理、标准的工具(Tool)与服务(Service)抽象,到完善的开发工具链(ESLint、Prettier、Husky)。它的核心价值在于,让你能跳过所有繁琐的初始化工作,直接聚焦于实现你的业务逻辑——也就是你真正想让AI去调用的那些工具。

我最初接触MCP开发时,花了不少时间在项目配置和架构设计上。后来发现,一个良好的起点能极大提升开发效率和代码质量。这个模板正是基于这样的实践总结,它遵循了MCP社区推荐的最佳实践,特别适合需要与Claude Desktop、Cursor等客户端稳定、高效通信的服务器场景。无论你是想开发一个连接内部数据库的查询工具,还是一个调用第三方API的天气服务,都可以基于这个模板快速启动。

2. 核心设计理念与项目结构深度解析

2.1 为什么需要这样一个模板?

在深入代码之前,我们先聊聊为什么“从模板开始”是一个明智的选择。MCP服务器的开发,虽然核心是定义工具(Tools)和处理请求,但其外围的工程化挑战一点也不少:

  1. 类型安全与开发体验:MCP涉及大量的数据交换,清晰的TypeScript接口和Zod模式定义能提前捕获许多运行时错误。
  2. 配置管理:服务器可能需要读取环境变量、配置文件,动态调整行为。一个集中的配置管理器至关重要。
  3. 代码组织:随着工具数量的增加,如何避免server.ts变成一个上千行的“巨无霸”文件?业务逻辑、工具适配层、工具参数定义需要清晰分离。
  4. 错误处理与日志:统一的错误响应格式和清晰的日志输出,是调试和运维的基石。
  5. 开发效率:热重载、代码格式化、提交前检查,这些现代前端开发的标准配置,在服务器开发中同样能带来巨大便利。

这个模板的設計,正是为了系统性地解决上述问题。它不是简单地把文件堆在一起,而是体现了一种“关注点分离”和“约定优于配置”的架构思想。

2.2 项目骨架全景图与模块职责

让我们拆解模板的目录结构,理解每个部分的职责。这是你未来所有开发的“地图”。

custom-mcp-template/ ├── src/ │ ├── config/ │ │ └── ConfigurationManager.ts # 【核心】配置管理中心 │ ├── services/ │ │ ├── index.ts │ │ └── ExampleService.ts # 【核心】业务逻辑实现层 │ ├── tools/ │ │ ├── index.ts # 【核心】工具注册入口 │ │ ├── exampleTool.ts # 【核心】工具适配层 │ │ └── exampleToolParams.ts # 【核心】工具参数定义 │ ├── types/ │ │ ├── index.ts │ │ └── exampleServiceTypes.ts # 【核心】数据类型与Zod模式 │ ├── utils/ │ │ ├── index.ts │ │ ├── logger.ts # 统一日志工具 │ │ └── errors.ts # 统一错误类与处理 │ ├── initialize.ts # 服务器初始化与工具注册 │ └── server.ts # 应用主入口 ├── dist/ # TypeScript编译输出目录 ├── package.json # 项目元数据与脚本 ├── tsconfig.json # TypeScript编译器配置 ├── .eslintrc.json # 代码检查规则 ├── .prettierrc.json # 代码格式化规则 └── .gitignore # Git忽略规则

各目录核心职责详解:

  • src/config/:这里是项目的“控制台”。ConfigurationManager类采用单例模式,负责统一管理所有配置。它通常会从环境变量、配置文件或默认值中读取配置,并提供类型安全的获取方法。这样做的好处是,你在代码的任何地方引入配置管理器,都能获得一致且最新的配置,避免了配置散落各处的问题。
  • src/services/:这是你编写纯业务逻辑的地方。一个“服务”类应该只关注“做什么”,而不关心“谁在调用”或“数据如何进来/出去”。例如,一个WeatherService类可能只有一个getForecast(city: string)方法,里面封装了调用天气API、解析数据、计算指数等所有逻辑。这保证了业务逻辑的可测试性和可复用性。
  • src/tools/:这是MCP协议层与业务逻辑层的适配器。每个工具文件(如exampleTool.ts)的职责是:
    1. 定义工具在MCP中的名称和描述。
    2. 使用Zod严格校验从Claude/Cursor传来的输入参数。
    3. 调用对应的Service来执行业务逻辑。
    4. Service返回的业务数据,格式化成MCP协议要求的Content对象(通常是textimage类型)。
    5. 捕获并处理错误,将其转换为标准的McpError。 这种分离使得工具层很薄,只做协议适配,而复杂的逻辑都在Service中。
  • src/types/:项目的“数据字典”。这里用TypeScript的interfacetype定义数据结构,并用Zod创建对应的运行时验证模式(schema)。Zod模式不仅用于工具参数校验,也可以用于验证Service的输入输出,确保整个数据流都是类型安全的。
  • src/utils/:共享的“工具箱”。logger提供了分级(如info, warn, error)且可配置的日志输出,是调试的利器。errors定义了自定义错误类(如ValidationError,ServiceError),并提供了将各种错误统一转换为MCP错误格式的工具函数。
  • src/initialize.ts:这是连接一切的“接线板”。它创建服务器实例,并从tools/index.ts导入所有工具注册函数并依次调用,最终返回一个配置好的服务器实例给server.ts
  • src/server.ts:程序的“启动器”。它非常简洁,主要就是调用initialize.ts创建服务器,然后启动它监听某个端口(通常从配置中读取)。

注意:这种分层架构(工具层-服务层)是模板的精髓。它强制你进行良好的职责分离。我见过不少初学者把API调用、数据处理、错误处理全堆在工具函数里,代码很快变得难以维护。遵循这个模板的结构,能让你的项目从一开始就保持清晰。

3. 从零开始:使用模板创建并运行你的第一个MCP服务器

理论讲得再多,不如亲手跑一遍。下面我们一步步来,看看如何从这个模板孵化出一个属于你自己的、能实际运行的MCP服务器。

3.1 初始化你的项目

模板提供了一个非常方便的创建命令。假设你想创建一个管理个人书签的MCP服务器,项目名定为bookmark-mcp-server

# 使用npx直接运行模板的创建命令 npx create-mcp-server bookmark-mcp-server

执行这个命令后,会发生以下几件事:

  1. 创建项目目录:在当前路径下,一个名为bookmark-mcp-server的新文件夹会被创建。
  2. 交互式配置:命令行会提示你输入项目的基本信息,例如:
    • Project name: (默认是bookmark-mcp-server,可直接回车)
    • Description: 你可以输入A MCP server to manage my browser bookmarks.
  3. 复制模板文件:模板的所有源文件(src/,docs/, 配置文件等)都会被复制到新目录中。
  4. 更新元数据:新项目里的package.json中的name,description等字段会根据你的输入自动更新。

一个可能的坑点:如果create-mcp-server这个包还没有发布到npm,或者你是在本地开发这个模板本身,上述命令可能会失败。这时,你需要先在模板项目根目录下执行npm link,将其链接到全局,然后再在目标目录使用create-mcp-server命令。

初始化完成后,终端会给出后续指引:

cd bookmark-mcp-server npm install

进入项目目录并安装依赖,这是标准操作。

3.2 理解并运行示例代码

安装完依赖后,先别急着写代码。让我们看看模板自带的示例,理解各个部分是如何协作的。

  1. 查看示例类型定义:打开src/types/exampleServiceTypes.ts。这里定义了一个简单的ExampleData接口和一个对应的Zod模式exampleDataSchema。Zod的.describe()方法非常有用,它生成的描述可能会被MCP客户端用来生成更友好的提示。
  2. 查看示例服务:打开src/services/ExampleService.ts。这是一个简单的服务类,有一个getData方法。在实际项目中,这里可能会是数据库查询、API调用等复杂逻辑。
  3. 查看工具参数定义:打开src/tools/exampleToolParams.ts。这里定义了工具的名称、描述,以及最重要的输入参数模式。注意TOOL_PARAMS是一个Zod对象,它严格定义了客户端必须传入什么参数。
  4. 查看工具实现:打开src/tools/exampleTool.ts。这是关键文件。registerExampleTool函数做了以下几件事:
    • 调用server.tool(...)注册工具。
    • 在异步处理函数中,首先用TOOL_PARAMS.parse(args)校验输入,校验失败Zod会直接抛出错误。
    • 实例化ExampleService并调用其getData方法。
    • 将返回的数据用McpServercreateTextContent方法包装成MCP内容。
    • try-catch包裹,捕获错误并调用utils/errors中的函数将其转化为MCP错误。
  5. 查看工具注册入口:打开src/tools/index.ts。所有的工具注册函数都在这里导入,并在registerTools函数中被调用。当你添加新工具时,必须记得在这里“挂号”。
  6. 查看配置:打开src/config/ConfigurationManager.ts。目前它可能只管理日志级别等基础配置。你需要根据自己服务器的需求,在这里添加新的配置项,比如数据库连接字符串、API密钥等。

现在,让我们运行这个示例服务器:

npm run dev

这个命令启动了开发服务器,它使用了ts-nodenodemonts-node让你能直接运行TypeScript代码而无需先编译,nodemon则会监听文件变化并自动重启服务器。你会看到控制台输出服务器已启动,并监听着某个端口(例如3000)。

3.3 连接Claude Desktop进行测试

服务器跑起来了,但怎么知道它工作正常呢?我们需要一个MCP客户端来调用它。以Claude Desktop为例:

  1. 配置Claude Desktop:找到Claude Desktop的配置文件夹。
    • macOS:~/Library/Application Support/Claude/claude_desktop_config.json
    • Windows:%APPDATA%\Claude\claude_desktop_config.json
  2. 编辑配置文件:在mcpServers对象中添加你的服务器配置。对于本地开发的服务器,通常使用stdio传输方式。
{ "mcpServers": { "my-bookmark-server": { "command": "node", "args": [ "/ABSOLUTE/PATH/TO/YOUR/bookmark-mcp-server/dist/server.js" ], "env": { "NODE_ENV": "production" } } } }

重要提示:在开发阶段,使用npm run dev启动的是ts-node,但这里配置的是生产构建后的JS文件路径。所以你需要先运行npm run build来编译项目,生成dist目录下的server.js。或者,你也可以配置为直接调用ts-node运行src/server.ts,但生产环境推荐使用编译后的JS文件。

  1. 重启Claude Desktop:保存配置文件并完全重启Claude Desktop应用。
  2. 进行测试:在Claude的对话窗口中,你现在可以尝试使用你的工具了。根据示例工具的定义,你可能会输入类似“调用exampleTool,参数是...”的指令。如果配置成功,Claude会识别到这个工具并调用它,你将看到服务器的日志输出和Claude返回的结果。

4. 实战:添加一个真实可用的新工具

让我们超越示例,添加一个稍微真实一点的工具:一个获取指定GitHub仓库最近Issue的工具。我们将它命名为get_recent_issues

4.1 第一步:定义数据类型与模式

src/types/下创建githubTypes.ts

// src/types/githubTypes.ts import { z } from 'zod'; // TypeScript接口,用于内部代码提示 export interface GithubIssue { id: number; number: number; title: string; state: 'open' | 'closed'; html_url: string; created_at: string; user: { login: string; }; } export interface GithubServiceConfig { apiToken?: string; // GitHub API令牌,用于提高速率限制 baseUrl?: string; // 可选,用于企业GitHub } // Zod模式,用于运行时验证 export const githubIssueSchema = z.object({ id: z.number().describe('The unique ID of the issue'), number: z.number().describe('The issue number within the repository'), title: z.string().describe('The title of the issue'), state: z.enum(['open', 'closed']).describe('The state of the issue'), html_url: z.string().url().describe('The URL to view the issue on GitHub'), created_at: z.string().datetime().describe('The creation timestamp'), user: z.object({ login: z.string().describe('The username of the issue creator'), }), }); export const githubServiceConfigSchema = z.object({ apiToken: z.string().optional().describe('GitHub Personal Access Token (optional)'), baseUrl: z.string().url().optional().describe('Base URL for GitHub API (e.g., for GitHub Enterprise)'), });

别忘了在src/types/index.ts中导出它们:

// src/types/index.ts export * from './exampleServiceTypes'; export * from './githubTypes'; // 新增

4.2 第二步:实现核心业务逻辑服务

src/services/下创建GithubService.ts。这个服务负责与GitHub API通信。

// src/services/GithubService.ts import { ConfigurationManager } from '../config/ConfigurationManager'; import { GithubIssue, GithubServiceConfig } from '../types/githubTypes'; import { ServiceError } from '../utils/errors'; import { logger } from '../utils/logger'; export class GithubService { private config: GithubServiceConfig; private baseApiUrl: string; constructor(config?: Partial<GithubServiceConfig>) { // 合并默认配置、全局配置和传入的配置 const globalConfig = ConfigurationManager.getInstance().getConfig(); this.config = { apiToken: globalConfig.github?.apiToken, baseUrl: globalConfig.github?.baseUrl || 'https://api.github.com', ...config, }; this.baseApiUrl = this.config.baseUrl!; } async getRecentIssues(owner: string, repo: string, count: number = 5): Promise<GithubIssue[]> { const url = `${this.baseApiUrl}/repos/${owner}/${repo}/issues`; logger.info(`Fetching recent issues from ${url}`); const headers: Record<string, string> = { 'Accept': 'application/vnd.github.v3+json', 'User-Agent': 'My-MCP-Server', // GitHub API要求User-Agent }; if (this.config.apiToken) { headers['Authorization'] = `token ${this.config.apiToken}`; } const params = new URLSearchParams({ per_page: count.toString(), state: 'all', // 可以改为 'open' 或 'closed' sort: 'created', direction: 'desc', }); try { const response = await fetch(`${url}?${params}`, { headers }); if (!response.ok) { // 处理常见的API错误 if (response.status === 404) { throw new ServiceError(`Repository ${owner}/${repo} not found`, 'NOT_FOUND'); } if (response.status === 403) { throw new ServiceError('API rate limit exceeded or access forbidden', 'RATE_LIMIT'); } throw new ServiceError(`GitHub API error: ${response.statusText}`, 'API_ERROR'); } const issues: GithubIssue[] = await response.json(); // 过滤掉Pull Request(GitHub API的/issues端点也返回PR) const filteredIssues = issues.filter(issue => !('pull_request' in issue)); logger.debug(`Fetched ${filteredIssues.length} issues for ${owner}/${repo}`); return filteredIssues.slice(0, count); // 确保返回数量不超过请求数 } catch (error) { if (error instanceof ServiceError) { throw error; // 重新抛出我们已经处理过的业务错误 } // 处理网络错误等 logger.error(`Failed to fetch issues from GitHub: ${error}`); throw new ServiceError('Failed to communicate with GitHub API', 'NETWORK_ERROR'); } } }

src/services/index.ts中导出:

// src/services/index.ts export * from './ExampleService'; export * from './GithubService'; // 新增

4.3 第三步:定义工具参数模式

src/tools/下创建getRecentIssuesParams.ts

// src/tools/getRecentIssuesParams.ts import { z } from 'zod'; export const TOOL_NAME = 'get_recent_issues'; export const TOOL_DESCRIPTION = 'Fetches the most recent issues from a specified GitHub repository.'; export const TOOL_PARAMS = z.object({ owner: z.string() .min(1) .describe('The owner (username or organization) of the GitHub repository. Example: "nodejs"'), repo: z.string() .min(1) .describe('The name of the GitHub repository. Example: "node"'), count: z.number() .int() .min(1) .max(30) .optional() .default(5) .describe('The number of recent issues to fetch (max 30, default 5).'), });

4.4 第四步:实现工具适配器

src/tools/下创建getRecentIssuesTool.ts。这是连接MCP协议和业务服务的桥梁。

// src/tools/getRecentIssuesTool.ts import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { z } from 'zod'; import { GithubService } from '../services/GithubService'; import { handleToolError, ValidationError } from '../utils/errors'; import { logger } from '../utils/logger'; import { TOOL_NAME, TOOL_DESCRIPTION, TOOL_PARAMS } from './getRecentIssuesParams'; /** * 注册获取GitHub最近Issue的工具 */ export function registerGetRecentIssuesTool(server: McpServer) { server.tool( TOOL_NAME, TOOL_DESCRIPTION, // 这里需要将Zod模式转换为JSON Schema,MCP SDK内部会处理 TOOL_PARAMS as z.ZodTypeAny, async (args) => { try { logger.info(`Tool ${TOOL_NAME} called with args:`, args); // 1. 参数验证 (Zod会在server.tool内部处理,但这里再次明确解析以获取类型提示) const { owner, repo, count } = TOOL_PARAMS.parse(args); // 2. 实例化服务并执行业务逻辑 const githubService = new GithubService(); const issues = await githubService.getRecentIssues(owner, repo, count); // 3. 格式化输出为MCP内容 if (issues.length === 0) { return { content: [ { type: 'text', text: `No recent issues found in repository ${owner}/${repo}.`, }, ], }; } // 将Issue列表格式化为易读的文本 const issueListText = issues .map( (issue) => `#${issue.number} [${issue.state}] ${issue.title}\n` + ` Created by: ${issue.user.login} on ${new Date(issue.created_at).toLocaleDateString()}\n` + ` Link: ${issue.html_url}\n` ) .join('\n'); const summary = `Found ${issues.length} most recent issue(s) in **${owner}/${repo}**:\n\n`; return { content: [ { type: 'text', text: summary + issueListText, }, ], }; } catch (error) { // 4. 统一错误处理 logger.error(`Error in tool ${TOOL_NAME}:`, error); throw handleToolError(error, TOOL_NAME); } } ); logger.debug(`Tool ${TOOL_NAME} registered successfully.`); }

4.5 第五步:注册新工具

打开src/tools/index.ts,导入并调用新工具的注册函数。

// src/tools/index.ts import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { registerExampleTool } from './exampleTool'; import { registerGetRecentIssuesTool } from './getRecentIssuesTool'; // 新增导入 export function registerTools(server: McpServer) { registerExampleTool(server); registerGetRecentIssuesTool(server); // 新增调用 // ... 未来其他工具的注册也在这里添加 }

4.6 第六步:添加服务配置(可选但推荐)

如果你的服务需要配置(如GitHub API Token),需要在配置管理器中添加。打开src/config/ConfigurationManager.ts

// src/config/ConfigurationManager.ts (部分代码) import { githubServiceConfigSchema } from '../types/githubTypes'; // 导入模式 import { z } from 'zod'; // 1. 扩展全局配置模式 const serverConfigSchema = z.object({ logLevel: z.enum(['debug', 'info', 'warn', 'error']).default('info'), port: z.number().int().min(1).max(65535).default(3000), // 新增github配置节 github: githubServiceConfigSchema.optional().default({}), }); type ServerConfig = z.infer<typeof serverConfigSchema>; export class ConfigurationManager { private static instance: ConfigurationManager; private config: ServerConfig; private constructor() { // 从环境变量等来源加载配置 this.config = serverConfigSchema.parse({ logLevel: process.env.LOG_LEVEL as any, port: process.env.PORT ? parseInt(process.env.PORT) : undefined, // 从环境变量加载GitHub Token github: { apiToken: process.env.GITHUB_API_TOKEN, baseUrl: process.env.GITHUB_BASE_URL, }, }); } // 2. 添加一个获取GitHub配置的便捷方法 getGithubConfig() { return this.config.github || {}; } // ... 其他现有方法 }

现在,你可以在服务中通过ConfigurationManager.getInstance().getGithubConfig()来获取配置了,正如我们在GithubService的构造函数中所做的那样。

4.7 第七步:测试你的新工具

  1. 构建并运行

    npm run build npm start

    或者继续使用npm run dev进行开发。

  2. 更新Claude Desktop配置:确保配置指向最新编译的服务器文件。

  3. 在Claude中测试:重启Claude Desktop后,尝试对Claude说:“使用get_recent_issues工具,查看nodejs组织下node仓库最近的3个issue。” Claude应该能识别这个工具,并返回格式化的issue列表。

通过以上七个步骤,我们完整地实现了一个具备实际功能、结构清晰、易于维护的新工具。这个流程是模板所倡导的标准开发模式,熟练掌握后,添加新工具将变得非常高效和规范。

5. 开发、构建与部署全流程指南

5.1 开发工作流与效率工具

模板内置的工具链旨在提升开发体验和代码质量。

  • 开发模式npm run dev是你的主要开发命令。它利用nodemon监控src/目录下的文件变化,一旦你保存了.ts文件,服务器会自动重启。这避免了手动停止、重启的麻烦,实现了近乎实时的反馈。
  • 代码质量
    • npm run lint:运行ESLint检查代码风格和潜在问题。模板通常配置了较为严格的规则,帮助你养成良好习惯。
    • npm run format:运行Prettier自动格式化代码,确保团队协作时代码风格统一。
  • Git提交前检查:模板通过huskylint-staged配置了Git钩子。当你执行git commit时,会自动对暂存区的文件运行lintformat。这保证了提交到仓库的代码都是符合规范的。如果这是你第一次使用该项目,可能需要运行npx husky install来初始化钩子。

实操心得:我强烈建议在编辑器中集成ESLint和Prettier插件,并设置为保存时自动格式化。这样在开发时就能即时看到问题并修正,而不是等到提交时才被拦截。

5.2 生产环境构建与部署

开发完成后,你需要将TypeScript代码编译成JavaScript,以便在生产环境(如服务器、容器)中运行。

  1. 构建:运行npm run build。这个命令执行tsc(TypeScript编译器),根据tsconfig.json中的配置,将src/下的所有.ts文件编译成.js文件,并输出到dist/目录。同时,它可能还会处理非.ts文件的复制(通过配置tsc或额外的脚本)。
  2. 运行生产版本:构建完成后,使用npm start来启动生产服务器。这个命令通常直接运行node dist/server.js。生产环境应确保NODE_ENV环境变量设置为production,这可能会影响一些库的行为(如更少的调试日志)。
  3. 进程管理:在生产环境中,不建议直接通过node命令运行。使用进程管理器如pm2systemd可以保证应用在崩溃后自动重启,并方便日志收集和性能监控。
    # 使用pm2的例子 npm install -g pm2 pm2 start dist/server.js --name "my-mcp-server" pm2 save pm2 startup # 设置开机自启
  4. 环境变量:生产环境的配置(如端口、API密钥、数据库连接)务必通过环境变量或安全的配置文件来管理。绝对不要将敏感信息硬编码在代码中或提交到版本库。模板的ConfigurationManagerprocess.env读取,你需要在部署环境中设置这些变量。

5.3 调试与日志查看

清晰的日志是排查问题的生命线。模板的utils/logger通常配置了不同级别(debug, info, warn, error)的日志。

  • 开发时:将LOG_LEVEL环境变量设为debug,可以看到最详细的日志,包括工具被调用、参数详情、服务内部步骤等。
  • 生产环境:建议将LOG_LEVEL设为infowarn,以减少日志量,同时又能记录关键事件和错误。
  • 日志输出:日志默认输出到控制台。对于生产环境,你可能需要配置日志库(如Winston、Pino)将日志写入文件或发送到日志聚合服务(如ELK、Sentry)。你可以修改utils/logger.ts来实现这些功能。

当工具调用失败时,首先查看服务器的错误日志(error级别)。模板的错误处理机制会将捕获的错误信息记录于此,这通常是定位问题的第一步。

6. 常见问题、故障排查与进阶技巧

6.1 工具注册失败或客户端无法识别

症状:服务器启动正常,但Claude Desktop中看不到新添加的工具。

排查步骤

  1. 检查工具注册:确认你的工具注册函数(如registerGetRecentIssuesTool)确实在src/tools/index.tsregisterTools函数中被调用。这是最容易被忽略的一步。
  2. 检查服务器重启:如果你在开发模式(npm run dev)下,确保文件保存后服务器成功重启了。查看控制台是否有编译错误。有时TypeScript错误不会阻止nodemon重启,但会导致工具注册代码未执行。
  3. 检查MCP客户端配置:确认Claude Desktop的配置文件路径正确,且指向的是最新编译的服务器入口文件(dist/server.js)。修改服务器代码后,需要重新运行npm run build,或者确保开发服务器的stdio路径配置正确。
  4. 重启MCP客户端:Claude Desktop有时会缓存服务器列表。修改服务器配置后,务必完全退出并重启Claude Desktop应用,而不仅仅是刷新对话窗口。
  5. 查看服务器启动日志:服务器启动时,initialize.ts和各个工具注册函数中的logger.debug信息会输出。确认你新工具的成功注册日志出现了。

6.2 工具调用时报参数验证错误

症状:Claude能列出工具,但调用时服务器返回参数错误。

排查步骤

  1. 仔细阅读错误信息:MCP SDK和Zod会返回具体的错误信息,比如“Expected string, received number”或缺少某个必需字段。根据错误信息修正你的请求参数。
  2. 检查Zod模式定义:确认TOOL_PARAMS中的模式定义与你期望的参数完全匹配。特别注意.optional().default()的使用。一个常见的错误是,模式期望一个number,但客户端传入了字符串"5"
  3. 在工具函数内部打印参数:在工具处理函数的开头添加logger.debug('Raw args received:', args),查看实际收到的原始参数是什么,这有助于发现客户端传参和模式定义之间的差异。

6.3 服务逻辑中的网络或API错误

症状:工具调用触发了,但服务器日志显示业务服务(如GithubService)中发生了网络超时、API返回4xx/5xx错误等。

排查步骤

  1. 检查网络连通性:确保运行服务器的机器可以访问目标API(如api.github.com)。如果是公司内网,可能需要配置代理。
  2. 检查API认证与配额:如果使用需要令牌的API(如GitHub Token),确认令牌有效且具有所需权限,并且没有超过速率限制。在代码中妥善处理403 Forbidden429 Too Many Requests等错误。
  3. 实现重试与退避机制:对于不稳定的网络或API,考虑在服务层添加重试逻辑(例如,使用p-retry库)。但要注意幂等性(即重试不会导致重复副作用)。
  4. 完善错误处理:确保服务类中的try-catch块能捕获所有可能的异常,并将其转换为有意义的ServiceError,以便工具层能将其转化为用户友好的MCP错误信息。避免将底层库的原始错误堆栈直接暴露给客户端。

6.4 性能优化与最佳实践

当你的MCP服务器工具越来越多,逻辑越来越复杂时,以下几点可以帮助你保持项目的健壮性:

  1. 配置管理:将所有配置集中到ConfigurationManager。对于敏感信息,使用dotenv.env文件读取(但确保.env.gitignore中),或使用Docker Secrets、云服务商提供的密钥管理服务。
  2. 依赖注入(可选):对于大型项目,可以考虑引入一个轻量级的依赖注入容器(如tsyringe),来管理Service类的实例化,而不是在每个工具中直接new。这便于进行单元测试和模拟。
  3. 单元测试:为services/目录下的核心业务逻辑编写单元测试(使用Jest、Vitest等)。由于服务层不依赖MCP SDK,测试起来相对容易。工具层(tools/)可以编写集成测试,验证参数验证和错误处理流程。
  4. 工具描述清晰化:在定义TOOL_DESCRIPTION和参数的.describe()时,尽可能清晰、具体。好的描述能帮助Claude等AI更准确地理解工具的用途和使用方法,从而在合适的场景下自动调用它。
  5. 资源清理:如果服务中打开了数据库连接、文件句柄或网络连接池,确保在服务器关闭或长时间空闲时能正确释放。虽然Node.js服务器通常长时间运行,但良好的资源管理习惯很重要。

这个custom-mcp-template模板为你铺平了道路,但真正强大的MCP服务器源于你对业务需求的深刻理解和扎实的代码实践。从这个小而美的模板开始,逐步构建起能为你和你的AI助手赋能的高效工具集吧。如果在使用中遇到模板本身的问题,不妨回头仔细阅读其源码和文档,或者参与到项目的社区中,你的实践反馈也可能成为它未来改进的一部分。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/15 3:15:04

基于RAG与向量数据库的PDF智能问答应用开发实战

1. 项目概述&#xff1a;一个融合PDF智能问答的现代化AI聊天应用最近在做一个挺有意思的Side Project&#xff0c;核心目标是把一个纯粹的AI聊天机器人&#xff0c;升级成一个能“读懂”你上传的PDF文件&#xff0c;并基于文件内容进行精准问答的智能助手。这个项目我称之为“C…

作者头像 李华
网站建设 2026/5/15 3:11:11

AI工程化实战指南:从模型原型到生产部署的完整知识体系

1. 项目概述&#xff1a;一个面向AI工程师的实战知识库 最近在GitHub上看到一个挺有意思的仓库&#xff0c;叫“AI-Engineering.academy”。光看名字&#xff0c;你可能会觉得这又是一个堆砌AI论文或者罗列教程链接的收藏夹。但点进去仔细翻翻&#xff0c;你会发现它的定位非常…

作者头像 李华
网站建设 2026/5/15 3:09:49

ARM安全调试与跟踪机制详解

1. ARM安全调试与跟踪机制概述在ARMv8/v9架构的安全扩展中&#xff0c;调试与跟踪机制的设计直接关系到系统的整体安全性。现代处理器需要同时满足开发调试的便利性和生产环境的安全隔离需求&#xff0c;这就对调试子系统提出了精细化的访问控制要求。以MDCR_EL3&#xff08;Mo…

作者头像 李华
网站建设 2026/5/15 3:07:04

终极指南:3秒快速预览Office文档,无需安装完整Office套件

终极指南&#xff1a;3秒快速预览Office文档&#xff0c;无需安装完整Office套件 【免费下载链接】QuickLook.Plugin.OfficeViewer Word, Excel, and PowerPoint plugin for QuickLook. 项目地址: https://gitcode.com/gh_mirrors/qu/QuickLook.Plugin.OfficeViewer 在W…

作者头像 李华
网站建设 2026/5/15 3:03:25

终极指南:3步完成BetterNCM插件安装器配置

终极指南&#xff1a;3步完成BetterNCM插件安装器配置 【免费下载链接】BetterNCM-Installer 一键安装 Better 系软件 项目地址: https://gitcode.com/gh_mirrors/be/BetterNCM-Installer BetterNCM安装器是一款专为网易云音乐PC客户端设计的Rust语言开发工具&#xff0…

作者头像 李华