A config-driven, modular MCP (Model Context Protocol) server with zero dependencies. Just edit a JSON config file to enable/disable tools and plugins!
- 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
# 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.jsonThe 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.
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!
Set "enabled": false:
{
"name": "delete_file",
"enabled": false // ← Tool won't be available
}File and directory operations with path restrictions.
Tools:
read_file- Read file contentswrite_file- Write to fileslist_directory- List directory contentsfile_info- Get file metadatadelete_file- Delete files
Config Options:
{
"name": "read_file",
"enabled": true,
"allowedPaths": [".", "/tmp"], // Restrict access
"readonly": false // Make filesystem readonly
}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
}Make HTTP requests with domain restrictions.
Tools:
http_get- GET requestshttp_post- POST requests
Config Options:
{
"name": "http_get",
"enabled": true,
"allowedDomains": ["api.github.com", "example.com"]
}Utility functions for common tasks.
Tools:
echo- Echo back input (testing)calculate- Safe math evaluationtimestamp- Get current timebase64_encode- Base64 encodingbase64_decode- Base64 decoding
// 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}`;
}
}
}
};{
"plugins": [
{
"name": "my-plugin",
"type": "builtin",
"module": "my-plugin",
"enabled": true,
"tools": [
{
"name": "my_tool",
"enabled": true,
"customOption": "value"
}
]
}
]
}That's it! Your tool is now available.
Load plugins from outside the project:
{
"plugins": [
{
"name": "external-plugin",
"type": "external",
"module": "/absolute/path/to/plugin.js",
"enabled": true,
"tools": [...]
}
]
}{
"name": "read_file",
"allowedPaths": ["./data", "/tmp"] // Only these paths accessible
}{
"name": "exec_command",
"allowedCommands": ["ls", "cat", "grep"] // Only these commands
}{
"name": "http_get",
"allowedDomains": ["trusted-api.com"] // Only these domains
}{
"name": "write_file",
"readonly": true // Prevent writes
}{
"server": {
"name": "server-name", // MCP server name
"version": "1.0.0" // Server version
}
}{
"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
}{
"name": "tool-name", // Tool identifier
"enabled": true, // Enable/disable this tool
// ... custom options passed to tool's execute()
}{
"plugins": [
{
"name": "filesystem",
"type": "builtin",
"module": "filesystem",
"enabled": true,
"tools": [
{
"name": "read_file",
"enabled": true,
"allowedPaths": ["/var/log"]
},
{
"name": "list_directory",
"enabled": true
}
]
}
]
}{
"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
}
]
}
]
}{
"plugins": [
{
"name": "http",
"type": "builtin",
"module": "http",
"enabled": true,
"tools": [
{
"name": "http_get",
"enabled": true,
"allowedDomains": [
"api.github.com",
"httpbin.org"
]
}
]
}
]
}# Install MCP Inspector
npm install -g @modelcontextprotocol/inspector
# Test your server
mcp-inspector node server.jsSend 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.jsControl log level via environment variable:
# Debug mode
LOG_LEVEL=DEBUG node server.js
# Quiet mode
LOG_LEVEL=ERROR node server.jsLog levels: ERROR, WARN, INFO (default), DEBUG
┌─────────────────────────────────────┐
│ 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│
└───────────┘ └─────────────┘ └───────┘
.
├── 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
A: Edit config.json and add the tool to a plugin's tools array. Restart the server.
A: Yes! Create a .js file in plugins/ following the plugin structure, then reference it in config.json.
A: Use the allowedPaths option in the filesystem tool config.
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.
A: Yes, but review the security settings carefully. Consider running in a container or with restricted user permissions.
MIT
Contributions welcome! The codebase is intentionally simple and zero-dependency to keep it maintainable.