EXT-MCP
๐งฉ Extendable MCP Framework
ext-mcp introduces two core concepts: middleware and mods.
- Middleware: carries shared capabilities and follows an onion-style execution model.
- Mods: extends functionality; a mod is a collection of MCP features โ features here refer to tool/prompt/resource in the MCP protocol.
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ LLM Request โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโ
โ middleware1 โ
โโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโ
โ middleware2 โ
โโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ mods โ
โ โโโโโโโ โโโโโโโ โโโโโโโ โ
โ โmod1 โ โmod2 โ โmod3 โ โ
โ โโโโโโโ โโโโโโโ โโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโ
โ middleware2 โ
โโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโ
โ middleware1 โ
โโโโโโโโโโโโโโโ
Quick Start
๐ฐ See the complete example in the demo directory
Installation
npm i ext-mcp
App entry
import path from "path";
import XMCP from "ext-mcp";
import errorHandler from "./middlewares/error-handler";
import sayGoodbye from "./mods/say-goodbye";
const app = new XMCP({
name: "my-mcp",
version: "0.0.1",
});
// ๐๏ธ Middleware
app.use(path.join(__dirname, "./middlewares/logger")); // file path
app.use(errorHandler); // function
app.use(require.resolve('@foo/mcp-middleware-logger')); // npm/workspace package
// ๐งฉ Mods
app.installMod(path.join(__dirname, "./mods/say-hello")); // file path
app.installMod(sayGoodbye); // function
app.installMod(require.resolve('@foo/mcp-mod-demo')); // npm/workspace package
// Start the service. Currently only stdio mode is supported
app.start();
Middleware example
Example: implement a logger middleware that injects
logIdandloggerinto the context
import { type Middleware } from "ext-mcp";
export interface LoggerContext {
logId: string;
logger: {
info: (message: string) => void;
error: (message: string) => void;
};
}
const middleware: Middleware<LoggerContext> = async (context, next) => {
context.logId = `foo-log-id`;
context.logger = createLogger(context.logId);
context.logger.info(`mcp started: ${context.actionName}`);
const res = await next();
context.logger.info(`mcp finished: ${context.actionName}`);
return res;
};
export default middleware;
Mod example
import type { Mod, Tool } from "ext-mcp";
import { z } from "zod/v3";
const sayHello: Tool<{ name: z.ZodString }> = {
name: "say-hello",
config: {
title: "Say hello",
description: "Used for MCP mod demo",
inputSchema: { name: z.string().describe("user's name") },
},
handler: (context) => {
const { name } = context.args;
return {
content: [
{
type: "text",
text: `Hello, ${name}!`,
},
],
};
},
};
const demoMod: Mod = {
name: "demo-mod",
version: "0.0.1",
description: "Demo mod",
tools: [sayHello],
};
export default demoMod;
Contributing
Core source is in the src folder; the demo lives under demo.
Build & Test
# Install dependencies
npm install
# Build
npm run build
# Test
npm test
Debugging the demo service
# Start the inspector. @see https://github.com/modelcontextprotocol/inspector
npx @modelcontextprotocol/inspector
After the inspector starts, paste the absolute path to demo/run.sh into the Command field to begin debugging.
IDE configuration for the demo service
This repository includes configuration for several IDEs to view the demo directly: