Skip to content

A Node.js server for the Model Context Protocol (MCP) with dynamic tool loading, HTTP API, and authentication.

License

Notifications You must be signed in to change notification settings

purinton/mcp-server

Repository files navigation

Purinton Dev

@purinton/mcp-server npm versionlicensebuild status

A Node.js server for the Model Context Protocol (MCP) with dynamic tool loading, HTTP API, and authentication. Easily extendable with custom tools for AI and automation workflows. Supports both CommonJS and ESM.


Table of Contents

Features

  • Model Context Protocol (MCP) server implementation for Node.js
  • Dynamic tool loading from a directory (tools/)
    • Loads .mjs files in ESM mode, .cjs files in CommonJS mode
  • HTTP API with authentication (Bearer token or custom async callback)
  • Express-based, easy to extend
  • Utility helpers for tool responses and BigInt-safe serialization
  • TypeScript type definitions included
  • Supports both CommonJS and ESM usage

Installation

npm install @purinton/mcp-server

Usage

ESM Example

// Example for ESM (module JS) usage
import { mcpServer } from '@purinton/mcp-server';

(async () => {
  const { app, httpInstance } = await mcpServer({
    // port: 1234, // You can change the port as needed
    // authToken: 'your-secret-token', // You can still use this for static token auth
    // toolsDir: './tools', // Path to your tools directory
    // name: 'Example MCP Server', // Set your server name
    // version: '1.0.0', // Set your server version
    // // Example: custom async auth callback
    // authCallback: async (token) => {
    //  // Replace with your own logic, e.g. check token in DB or against a list
    //  return token === 'your-secret-token';
    // }
  });
  console.log('MCP Server started!');
})();

CommonJS Example

// Example for CommonJS usage
const { mcpServer } = require('@purinton/mcp-server');

(async () => {
  const { app, httpInstance } = await mcpServer({
    // port: 1234, // You can change the port as needed
    // authToken: 'your-secret-token', // You can still use this for static token auth
    // toolsDir: './tools', // Path to your tools directory
    // name: 'Example MCP Server', // Set your server name
    // version: '1.0.0', // Set your server version
    // // Example: custom async auth callback
    // authCallback: async (token) => {
    //  // Replace with your own logic, e.g. check token in DB or against a list
    //  return token === 'your-secret-token';
    // }
  });
  console.log('MCP Server started!');
})();

Note:

  • In ESM mode, tools must be .mjs files and use the ESM export signature.
  • In CommonJS mode, tools must be .cjs files and use the CommonJS export signature.

Custom Tool Example (ESM)

To add your own tool for ESM, create a file in the tools/ directory (e.g., tools/echo.mjs):

import { z, buildResponse } from '@purinton/mcp-server';

export default async function ({ mcpServer, toolName, log }) {
  mcpServer.tool(
    toolName,
    "Echo Tool",
    { echoText: z.string() },
    async (_args, _extra) => {
      log.debug(`${toolName} Request`, { _args });
      const response = {
        message: "echo-reply",
        data: {
          text: _args.echoText
        }
      };
      log.debug(`${toolName} Response`, { response });
      return buildResponse(response);
    }
  );
}

Custom Tool Example (CommonJS)

To add your own tool for CommonJS, create a file in the tools/ directory (e.g., tools/echo.cjs):

const { z, buildResponse } = require('@purinton/mcp-server');

module.exports = async function ({ mcpServer, toolName, log }) {
  mcpServer.tool(
    toolName,
    "Echo Tool",
    { echoText: z.string() },
    async (_args, _extra) => {
      log.debug(`${toolName} Request`, { _args });
      const response = {
        message: "echo-reply",
        data: {
          text: _args.echoText
        }
      };
      log.debug(`${toolName} Response`, { response });
      return buildResponse(response);
    }
  );
};

API

`async mcpServer(options): Promise<{ app, httpInstance, mcpServer, transport }>```

Starts the MCP + HTTP server. Options:

  • log (optional): Logger instance (default: @purinton/log)
  • toolsDir (optional): Path to tools directory (default: ./tools relative to the entry file)
  • port (optional): Port for HTTP server (default: 1234 or process.env.MCP_PORT)
  • authToken (optional): Bearer token for authentication (default: process.env.MCP_TOKEN)
  • authCallback (optional): Custom async callback for authentication. Receives (token) and returns true/false or a Promise.
  • name (optional): Name for the MCP server
  • version (optional): Version for the MCP server

Returns an object with:

  • app: Express application instance
  • httpInstance: HTTP server instance
  • mcpServer: MCP server instance
  • transport: HTTP transport instance

convertBigIntToString(value)

Recursively converts all BigInt values in an object to strings.

buildResponse(data)

Wraps a plain JS object into the standard tool response payload.

z

Re-exports zod for schema validation.

TypeScript

Type definitions are included:

export interface McpServerOptions {
  log?: any;
  toolsDir?: string;
  port?: number | string;
  authToken?: string;
  authCallback?: (token?: string) => boolean | Promise<boolean>;
  name?: string;
  version?: string;
}

export interface McpServerResult {
  app: import('express').Application;
  httpInstance: import('http').Server;
  mcpServer: any;
  transport: any;
}

export function mcpServer(options?: McpServerOptions): Promise<McpServerResult>;
export function convertBigIntToString(value: any): any;
export function buildResponse(data: any): { content: { type: 'text'; text: string }[] };
export { z };

Support

For help, questions, or to chat with the author and community, visit:

DiscordPurinton Dev

Purinton Dev on Discord

License

MIT © 2025 Russell Purinton

Links

About

A Node.js server for the Model Context Protocol (MCP) with dynamic tool loading, HTTP API, and authentication.

Topics

Resources

License

Stars

Watchers

Forks