关于 easy-mcp
easy-mcp 是一个用 TypeScript 创建 MCP 服务的框架,通过类似 Express 的简单声明隐藏协议细节与样板代码。它适合希望快速搭建 MCP 服务的开发者,并提供装饰器 API 以自动推断参数类型。项目目前处于 beta 阶段。
主要功能
- 01类似 Express 的简洁 API
- 02实验性装饰器 API,自动推断参数
- 03通过上下文对象进行日志与进度报告
- 04基于 TypeScript 的类型安全
适用场景
- 01快速搭建包含工具与资源的 MCP 服务
- 02使用装饰器定义无需手写输入 schema 的工具
- 03为 MCP 服务定义资源模板与 Roots
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
easy-mcp

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