Most MCP servers are hardcoded. Want to add a new tool? You need to:
- Write code
- Import the new tool
- Register it in the server
- Recompile/bundle (if using TypeScript)
- Redeploy
This is slow and requires development skills.
With this server, adding a tool is just editing a JSON file:
// server.ts
import { FileSystem } from './tools/filesystem';
import { Shell } from './tools/shell';
import { HTTP } from './tools/http';
const server = new McpServer({
tools: [
new FileSystem(),
new Shell(),
new HTTP()
]
});Want to disable Shell? Edit code, rebuild, redeploy.
{
"plugins": [
{"name": "filesystem", "enabled": true},
{"name": "shell", "enabled": false},
{"name": "http", "enabled": true}
]
}Want to disable Shell? Change true to false. Restart. Done.
Your config file is self-documenting:
{
"name": "read_file",
"enabled": true,
"allowedPaths": ["./data", "/tmp"],
"readonly": false
}Anyone can understand: "File reading is enabled for ./data and /tmp, and writing is allowed."
# Development
node server.js config.dev.json
# Production (restricted)
node server.js config.prod.json
# Local testing
node server.js config.test.jsonEach environment has different tools enabled - no code changes!
Traditional: Security requires code changes.
// Need to edit code to add path restrictions
if (!allowedPaths.includes(path)) {
throw new Error('Not allowed');
}Modular: Security is configuration.
{
"allowedPaths": ["./safe-dir"],
"readonly": true,
"blockedCommands": ["rm", "sudo"]
}Scenario: Client wants to test file operations, but you're worried about security.
Traditional: Write temporary security code, deploy, test, remove code, redeploy.
Modular:
{
"name": "filesystem",
"enabled": true,
"tools": [
{
"name": "read_file",
"enabled": true,
"allowedPaths": ["/tmp/test-data"]
},
{
"name": "write_file",
"enabled": false // Disabled for testing
}
]
}Restart. Test. Change config. Restart. Done in seconds.
Users can share plugin files:
# Download a community plugin
curl -o plugins/weather.js https://mcp-plugins.com/weather.js
# Add to config
{
"plugins": [
{
"name": "weather",
"type": "builtin",
"module": "weather",
"enabled": true,
"apiKey": "your-key",
"tools": [{"name": "get_weather", "enabled": true}]
}
]
}No compilation, no dependencies, just drop in and configure.
| Feature | Traditional | Modular (This) |
|---|---|---|
| Add tool | Edit code | Edit JSON |
| Disable tool | Comment code | Set enabled: false |
| Security config | Hardcoded | In JSON |
| Multiple configs | Build variants | Multiple JSON files |
| Non-dev friendly | No | Yes |
| Deployment | Rebuild/redeploy | Just restart |
| Plugin sharing | Complex | Drop file + config |
| Dependencies | Many | Zero |
| Lines of code | 1000s | 887 |
| Testing | Unit tests needed | Config validation |
This server:
- 887 lines of code total
- 0 dependencies
- 4 built-in plugins (15 tools total)
- Fully functional MCP server
Typical TypeScript MCP server:
- 2000+ lines
- 20+ dependencies (@modelcontextprotocol/sdk, typescript, etc.)
- Harder to configure
- Requires build step
Traditional: Code defines behavior
- Want different behavior? Change code.
- Want to test? Mock/stub code.
- Want to secure? Add security code.
Modular: Config defines behavior
- Want different behavior? Change config.
- Want to test? Use test config.
- Want to secure? Set config options.
// 1. Create tool class
class MyTool implements Tool {
async execute(args: ToolArgs): Promise<ToolResult> {
// Implementation
}
}
// 2. Register in server
import { MyTool } from './tools/my-tool';
server.registerTool(new MyTool());
// 3. Update types
interface ToolRegistry {
myTool: MyTool;
// ... other tools
}
// 4. Rebuild
npm run build
// 5. Redeploy
pm2 restart mcp-serverTime: 30+ minutes
// 1. Create plugin (one file)
export default {
tools: {
my_tool: {
description: 'Does something',
inputSchema: { /* ... */ },
async execute(args) {
return 'Done!';
}
}
}
};
// 2. Add to config.json
{
"plugins": [
{
"name": "custom",
"type": "builtin",
"module": "custom",
"enabled": true,
"tools": [
{"name": "my_tool", "enabled": true}
]
}
]
}
// 3. Restart
node server.jsTime: 5 minutes
- Very complex tools needing TypeScript types
- Need strong IDE autocomplete for tool development
- Large team with strict code review processes
- Tools require compilation (C++ bindings, etc.)
- Rapid prototyping
- Multiple deployment environments
- Non-developers need to configure
- Want minimal dependencies
- Security through configuration
- Plugin marketplace potential
- Simple, understandable codebase
Traditional MCP servers are developer tools. Modular MCP servers are user tools.
You can hand the config file to a sysadmin, support engineer, or power user and say:
"Edit this JSON to enable/disable features"
Try doing that with a TypeScript codebase!
Because it's config-driven, you could add:
- Web UI for config editing (no code changes)
- Config validation with JSON Schema
- Hot reload when config changes
- A/B testing with different configs
- Plugin marketplace with config templates
- Config migration tools
- Cloud-based configs (fetch from URL)
All without changing the core server code!
Modular MCP server = configuration as code.
The config file IS the source of truth. The code is just the interpreter.
This is the Unix philosophy: simple, composable, configurable tools.