Skip to content

Latest commit

 

History

History
314 lines (238 loc) · 6.44 KB

File metadata and controls

314 lines (238 loc) · 6.44 KB

Why a Modular, Config-Driven MCP Server?

The Problem with Traditional MCP Servers

Most MCP servers are hardcoded. Want to add a new tool? You need to:

  1. Write code
  2. Import the new tool
  3. Register it in the server
  4. Recompile/bundle (if using TypeScript)
  5. Redeploy

This is slow and requires development skills.

The Modular Solution

With this server, adding a tool is just editing a JSON file:

Before (Traditional)

// 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.

After (Modular)

{
  "plugins": [
    {"name": "filesystem", "enabled": true},
    {"name": "shell", "enabled": false},
    {"name": "http", "enabled": true}
  ]
}

Want to disable Shell? Change true to false. Restart. Done.

Real-World Benefits

1. Non-Developers Can Configure

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."

2. Environment-Specific Configs

# Development
node server.js config.dev.json

# Production (restricted)
node server.js config.prod.json

# Local testing
node server.js config.test.json

Each environment has different tools enabled - no code changes!

3. Security by Configuration

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"]
}

4. Rapid Iteration

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.

5. Plugin Marketplace Potential

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.

Comparison

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

Code Statistics

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

The Config-First Philosophy

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.

Example: Adding a Custom Tool (5 minutes)

Traditional Way

// 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-server

Time: 30+ minutes

Modular Way

// 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.js

Time: 5 minutes

When Traditional is Better

  • 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.)

When Modular is Better

  • Rapid prototyping
  • Multiple deployment environments
  • Non-developers need to configure
  • Want minimal dependencies
  • Security through configuration
  • Plugin marketplace potential
  • Simple, understandable codebase

The Bottom Line

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!

Future Possibilities

Because it's config-driven, you could add:

  1. Web UI for config editing (no code changes)
  2. Config validation with JSON Schema
  3. Hot reload when config changes
  4. A/B testing with different configs
  5. Plugin marketplace with config templates
  6. Config migration tools
  7. Cloud-based configs (fetch from URL)

All without changing the core server code!

Conclusion

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.