Skip to content

CS-Tao/x-mcp

Repository files navigation

Test Publish Chinese Doc

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 logId and logger into 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: