不会写Mcp server,那就让Trae写吧

前言

在上一篇文章中,Trae帮助我们深入理解了MCP(Model Context Protocol)是什么,以及MCP标准输入输出的格式。MCP是一种轻量级的通信协议,通过标准输入输出(stdin/stdout)进行数据交换,使用JSON格式来传递消息。

这种协议非常适合构建微服务,因为它简单、高效,且易于实现。

在本文中,我们将进一步深入,使用官方SDK来创建一个支持文件夹增删改查功能的MCP服务。我们将通过一个具体的实例,展示如何从零开始构建这样一个服务,并确保它能够与MCP协议无缝对接。

首先先让Trae使用官方的sdk帮我创建一个可以crud文件夹的mcp服务,使用nodejs来编写这个Mcp服务

可以看到所谓的官方的sdk是这个@modelcontextprotocol/sdk,如果对这个不了解的话,可以自己去npm上面看,这里可以简单的理解为,有了这个官方的sdk,我们的mcp就可以做很多事情

2.1 初始化项目

首先,我们需要创建一个新的Node.js项目并初始化。

Trae在你的工作目录中运行以下命令:

bash 复制代码
mkdir folder-mcp--server
cd folder-mcp--server
npm init -y

这将创建一个名为folder-mcp--server的文件夹,并在其中初始化一个新的Node.js项目。

2.2 安装官方SDK

接下来,我们需要安装@modelcontextprotocol/sdk

bash 复制代码
npm install @modelcontextprotocol/sdk

安装完成后,你可以在项目的node_modules文件夹中找到@modelcontextprotocol/sdk

2.3 编写服务代码

Trae在项目src目录中创建一个名为index.js的文件,并添加以下代码:

javascript 复制代码
#!/usr/bin/env node
  
import { Server } from '@modelcontextprotocol/sdk/server/index.js';

import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';

import {

  CallToolRequestSchema,

  ListToolsRequestSchema,

} from '@modelcontextprotocol/sdk/types.js';

import { promises as fs, constants as fsConstants } from 'fs';

import path from 'path';

import { fileURLToPath } from 'url';

  

创建一个mcp服务

css 复制代码
const server = new Server(
  {
    name: 'folder-mcp-server',
    version: '1.0.0',
  },
  {
    capabilities: {
      tools: {},
    },
  }
);

辅助函数:检查路径是否存在

typescript 复制代码
async function pathExists(targetPath: string): Promise<boolean> {
  try {
    await fs.access(targetPath);
    return true;
  } catch {
    return false;
  }
}

辅助函数:计算文件夹大小

typescript 复制代码
async function getFolderSize(folderPath: string): Promise<number> {
  let totalSize = 0;
  try {
    const stats = await fs.stat(folderPath);
    if (!stats.isDirectory()) {
      return stats.size;
    }
  
    const items = await fs.readdir(folderPath, { withFileTypes: true });
    for (const item of items) {
      const itemPath = path.join(folderPath, item.name);
      if (item.isDirectory()) {
        totalSize += await getFolderSize(itemPath);
      } else {
        const itemStats = await fs.stat(itemPath);
        totalSize += itemStats.size;
      }
    }
  } catch (error) {
    console.error(`Error calculating folder size: ${error}`);
  }
  return totalSize;
}

辅助函数:格式化文件大小

ini 复制代码
function formatBytes(bytes: number, decimals = 2): string {
  if (bytes === 0) return '0 Bytes';
  const k = 1024;
  const dm = decimals < 0 ? 0 : decimals;
  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}

辅助函数:递归复制文件夹

csharp 复制代码
async function copyFolder(source: string, destination: string): Promise<void> {
  await fs.mkdir(destination, { recursive: true });
  const items = await fs.readdir(source, { withFileTypes: true });
  for (const item of items) {
    const sourcePath = path.join(source, item.name);
    const destinationPath = path.join(destination, item.name);
    if (item.isDirectory()) {
      await copyFolder(sourcePath, destinationPath);
    } else {
      await fs.copyFile(sourcePath, destinationPath);
    }
  }
}  

注册工具

php 复制代码
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: 'create_folder',
        description: '创建新文件夹',
        inputSchema: {
          type: 'object',
          properties: {
            path: {
              type: 'string',
              description: '要创建的文件夹路径',
            },
            recursive: {
              type: 'boolean',
              description: '是否递归创建父目录',
              default: true,
            },
          },
          required: ['path'],
        },
      },
      {
        name: 'delete_folder',
        description: '删除文件夹',
        inputSchema: {
          type: 'object',
          properties: {
            path: {
              type: 'string',
              description: '要删除的文件夹路径',
            },
            force: {
              type: 'boolean',
              description: '是否强制删除非空文件夹',
              default: false,
            },
          },
          required: ['path'],
        },
      },
      {
        name: 'rename_folder',
        description: '重命名或移动文件夹',
        inputSchema: {
          type: 'object',
          properties: {
            oldPath: {
              type: 'string',
              description: '原文件夹路径',
            },
            newPath: {
              type: 'string',
              description: '新文件夹路径',
            },
          },
          required: ['oldPath', 'newPath'],
        },
      },
      {
        name: 'list_folder',
        description: '列出文件夹内容',
        inputSchema: {
          type: 'object',
          properties: {
            path: {
              type: 'string',
              description: '要列出的文件夹路径',
            },
            recursive: {
              type: 'boolean',
              description: '是否递归列出子文件夹内容',
              default: false,
            },
          },
          required: ['path'],
        },
      },
  }

});

启动服务器的方法

javascript 复制代码
async function main() {

  const transport = new StdioServerTransport();

  await server.connect(transport);

  console.error('文件夹管理MCP服务器已启动');

}

  


main().catch((error) => {

  console.error('服务器启动失败:', error);

  process.exit(1);

});

然后让Trae帮我们创建一个可以测试的js脚本

执行node simple-test.js ,可以看到控制台,输出打印和文件夹创建test-demo文件夹

执行一下删除文件夹,把刚刚新建的也删除了

相关推荐
sinat_267611911 天前
Trae AI 进行 Android 从0 到 1的一键开发
kotlin·android studio·trae
阆遤2 天前
利用TRAE对nanobot进行安全分析并优化
python·安全·ai·trae·nanobot
Molesidy2 天前
【VSCode】VSCode或者Trae的扩展文件夹以及用户设置文件夹的路径更改到指定位置
ide·编辑器·trae
yosh'joy!!2 天前
下载Trae使用
ai·trae
豆包MarsCode3 天前
只需一个指令,让 OpenClaw 安排 TRAE 干活
trae
sugar15693 天前
Trae快速构建自己项目的docker镜像
docker·容器·trae
sugar15693 天前
Trae 添加项目规则,快速完成crmeb项目本地开发环境搭建
docker·容器·trae
欧简墨4 天前
kotlin Android Extensions插件迁移到viewbinding总结
android·trae
arbboter4 天前
【AI编程】约束即设计:AI时代的人机边界重构
ai编程·ai工作流·人机协作·trae·声明式执行·流程编排
进击的雷神6 天前
Trae AI IDE 完全指南:从入门到精通
大数据·ide·人工智能·trae