基于TypeScript开发MCP工具及Skills自定义开发指南
在现代前端工程化与工具链体系中,MCP(Module Control Panel/模块控制面板)工具是提升开发效率、规范模块管理的核心组件,常用于项目模块的配置、调试、监控等场景。而Skills作为MCP工具的扩展能力载体,能够让工具适配不同业务场景的个性化需求。本文将从环境搭建、核心功能实现、Skills自定义开发三个维度,详细讲解如何基于TypeScript(以下简称TS)开发一款可扩展、高可维护的MCP工具,适合有TS基础、希望搭建自定义工具链的开发者参考。
一、前置知识与环境准备
在开始开发前,需明确核心技术栈与环境依赖,确保开发过程顺畅。本文开发的MCP工具将基于TS实现,结合Node.js提供后端能力(用于处理本地文件、接口请求),搭配Electron实现桌面端交互(可选,也可仅开发Web端),Skills自定义则基于TS的接口封装与插件化思想。
1.1 核心技术栈
-
TypeScript:核心开发语言,提供类型校验、面向对象特性,提升代码可维护性与可读性;
-
Node.js:用于处理文件操作、本地服务、依赖管理,适配MCP工具的本地模块管理需求;
-
Electron(可选):用于打包桌面端应用,实现跨平台(Windows、Mac、Linux)运行;
-
ESModule:模块规范,替代CommonJS,提升模块兼容性与Tree-Shaking能力;
-
Prettier+ESLint:代码格式化与语法校验,规范TS代码风格。
1.2 环境搭建步骤
-
初始化项目:创建项目目录,执行
npm init -y初始化package.json,配置type为module(支持ESModule); -
安装依赖:核心依赖包括typescript、@types/node(TS类型声明)、ts-node(TS即时运行),可选依赖electron、electron-builder(打包);
`npm install typescript @types/node ts-node --save-dev
可选:安装Electron相关依赖
npm install electron electron-builder --save-dev`
-
配置tsconfig.json:核心配置如下,确保TS编译符合项目需求:
{ "compilerOptions": { "target": "ES2020", // 目标ES版本 "module": "ESNext", // 模块规范 "outDir": "./dist", // 编译输出目录 "rootDir": "./src", // 源码目录 "strict": true, // 开启严格模式 "esModuleInterop": true, // 兼容CommonJS模块 "skipLibCheck": true, // 跳过第三方库类型校验 "forceConsistentCasingInFileNames": true // 强制文件名大小写一致 }, "include": ["src/**/*"], // 需编译的文件 "exclude": ["node_modules", "dist"] // 排除文件 } -
配置脚本:在package.json中添加编译、运行脚本,方便开发调试:
"scripts": { "dev": "ts-node src/index.ts", // 开发环境运行 "build": "tsc", // TS编译 "electron:dev": "electron .", // Electron开发模式(可选) "electron:build": "electron-builder" // 打包桌面端(可选) }
二、基于TS开发MCP工具核心功能
MCP工具的核心定位是"模块管理中枢",本文以"本地项目模块控制面板"为例,实现模块列表展示、模块新增/删除/编辑、模块状态监控三个核心功能,后续可基于此扩展更多能力(如接口调试、日志查看)。
2.1 项目目录结构设计
合理的目录结构是提升代码可维护性的关键,基于TS的面向对象思想,采用模块化拆分,目录结构如下:
plaintext
src/
├── core/ // 核心逻辑层(MCP工具核心能力)
│ ├── MCP.ts // MCP主类(管理所有模块与Skills)
│ ├── Module.ts // 模块类(定义模块属性与方法)
│ └── Logger.ts // 日志工具(用于模块状态监控)
├── skills/ // Skills扩展层(自定义技能实现)
│ ├── base.ts // Skills基础接口(规范所有Skills)
│ └── example.ts // 示例Skill(如模块导出功能)
├── utils/ // 工具函数层
│ ├── file.ts // 文件操作工具(读写模块配置)
│ └── type.ts // TS类型定义(公共类型)
└── index.ts // 入口文件(初始化MCP工具)
2.2 核心类实现(TS面向对象)
MCP工具的核心是MCP主类与Module模块类,通过类的封装实现模块的统一管理,结合TS类型校验,避免类型错误。
2.2.1 类型定义(utils/type.ts)
先定义公共类型,确保所有核心逻辑的类型统一:
typescript
// 模块状态枚举
export enum ModuleStatus {
ACTIVE = "active", // 激活状态
INACTIVE = "inactive", // 未激活
ERROR = "error" // 异常状态
}
// 模块类型定义
export interface ModuleOptions {
id: string; // 模块唯一ID
name: string; // 模块名称
desc: string; // 模块描述
path: string; // 模块本地路径
status: ModuleStatus; // 模块状态
}
// Skills基础类型(后续自定义Skills需实现此接口)
export interface Skill {
id: string;
name: string;
execute: (module?: ModuleOptions) => Promise<void>; // 执行方法
}
// MCP配置类型
export interface MCPConfig {
modules: ModuleOptions[]; // 模块列表
activeSkills: string[]; // 激活的Skills
}
2.2.2 模块类(core/Module.ts)
封装模块的属性与方法,负责单个模块的状态管理、信息更新:
typescript
import { ModuleOptions, ModuleStatus } from "../utils/type";
import Logger from "./Logger";
class Module {
private options: ModuleOptions;
private logger: Logger;
constructor(options: ModuleOptions) {
// TS类型校验:确保传入的options符合ModuleOptions类型
this.options = {
id: options.id,
name: options.name,
desc: options.desc || "",
path: options.path,
status: options.status || ModuleStatus.INACTIVE
};
this.logger = new Logger(`Module[${this.options.name}]`);
}
// 获取模块信息
getInfo(): ModuleOptions {
return { ...this.options }; // 浅拷贝,避免直接修改原对象
}
// 激活模块
activate(): void {
this.options.status = ModuleStatus.ACTIVE;
this.logger.info(`模块激活成功,路径:${this.options.path}`);
}
// 禁用模块
deactivate(): void {
this.options.status = ModuleStatus.INACTIVE;
this.logger.info("模块禁用成功");
}
// 更新模块信息
update(options: Partial<ModuleOptions>): void {
this.options = { ...this.options, ...options };
this.logger.info("模块信息更新成功");
}
// 校验模块路径是否存在(结合Node.js文件操作)
async checkPath(): Promise<boolean> {
const fs = await import("fs/promises");
try {
await fs.access(this.options.path);
return true;
} catch (err) {
this.options.status = ModuleStatus.ERROR;
this.logger.error(`模块路径不存在:${this.options.path}`);
return false;
}
}
}
export default Module;
2.2.3 MCP主类(core/MCP.ts)
MCP主类是工具的核心中枢,负责管理所有模块、加载Skills、提供对外API:
typescript
import { MCPConfig, ModuleOptions, Skill, ModuleStatus } from "../utils/type";
import Module from "./Module";
import Logger from "./Logger";
import { readConfig, writeConfig } from "../utils/file";
class MCP {
private config: MCPConfig;
private modules: Map<string, Module>; // 用Map存储模块,方便按ID查询
private skills: Map<string, Skill>;
private logger: Logger;
constructor() {
this.logger = new Logger("MCP");
this.config = { modules: [], activeSkills: [] };
this.modules = new Map();
this.skills = new Map();
// 初始化:读取本地配置文件
this.initConfig();
}
// 初始化配置(从本地文件读取)
private async initConfig(): Promise<void> {
try {
const config = await readConfig();
if (config) {
this.config = config;
// 加载模块
this.config.modules.forEach((moduleOpt) => {
this.addModule(moduleOpt);
});
this.logger.info("配置初始化成功");
}
} catch (err) {
this.logger.error("配置初始化失败,使用默认配置", err);
}
}
// 新增模块
addModule(options: ModuleOptions): Module {
if (this.modules.has(options.id)) {
this.logger.warn(`模块已存在:${options.id}`);
return this.modules.get(options.id)!;
}
const module = new Module(options);
this.modules.set(options.id, module);
this.config.modules.push(options);
// 保存配置到本地
this.saveConfig();
this.logger.info(`新增模块:${options.name}`);
return module;
}
// 删除模块
deleteModule(moduleId: string): boolean {
if (!this.modules.has(moduleId)) {
this.logger.warn(`模块不存在:${moduleId}`);
return false;
}
this.modules.delete(moduleId);
this.config.modules = this.config.modules.filter((m) => m.id !== moduleId);
this.saveConfig();
this.logger.info(`删除模块:${moduleId}`);
return true;
}
// 获取所有模块列表
getModules(): ModuleOptions[] {
return Array.from(this.modules.values()).map((module) => module.getInfo());
}
// 加载Skill(支持动态加载)
loadSkill(skill: Skill): void {
if (this.skills.has(skill.id)) {
this.logger.warn(`Skill已存在:${skill.id}`);
return;
}
this.skills.set(skill.id, skill);
// 自动激活新增Skill
this.config.activeSkills.push(skill.id);
this.saveConfig();
this.logger.info(`加载Skill:${skill.name}`);
}
// 执行Skill(支持传入指定模块)
async executeSkill(skillId: string, moduleId?: string): Promise<void> {
const skill = this.skills.get(skillId);
if (!skill) {
this.logger.error(`Skill不存在:${skillId}`);
throw new Error(`Skill ${skillId} not found`);
}
const module = moduleId ? this.modules.get(moduleId) : undefined;
this.logger.info(`执行Skill:${skill.name},模块:${module?.getInfo().name || "无"}`);
await skill.execute(module?.getInfo());
}
// 保存配置到本地文件
private async saveConfig(): Promise<void> {
await writeConfig(this.config);
}
}
export default MCP;
2.2.4 工具函数实现(utils/file.ts)
实现本地配置文件的读写操作,基于Node.js的fs模块,封装成Promise方法,适配TS的异步编程:
typescript
import { MCPConfig } from "./type";
import fs from "fs/promises";
import path from "path";
// 配置文件路径(本地项目根目录下的mcp.config.json)
const CONFIG_PATH = path.resolve(process.cwd(), "mcp.config.json");
// 读取配置
export async function readConfig(): Promise<MCPConfig | null> {
try {
const data = await fs.readFile(CONFIG_PATH, "utf-8");
return JSON.parse(data) as MCPConfig;
} catch (err) {
// 配置文件不存在时,返回null,由MCP主类使用默认配置
return null;
}
}
// 写入配置
export async function writeConfig(config: MCPConfig): Promise<void> {
await fs.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
}
// 检查文件是否存在
export async function fileExists(filePath: string): Promise<boolean> {
try {
await fs.access(filePath);
return true;
} catch (err) {
return false;
}
}
2.3 入口文件(index.ts)
初始化MCP工具,演示核心功能的使用,可根据需求扩展为Web端或桌面端入口:
typescript
import MCP from "./core/MCP";
import { ModuleStatus } from "./utils/type";
import ExampleSkill from "./skills/example";
// 初始化MCP工具
const mcp = new MCP();
// 示例:新增模块
const testModule = mcp.addModule({
id: "test-001",
name: "测试模块",
desc: "MCP工具测试模块",
path: "./src/modules/test",
status: ModuleStatus.INACTIVE
});
// 示例:激活模块
testModule.activate();
// 示例:加载并执行Skill
mcp.loadSkill(ExampleSkill);
mcp.executeSkill("example-skill", "test-001").catch((err) => {
console.error("Skill执行失败:", err);
});
// 示例:打印所有模块信息
console.log("所有模块列表:", mcp.getModules());
三、Skills自定义开发(基于TS)
Skills是MCP工具的扩展能力,本质是"可复用的功能插件",通过实现统一的Skill接口,可快速扩展MCP工具的能力(如模块导出、日志导出、接口调试等)。本节将讲解Skills的开发规范、示例实现与动态加载方法。
3.1 Skills开发规范(基于TS接口)
所有自定义Skills必须实现utils/type.ts中定义的Skill接口,确保接口统一,可被MCP主类正常加载和执行。核心规范如下:
-
必须包含
id(唯一标识,建议使用"功能-模块"格式,如example-skill); -
必须包含
name(Skill名称,用于展示和日志输出); -
必须实现
execute方法(核心执行逻辑,支持异步,可接收单个模块参数); -
Skill逻辑需独立封装,不依赖MCP主类的内部实现,降低耦合度;
-
建议使用TS泛型,适配不同类型的模块参数(可选)。
3.2 示例Skill开发(导出模块配置)
以"模块配置导出"为例,实现一个自定义Skill,将指定模块的配置导出为JSON文件:
typescript
// skills/example.ts
import { Skill, ModuleOptions } from "../utils/type";
import { writeFile } from "../utils/file";
import path from "path";
// 实现Skill接口
const ExampleSkill: Skill = {
id: "example-skill",
name: "模块配置导出",
async execute(module?: ModuleOptions) {
if (!module) {
throw new Error("请指定需要导出的模块");
}
// 导出路径:模块路径下的module.config.json
const exportPath = path.resolve(module.path, "module.config.json");
// 写入模块配置
await writeFile(exportPath, JSON.stringify(module, null, 2));
console.log(`模块配置导出成功:${exportPath}`);
}
};
export default ExampleSkill;
3.3 Skills进阶开发:带参数的Skill
如需实现带自定义参数的Skill(如指定导出格式、接口地址等),可通过扩展Skill接口,使用TS泛型实现参数校验:
typescript
// utils/type.ts 扩展接口
export interface SkillWithParams<T = unknown> extends Skill {
params?: T; // 自定义参数
}
// skills/export-skill.ts(带参数的导出Skill)
import { SkillWithParams, ModuleOptions } from "../utils/type";
import { writeFile } from "../utils/file";
import path from "path";
// 自定义参数类型
interface ExportParams {
format: "json" | "txt"; // 导出格式
fileName?: string; // 自定义文件名
}
// 实现带参数的Skill
const ExportSkill: SkillWithParams<ExportParams> = {
id: "export-skill",
name: "带参数模块导出",
params: { format: "json", fileName: "module-config" }, // 默认参数
async execute(module?: ModuleOptions) {
if (!module) {
throw new Error("请指定需要导出的模块");
}
// 获取自定义参数
const { format, fileName } = this.params || { format: "json", fileName: "module-config" };
const exportPath = path.resolve(module.path, `${fileName}.${format}`);
// 根据格式导出
let content = "";
if (format === "json") {
content = JSON.stringify(module, null, 2);
} else if (format === "txt") {
content = `模块ID:${module.id}\n模块名称:${module.name}\n模块路径:${module.path}\n模块状态:${module.status}`;
}
await writeFile(exportPath, content);
console.log(`模块导出成功(格式:${format}):${exportPath}`);
}
};
export default ExportSkill;
3.4 Skills动态加载与管理
MCP工具支持动态加载Skills,无需修改核心代码,只需将自定义Skill放入skills目录,通过loadSkill方法加载即可。此外,还可实现以下进阶能力:
-
批量加载:遍历skills目录,自动加载所有符合规范的Skill;
-
Skill激活/禁用:在MCP配置中维护activeSkills列表,执行Skill前校验是否激活;
-
Skill卸载:新增
unloadSkill方法,从skills Map中删除指定Skill; -
参数配置:通过MCP工具的配置界面,动态修改Skill的自定义参数(需结合前端界面实现)。
四、调试与打包(可选)
4.1 开发调试
执行npm run dev,通过ts-node运行入口文件,查看控制台输出,验证模块管理、Skill执行等功能是否正常。如需调试TS代码,可在VS Code中配置launch.json,支持断点调试。
4.2 桌面端打包(Electron)
若需将MCP工具打包为桌面端应用,需配置Electron的main.js(入口文件),并修改package.json的打包配置:
javascript
// main.js(Electron入口)
import { app, BrowserWindow } from "electron";
import path from "path";
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 1000,
height: 600,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
nodeIntegration: true // 允许渲染进程使用Node.js API
}
});
// 加载前端页面(可结合React/Vue开发界面)
mainWindow.loadFile(path.join(__dirname, "../index.html"));
// 打开开发者工具
mainWindow.webContents.openDevTools();
}
app.whenReady().then(createWindow);
// 关闭窗口事件
app.on("window-all-closed", () => {
if (process.platform !== "darwin") app.quit();
});
执行npm run electron:build,即可生成对应系统的桌面端安装包(Windows为.exe,Mac为.dmg,Linux为.deb)。
五、总结与扩展方向
本文基于TypeScript实现了一款基础的MCP工具,核心在于通过TS的面向对象特性与类型校验,封装模块管理与Skills扩展能力,确保工具的可维护性与可扩展性。通过本文的开发流程,你可以快速搭建属于自己的MCP工具,并根据业务需求自定义Skills。
5.1 核心亮点
-
TS类型校验:全程使用TS类型定义,避免类型错误,提升代码可读性;
-
插件化设计:Skills基于接口封装,支持动态加载、卸载,扩展灵活;
-
跨平台适配:支持Web端与桌面端(Electron),适配不同使用场景;
-
规范可扩展:目录结构与代码规范清晰,便于后续功能迭代。
5.2 扩展方向
-
前端界面:结合React/Vue开发可视化界面,实现模块管理、Skill配置的可视化操作;
-
接口集成:添加HTTP请求工具,实现远程模块管理、接口调试功能;
-
日志系统:扩展Logger类,支持日志分级、日志导出、日志可视化;
-
权限管理:添加用户权限控制,区分管理员与普通用户的操作权限;
-
插件市场:实现Skills的在线下载、更新,打造MCP工具的插件生态。
