返回市场
易用MCP

易用MCP

作者:zcaceres184 星标更新:2025-01-20

项目介绍

easy-mcp

easyMCP logo

EasyMCP 已可用但处于测试阶段。请报告您遇到的任何问题。

EasyMCP 是使用 TypeScript 创建模型上下文协议(MCP)服务器的最简单方法。

它隐藏了简单的声明背后的管道、格式化和其他样板定义。

Easy MCP 允许您定义开始所需的最少内容。或者您可以定义更复杂的资源、模板、工具和提示。

功能

  • 类似 Express 的简单 API:EasyMCP 提供了一个高层次且直观的 API。通过类似于在 ExpressJS 中定义端点的方式定义工具、提示、资源、资源模板和根目录。每个可能的可选参数都是可选的,并且除非需要,否则会被隐藏。
  • 实验性装饰器 API:自动推断工具、提示和资源参数。无需定义输入模式!
  • 上下文对象:通过工具中的上下文对象访问 MCP 能力,如日志记录和进度报告。
  • 优秀的类型安全性:更好的开发体验和更少的运行时错误。

测试版限制

  • 尚不支持 MCP 抽样
  • 尚不支持 SSE
  • 尚无资源更新通知
  • 提示技术上接受输入,但 TypeScript SDK 建议它们不能。因此,此功能感觉未完成。

安装

要在您的项目目录中安装 EasyMCP,请运行以下命令:

bun install

使用(实验性)装饰器 API 快速入门

也可以查看 examples/express-decorators.ts 或运行 bun start:decorators

EasyMCP 的装饰器 API 非常简单,并且会自动推断类型和输入配置。

但是它是 实验性的,可能会发生变化或存在尚未发现的问题。

import EasyMCP from "./lib/EasyMCP";
import { Tool, Resource, Prompt } from "./lib/experimental/decorators";

class MyMCP extends EasyMCP {

  @Resource("greeting/{name}")
  getGreeting(name: string) {
    return `你好,${name}!`;
  }

  @Prompt()
  greetingPrompt(name: string) {
    return `生成一个问候语给 ${name}。`;
  }

  @Tool()
  greet(name: string, optionalContextFromServer: Context) {
    optionalContextFromServer.info(`问候 ${name}`);
    return `你好,${name}!`;
  }
}

const mcp = new MyMCP({ version: "1.0.0" });

使用装饰器 API 的复杂示例

查看 examples/express-express.ts 或运行 bun start:express

import EasyMCP from "./lib/EasyMCP";
import { Prompt } from "./lib/decorators/Prompt";
import { Resource } from "./lib/decorators/Resource";
import { Root } from "./lib/decorators/Root";
import { Tool } from "./lib/decorators/Tool";

@Root("/my-sample-dir/photos")
@Root("/my-root-dir", { name: "我的笔记本电脑的根目录" }) // 可以选择命名根目录
class ZachsMCP extends EasyMCP {
  /**
  您可以零配置地声明一个工具。相关的类型和管道将被推断并处理。

  默认情况下,工具的名称将是方法的名称。
  */
  @Tool()
  simpleFunc(nickname: string, height: number) {
    return `${nickname} 的高度是 ${height}`;
  }

  /**
   * 您可以通过可选数据增强工具,例如描述。

   由于 TypeScript 的限制,如果您希望工具将某些输入序列化为客户端的可选输入,您需要提供一个可选项列表。
   */
  @Tool({
    description: "一个可选描述",
    optionals: ["active", "items", "age"],
  })
  middleFunc(name: string, active?: string, items?: string[], age?: number) {
    return `middleFunc 被调用:名字 ${name},活动 ${active},物品 ${items},年龄 ${age}`;
  }

  /**
   * 如果您想要完全控制,还可以为工具的输入参数提供一个模式。
   */
  @Tool({
    description: "具有各种参数类型的函数",
    parameters: [
      {
        name: "date",
        type: "string",
        optional: false,
      },
      {
        name: "season",
        type: "string",
        optional: false,
      },
      {
        name: "year",
        type: "number",
        optional: true,
      },
    ],
  })
  complexTool(date: string, season: string, year?: number) {
    return `complexTool 被调用:日期 ${date},季节 ${season},年份 ${year}`;
  }

  /**
  * 工具可以使用上下文对象来访问 MCP 能力,如日志记录、进度报告和请求元数据。
  */
  @Tool({
    description: "一个使用上下文的工具",
  })
  async processData(dataSource: string, context: Context) {
    context.info(`开始处理来自 ${dataSource} 的数据`);

    try {
      const data = await context.readResource(dataSource);
      context.debug("数据已加载");

      for (let i = 0; i < 5; i++) {
        await new Promise((resolve) => setTimeout(resolve, 1000));
        await context.reportProgress(i * 20, 100);
        context.info(`处理步骤 ${i + 1} 完成`);
      }

      return `处理了来自 ${dataSource} 的 ${data.length} 字节的数据`;
    } catch (error) {
      context.error(`处理数据时出错:${(error as Error).message}`);
      throw error;
    }
  }

  /**
   * 资源可以用简单的 URI 来声明。

   默认情况下,资源的名称将是方法的名称。
   */
  @Resource("simple-resource")
  simpleResource() {
    return "你好,世界!";
  }

  /**
   * 或者包括 Handlebars,EasyMCP 将其视为资源模板。

   资源和资源模板都可以通过可选数据进行配置,例如描述。
   */
  @Resource("greeting/{name}")
  myResourceTemplate(name: string) {
    return `你好,${name}!`;
  }

  /**
   * 默认情况下,提示不需要配置。

   它们将以装饰的方法命名。
   */
  @Prompt()
  simplePrompt(name: string) {
    return `提示... ${name}`;
  }

  /**
   * 或者您可以覆盖并配置一个提示,为其指定名称、描述和显式参数。
   */
  @Prompt({
    name: "configured-prompt",
    description: "具有名称和描述的提示",
    args: [
      {
        name: "name",
        description: "要提示的事物的名称",
        required: true,
      },
    ],
  })
  configuredPrompt(name: string) {
    return `提示... ${name}`;
  }
}

const mcp = new ZachsMCP({ version: "1.0.0" });
console.log(mcp.name, "现在正在服务!");

使用类似 Express 的 API 快速入门

也可以查看 examples/example-minimal.ts 或运行 bun start:express

这个 API 更冗长且不那么神奇,但它更稳定且经过测试。

import EasyMCP from "easy-mcp";

const mcp = EasyMCP.create("my-mcp-server", {
  version: "0.1.0",
});

// 定义一个资源
mcp.resource({
  uri: "dir://desktop",
  name: "桌面目录", // 可选
  description: "列出桌面上的文件", // 可选
  mimeType: "text/plain", // 可选
  fn: async () => {
    return "file://desktop/file1.txt\nfile://desktop/file2.txt";
  },
});

// 定义一个资源模板
mcp.template({
  uriTemplate: "file://{filename}",
  name: "文件模板", // 可选
  description: "访问文件的模板", // 可选
  mimeType: "text/plain", // 可选
  fn: async ({ filename }) => {
    return `文件 ${filename} 的内容`;
  },
});

// 定义一个工具
mcp.tool({
  name: "greet",
  description: "问候一个人", // 可选
  inputs: [ // 可选
    {
      name: "name",
      type: "string",
      description: "要问候的名字",
      required: true,
    },
  ],
  fn: async ({ name }) => {
    return `你好,${name}!`;
  },
});

// 定义一个提示
mcp.prompt({
  name: "introduction",
  description: "生成一个介绍", // 可选
  args: [ // 可选
    {
      name: "name",
      type: "string",
      description: "你的名字",
      required: true,
    },
  ],
  fn: async ({ name }) => {
    return `嗨,我是 ${name}。很高兴见到你!`;
  },
});

// 启动服务器
mcp.serve().catch(console.error);

类似 Express 的 API

EasyMCP.create(name: string, options: ServerOptions)

创建一个新的 EasyMCP 实例。

  • name:您的 MCP 服务器的名称。
  • options:服务器选项,包括版本。

mcp.resource(config: ResourceConfig)

定义一个资源。

mcp.template(config: ResourceTemplateConfig)

定义一个资源模板。

mcp.tool(config: ToolConfig)

定义一个工具。

mcp.prompt(config: PromptConfig)

定义一个提示。

mcp.root(config: Root)

定义一个根目录。

mcp.serve()

启动 MCP 服务器。

(实验性)装饰器 API

EasyMCP 提供了装饰器,以便以更简洁和声明的方式定义您的 MCP 服务器组件。这里是对可用装饰器的概述:

@Tool(config?: ToolConfig)

将方法定义为工具。该方法将接收您声明的任何参数,并根据您的 TS 注解推断类型和输入配置。作为最后一个参数,可以添加一个可选的 context 参数以访问 MCP 能力。

  • config:工具的可选配置对象。
    • description:工具的可选描述。
    • optionals:应标记为可选的参数名称数组。
    • parameters:对输入模式进行全面控制的参数定义数组。

示例:

@Tool({
  description: "问候一个人",
  optionals: ["title"],
})
greet(name: string, title?: string, optionalContext: Context) {
  return `你好,${title ? title + " " : ""}${name}!`;
}

@Resource(uri: string, config?: Partial<ResourceDefinition>)

将方法定义为资源或资源模板。通过在 URI 中使用 Handlebars 定义资源模板。

  • uri:资源的 URI 或 URI 模板。
  • config:资源的可选配置对象。
    • name:资源的可选名称。
    • description:资源的可选描述。
    • mimeType:资源的可选 MIME 类型。

示例:

@Resource("greeting/{name}")
getGreeting(name: string) {
  return `你好,${name}!`;
}

@Prompt(config?: PromptDefinition)

将方法定义为提示。

  • config:提示的可选配置对象。
    • name:提示的可选名称(默认为方法名称)。
    • description:提示的可选描述。
    • args:参数定义数组。

示例:

@Prompt({
  description: "生成一个问候提示",
  args: [
    { name: "name", description: "要问候的名字", required: true },
  ],
})
greetingPrompt(name: string) {
  return `生成一个友好的问候给 ${name}。`;
}

@Root(uri: string, config?: { name?: string })

定义 MCP 服务器的根目录。此装饰器应用于类,而不是方法。

  • uri:根目录的 URI。
  • config:可选配置对象。
    • name:根目录的可选名称。

示例:

@Root("/my-sample-dir/photos")
@Root("/my-root-dir", { name: "我的笔记本电脑的根目录" })
class MyMCP extends EasyMCP {
  // ...
}

使用装饰器时,EasyMCP 会自动推断类型并为您的工具、资源、提示和根目录创建适当的配置。这可以显著减少样板代码并使您的 MCP 服务器定义更加简洁。

但是……装饰器 API 是实验性的,可能存在错误或意外变化。

贡献

欢迎贡献!只需提交 PR。

许可证

本项目采用 MIT 许可证。

致谢

EasyMCP 由 Zach Caceres 创造,灵感来源于 kjlowin 的 FastMCP,这是一个用于 Python MCP 服务器的库。