mcpField
E
easy-mcp

easy-mcp

本地服务
github.com/zcaceres开发工具19792 周前更新

关于 easy-mcp

easy-mcp 是一个用 TypeScript 创建 MCP 服务的框架,通过类似 Express 的简单声明隐藏协议细节与样板代码。它适合希望快速搭建 MCP 服务的开发者,并提供装饰器 API 以自动推断参数类型。项目目前处于 beta 阶段。

主要功能

  1. 01类似 Express 的简洁 API
  2. 02实验性装饰器 API,自动推断参数
  3. 03通过上下文对象进行日志与进度报告
  4. 04基于 TypeScript 的类型安全

适用场景

  1. 01快速搭建包含工具与资源的 MCP 服务
  2. 02使用装饰器定义无需手写输入 schema 的工具
  3. 03为 MCP 服务定义资源模板与 Roots

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

暂未收录安装方式

我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。

代码仓库

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

easy-mcp

easyMCP 标志

EasyMCP 可以使用,但目前处于测试阶段。请报告你遇到的任何问题。

EasyMCP 是使用 TypeScript 创建 Model Context Protocol(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 `Hello, ${name}!`;
  }

  @Prompt()
  greetingPrompt(name: string) {
    return `Generate a greeting for ${name}.`;
  }

  @Tool()
  greet(name: string, optionalContextFromServer: Context) {
    optionalContextFromServer.info(`Greeting ${name}`);
    return `Hello, ${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: "My laptop's root directory" }) // Optionally you can name the root
class ZachsMCP extends EasyMCP {
  /**
  You can declare a Tool with zero configuration. Relevant types and plumbing will be inferred and handled.

  By default, the name of the Tool will be the name of the method.
  */
  @Tool()
  simpleFunc(nickname: string, height: number) {
    return `${nickname} of ${height} height`;
  }

  /**
   * You can enhance a tool with optional data like a description.

   Due to limitations in Typescript, if you want the Tool to serialize certain inputs as optional to the Client, you need to provide an optionals list.
   */
  @Tool({
    description: "An optional description",
    optionals: ["active", "items", "age"],
  })
  middleFunc(name: string, active?: string, items?: string[], age?: number) {
    return `exampleFunc called: name ${name}, active ${active}, items ${items}, age ${age}`;
  }

  /**
   * You can also provide a schema for the input arguments of a tool, if you want full control.
   */
  @Tool({
    description: "A function with various parameter types",
    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 called: date ${date}, season ${season}, year ${year}`;
  }

  /**
  * Tools can use a context object to access MCP capabilities like logging, progress reporting, and meta data from the request
  */
  @Tool({
    description: "A tool that uses context",
  })
  async processData(dataSource: string, context: Context) {
    context.info(`Starting to process data from ${dataSource}`);

    try {
      const data = await context.readResource(dataSource);
      context.debug("Data loaded");

      for (let i = 0; i < 5; i++) {
        await new Promise((resolve) => setTimeout(resolve, 1000));
        await context.reportProgress(i * 20, 100);
        context.info(`Processing step ${i + 1} complete`);
      }

      return `Processed ${data.length} bytes of data from ${dataSource}`;
    } catch (error) {
      context.error(`Error processing data: ${(error as Error).message}`);
      throw error;
    }
  }

  /**
   * Resources can be declared with a simple URI.

   By default, the name of the resource will be the name of the method.
   */
  @Resource("simple-resource")
  simpleResource() {
    return "Hello, world!";
  }

  /**
   * Or include handlebars which EasyMCP will treat as a Resource Template.

   Both Resources and Resource Templates can be configured with optional data like a description.
   */
  @Resource("greeting/{name}")
  myResourceTemplate(name: string) {
    return `Hello, ${name}!`;
  }

  /**
   * By default, prompts need no configuration.

   They will be named after the method they decorate.
   */
  @Prompt()
  simplePrompt(name: string) {
    return `Prompting... ${name}`;
  }

  /**
   * Or you can override and configure a Prompt with a name, description, and explicit arguments.
   */
  @Prompt({
    name: "configured-prompt",
    description: "A prompt with a name and description",
    args: [
      {
        name: "name",
        description: "The name of the thing to prompt",
        required: true,
      },
    ],
  })
  configuredPrompt(name: string) {
    return `Prompting... ${name}`;
  }
}

const mcp = new ZachsMCP({ version: "1.0.0" });
console.log(mcp.name, "is now serving!");

使用 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",
});

// Define a resource
mcp.resource({
  uri: "dir://desktop",
  name: "Desktop Directory", // Optional
  description: "Lists files on the desktop", // Optional
  mimeType: "text/plain", // Optional
  fn: async () => {
    return "file://desktop/file1.txt\nfile://desktop/file2.txt";
  },
});

// Define a resource template
mcp.template({
  uriTemplate: "file://{filename}",
  name: "File Template", // Optional
  description: "Template for accessing files", // Optional
  mimeType: "text/plain", // Optional
  fn: async ({ filename }) => {
    return `Contents of ${filename}`;
  },
});

// Define a tool
mcp.tool({
  name: "greet",
  description: "Greets a person", // Optional
  inputs: [ // Optional
    {
      name: "name",
      type: "string",
      description: "The name to greet",
      required: true,
    },
  ],
  fn: async ({ name }) => {
    return `Hello, ${name}!`;
  },
});

// Define a prompt
mcp.prompt({
  name: "introduction",
  description: "Generates an introduction", // Optional
  args: [ // Optional
    {
      name: "name",
      type: "string",
      description: "Your name",
      required: true,
    },
  ],
  fn: async ({ name }) => {
    return `Hi there! My name is ${name}. It's nice to meet you!`;
  },
});

// Start the server
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: "Greets a person",
  optionals: ["title"],
})
greet(name: string, title?: string, optionalContext: Context) {
  return `Hello, ${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 `Hello, ${name}!`;
}
@Prompt(config?: PromptDefinition)

将方法定义为提示词。

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

示例:

@Prompt({
  description: "Generates a greeting prompt",
  args: [
    { name: "name", description: "Name to greet", required: true },
  ],
})
greetingPrompt(name: string) {
  return `Generate a friendly greeting for ${name}.`;
}
@Root(uri: string, config?: { name?: string })

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

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

示例:

@Root("/my-sample-dir/photos")
@Root("/my-root-dir", { name: "My laptop's root directory" })
class MyMCP extends EasyMCP {
  // ...
}

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

不过……装饰器 API 仍处于实验阶段,可能存在错误或发生意料之外的变更。

贡献

欢迎贡献!只需提交 PR。

许可证

本项目采用 MIT 许可证。

致谢

EasyMCP 由 Zach Caceres 创建,灵感则来自 kjlowin 开发的 FastMCP,这是一个用于 Python MCP 服务的库。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。