不会写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文件夹

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

相关推荐
Goboy2 小时前
消消乐游戏:Trae 轻松实现色彩缤纷的消除乐趣
ai编程·trae
Goboy2 小时前
纸牌接龙:Trae 轻松实现经典纸牌挑战
ai编程·trae
liang_jy4 小时前
数组(Array)
数据结构·面试·trae
CF14年老兵5 小时前
「Vue 3 + View Transition 实现炫酷圆形缩放换肤动画」
前端·css·trae
bug菌5 小时前
还在为Java集成AI应用而头疼?Trae让你3分钟从小白变大神!
aigc·ai编程·trae
bug菌6 小时前
还在为Java Web后端开发而码字到深夜?Trae让你秒变全栈大神!
aigc·ai编程·trae
石小石Orz8 小时前
效率提升一倍!谈谈我的高效开发工具链
前端·后端·trae
围巾哥萧尘9 小时前
Mailbox | 围巾哥萧尘 | 使用 TRAE SOLO 创建高级暗模式电子邮件应用🧣
trae
豆包MarsCode9 小时前
项目笔记|Dogtor 颈椎健康插件开发全记录
trae