Maintainers
Pricing
Enterprise
More
More
Documentation
Try Live
Rankings
Install
Blog
Docs7
Ask
New
Add Docs
Easy MCP
https://github.com/zcaceres/easy-mcp
Admin
Absurdly easy Model Context Protocol Servers in Typescript
Tokens:
3,942
Snippets:
10
Trust Score:
9.7
License:
MIT
Update:
1 year ago
Context
Chat
Benchmark
Latest
Show doc for...
Code
Info
Show Results
Tokens:
Raw
Copy
Link
Tokens:
Raw
Copy
Link
### Installing EasyMCP (bash) Source: https://github.com/zcaceres/easy-mcp/blob/main/README.md Command to install the EasyMCP library using the Bun package manager. This is the standard method for adding EasyMCP as a dependency to your project. ```bash bun install ``` -------------------------------- ### Initializing and Defining Components with EasyMCP Express-like API (TypeScript) Source: https://github.com/zcaceres/easy-mcp/blob/main/README.md This snippet demonstrates the quick start process for setting up an EasyMCP server using the Express-like API. It shows how to create a server instance, define a static resource, a resource template, a tool with inputs, and a prompt with arguments, finally starting the server. ```typescript 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); ``` -------------------------------- ### Defining Tool with EasyMCP Decorator API (@Tool) (TypeScript) Source: https://github.com/zcaceres/easy-mcp/blob/main/README.md This example illustrates how to define an MCP tool using the @Tool decorator on a class method. It shows how method parameters like `name` and `title` become tool inputs, how to mark parameters as optional using `optionals`, and how to access context. ```typescript @Tool({ description: "Greets a person", optionals: ["title"], }) greet(name: string, title?: string, optionalContext: Context) { return `Hello, ${title ? title + " " : ""}${name}!`; } ``` -------------------------------- ### Defining Prompt with EasyMCP Decorator API (@Prompt) (TypeScript) Source: https://github.com/zcaceres/easy-mcp/blob/main/README.md This example shows how to define an MCP prompt using the @Prompt decorator on a class method. It illustrates how method parameters like `name` become prompt arguments and how to provide detailed argument definitions using the `args` option in the decorator config. ```typescript @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}.`; } ``` -------------------------------- ### Configuring Tool with Description and Optionals - TypeScript Source: https://github.com/zcaceres/easy-mcp/blob/main/README.md Shows how to provide a description for a tool and explicitly list optional parameters using the `optionals` array. This is necessary for TypeScript to correctly serialize optional inputs for the client. ```typescript 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 ``` -------------------------------- ### Defining MCP Resources, Prompts, and Tools with Decorators (TypeScript) Source: https://github.com/zcaceres/easy-mcp/blob/main/README.md Demonstrates how to define MCP components (Resource, Prompt, Tool) as class methods using experimental decorators in TypeScript. It shows basic usage, including accessing the context object within a tool method. ```typescript 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" }); ``` -------------------------------- ### Defining Simple Tool - TypeScript Source: https://github.com/zcaceres/easy-mcp/blob/main/README.md Demonstrates the basic usage of the `@Tool` decorator with zero configuration. The tool name is inferred from the method name, and parameter types are inferred from the method signature. ```typescript 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!"); ``` -------------------------------- ### Defining Root Directories with @Root Decorator (TypeScript) Source: https://github.com/zcaceres/easy-mcp/blob/main/README.md This snippet demonstrates how to apply the @Root decorator to a class to define one or more root directories for the MCP server. The decorator takes a URI and an optional configuration object with a name. Applying this decorator allows EasyMCP to automatically configure roots, reducing boilerplate. ```TypeScript @Root("/my-sample-dir/photos") @Root("/my-root-dir", { name: "My laptop's root directory" }) class MyMCP extends EasyMCP { // ... } ``` -------------------------------- ### Defining Resource/Template with EasyMCP Decorator API (@Resource) (TypeScript) Source: https://github.com/zcaceres/easy-mcp/blob/main/README.md This snippet demonstrates defining an MCP resource or resource template using the @Resource decorator. It shows how the URI template is defined in the decorator argument and how path parameters like `name` are passed as method arguments. ```typescript @Resource("greeting/{name}") getGreeting(name: string) { return `Hello, ${name}!`; } ``` -------------------------------- ### Defining EasyMCP Class with Roots - TypeScript Source: https://github.com/zcaceres/easy-mcp/blob/main/README.md Extends the base `EasyMCP` class and uses the `@Root` decorator to define multiple root paths for the MCP instance. Roots can optionally be named. ```typescript 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!"); ```