Skip to content

0brym/mcp

Repository files navigation

Modular MCP Server

A config-driven, modular MCP (Model Context Protocol) server with zero dependencies. Just edit a JSON config file to enable/disable tools and plugins!

Features

  • Config-Driven: Enable/disable tools by editing config.json
  • Zero Dependencies: Pure Node.js (18+), no npm packages needed
  • Plugin Architecture: Easy to add new plugins
  • Security: Built-in access controls and sandboxing per tool
  • Simple: ~500 lines of core code
  • Fast: Minimal overhead, direct tool execution

Quick Start

# 1. Create or edit config.json
cp config.example.json config.json

# 2. Start the server
node server.js

# Or specify a config file
node server.js my-config.json

How It Works

Config File Structure

The config.json file controls everything:

{
  "server": {
    "name": "my-mcp-server",
    "version": "1.0.0"
  },
  "plugins": [
    {
      "name": "utils",
      "type": "builtin",
      "module": "utils",
      "enabled": true,
      "tools": [
        {
          "name": "echo",
          "enabled": true
        },
        {
          "name": "calculate",
          "enabled": true
        }
      ]
    }
  ]
}

That's it! No code changes needed to add/remove tools.

Adding a Tool

Want to enable file operations? Just edit the config:

{
  "plugins": [
    {
      "name": "filesystem",
      "type": "builtin",
      "module": "filesystem",
      "enabled": true,
      "tools": [
        {
          "name": "read_file",
          "enabled": true,
          "allowedPaths": [".", "/tmp"]
        },
        {
          "name": "write_file",
          "enabled": true
        }
      ]
    }
  ]
}

Restart the server - done!

Disabling a Tool

Set "enabled": false:

{
  "name": "delete_file",
  "enabled": false  // ← Tool won't be available
}

Built-in Plugins

Filesystem Plugin

File and directory operations with path restrictions.

Tools:

  • read_file - Read file contents
  • write_file - Write to files
  • list_directory - List directory contents
  • file_info - Get file metadata
  • delete_file - Delete files

Config Options:

{
  "name": "read_file",
  "enabled": true,
  "allowedPaths": [".", "/tmp"],  // Restrict access
  "readonly": false                // Make filesystem readonly
}

Shell Plugin

Execute shell commands with whitelist/blacklist support.

Tools:

  • exec_command - Execute shell commands

Config Options:

{
  "name": "exec_command",
  "enabled": true,
  "allowedCommands": ["ls", "pwd", "cat"],  // Whitelist
  "blockedCommands": ["rm", "sudo"],         // Blacklist
  "maxBuffer": 1048576                       // 1MB output limit
}

HTTP Plugin

Make HTTP requests with domain restrictions.

Tools:

  • http_get - GET requests
  • http_post - POST requests

Config Options:

{
  "name": "http_get",
  "enabled": true,
  "allowedDomains": ["api.github.com", "example.com"]
}

Utils Plugin

Utility functions for common tasks.

Tools:

  • echo - Echo back input (testing)
  • calculate - Safe math evaluation
  • timestamp - Get current time
  • base64_encode - Base64 encoding
  • base64_decode - Base64 decoding

Creating Custom Plugins

1. Create Plugin File

// plugins/my-plugin.js
export default {
  // Optional initialization
  async init(config) {
    console.log('Plugin initialized:', config);
  },

  tools: {
    my_tool: {
      description: 'Description of what this tool does',
      inputSchema: {
        type: 'object',
        properties: {
          input: {
            type: 'string',
            description: 'Input parameter'
          }
        },
        required: ['input']
      },
      async execute(args, config) {
        // args = input parameters
        // config = tool-specific config from config.json

        return `Result: ${args.input}`;
      }
    }
  }
};

2. Add to Config

{
  "plugins": [
    {
      "name": "my-plugin",
      "type": "builtin",
      "module": "my-plugin",
      "enabled": true,
      "tools": [
        {
          "name": "my_tool",
          "enabled": true,
          "customOption": "value"
        }
      ]
    }
  ]
}

3. Restart Server

That's it! Your tool is now available.

External Plugins

Load plugins from outside the project:

{
  "plugins": [
    {
      "name": "external-plugin",
      "type": "external",
      "module": "/absolute/path/to/plugin.js",
      "enabled": true,
      "tools": [...]
    }
  ]
}

Security Features

Path Restrictions

{
  "name": "read_file",
  "allowedPaths": ["./data", "/tmp"]  // Only these paths accessible
}

Command Whitelisting

{
  "name": "exec_command",
  "allowedCommands": ["ls", "cat", "grep"]  // Only these commands
}

Domain Filtering

{
  "name": "http_get",
  "allowedDomains": ["trusted-api.com"]  // Only these domains
}

Read-Only Mode

{
  "name": "write_file",
  "readonly": true  // Prevent writes
}

Configuration Reference

Server Config

{
  "server": {
    "name": "server-name",     // MCP server name
    "version": "1.0.0"         // Server version
  }
}

Plugin Config

{
  "name": "plugin-name",       // Unique plugin identifier
  "type": "builtin",           // "builtin" or "external"
  "module": "plugin-file",     // Plugin file/path
  "enabled": true,             // Enable/disable entire plugin
  "required": false,           // Fail if plugin can't load
  "tools": [...]               // Array of tool configs
}

Tool Config

{
  "name": "tool-name",         // Tool identifier
  "enabled": true,             // Enable/disable this tool
  // ... custom options passed to tool's execute()
}

Examples

Example 1: Read-Only Filesystem

{
  "plugins": [
    {
      "name": "filesystem",
      "type": "builtin",
      "module": "filesystem",
      "enabled": true,
      "tools": [
        {
          "name": "read_file",
          "enabled": true,
          "allowedPaths": ["/var/log"]
        },
        {
          "name": "list_directory",
          "enabled": true
        }
      ]
    }
  ]
}

Example 2: Safe Shell Access

{
  "plugins": [
    {
      "name": "shell",
      "type": "builtin",
      "module": "shell",
      "enabled": true,
      "tools": [
        {
          "name": "exec_command",
          "enabled": true,
          "allowedCommands": ["ls", "pwd", "cat", "grep", "find"],
          "blockedCommands": ["rm", "sudo", "su"],
          "maxBuffer": 1048576
        }
      ]
    }
  ]
}

Example 3: HTTP API Client

{
  "plugins": [
    {
      "name": "http",
      "type": "builtin",
      "module": "http",
      "enabled": true,
      "tools": [
        {
          "name": "http_get",
          "enabled": true,
          "allowedDomains": [
            "api.github.com",
            "httpbin.org"
          ]
        }
      ]
    }
  ]
}

Testing

Using MCP Inspector

# Install MCP Inspector
npm install -g @modelcontextprotocol/inspector

# Test your server
mcp-inspector node server.js

Manual Testing

Send JSON-RPC requests via stdin:

# Initialize
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | node server.js

# List tools
echo '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' | node server.js

# Call a tool
echo '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"echo","arguments":{"message":"Hello!"}}}' | node server.js

Logging

Control log level via environment variable:

# Debug mode
LOG_LEVEL=DEBUG node server.js

# Quiet mode
LOG_LEVEL=ERROR node server.js

Log levels: ERROR, WARN, INFO (default), DEBUG

Architecture

┌─────────────────────────────────────┐
│         MCP Client (Claude)         │
└─────────────┬───────────────────────┘
              │ JSON-RPC over stdio
┌─────────────▼───────────────────────┐
│      core/mcp-protocol.js           │
│  (JSON-RPC handler, stdio I/O)      │
└─────────────┬───────────────────────┘
              │
┌─────────────▼───────────────────────┐
│    core/plugin-manager.js           │
│  (Loads plugins from config.json)   │
└─────────────┬───────────────────────┘
              │
      ┌───────┴───────┬─────────────┐
      │               │             │
┌─────▼─────┐  ┌──────▼──────┐  ┌──▼────┐
│filesystem │  │    shell    │  │  http │
│  plugin   │  │   plugin    │  │ plugin│
└───────────┘  └─────────────┘  └───────┘

Files

.
├── server.js                # Entry point
├── config.json              # Your configuration
├── config.example.json      # Example config
├── package.json             # Project metadata
├── core/
│   ├── mcp-protocol.js      # MCP/JSON-RPC implementation
│   ├── plugin-manager.js    # Plugin loader
│   └── logger.js            # Logging utility
├── plugins/
│   ├── filesystem.js        # File operations
│   ├── shell.js             # Shell commands
│   ├── http.js              # HTTP requests
│   └── utils.js             # Utilities
└── README.md                # This file

FAQ

Q: How do I add a new tool?

A: Edit config.json and add the tool to a plugin's tools array. Restart the server.

Q: Can I create my own plugins?

A: Yes! Create a .js file in plugins/ following the plugin structure, then reference it in config.json.

Q: How do I restrict file access?

A: Use the allowedPaths option in the filesystem tool config.

Q: Is this secure?

A: It has built-in security features (path restrictions, command whitelisting, etc.), but treat it like any tool that executes code - configure it carefully and run with appropriate permissions.

Q: Can I use this in production?

A: Yes, but review the security settings carefully. Consider running in a container or with restricted user permissions.

License

MIT

Contributing

Contributions welcome! The codebase is intentionally simple and zero-dependency to keep it maintainable.

About

modular mcp server

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published