MCP server by jeffreygnatek
MCP Plugin Server
A centralized Model Context Protocol (MCP) server with plugin architecture for external tool integrations. This server allows you to manage multiple MCP tools as plugins rather than running separate servers for each integration.
Features
- Plugin Architecture: Centralized management of MCP tools as plugins
- Secure Credential Storage: AES-256-GCM encrypted credential storage
- RESTful API: Complete REST API for plugin and credential management
- WebSocket Support: Real-time MCP protocol communication via WebSocket
- Hot-Reloading: Dynamic plugin loading and unloading
- Authentication: Multi-level authentication for server and plugin access
- Example Plugins: Pre-built examples including OpenWeatherMap integration
Quick Start
1. Installation
# Clone the repository
git clone <repository-url>
cd mcp-plugin-server
# Install dependencies
npm install
# Build the project
npm run build
2. Configuration
Create a .env
file with your configuration:
```bash
# Server port
PORT=3117
# Master key for encrypting credentials (generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))")
MASTER_KEY=your_64_character_hex_master_key_here
Admin token for accessing management endpoints
ADMIN_TOKEN=your_secure_admin_token_here
Allowed origins for CORS (comma-separated)
ALLOWED_ORIGINS=http://localhost:3000
Environment
NODE_ENV=development
### 3. Start the Server
```bash
# Development mode (with hot reloading)
npm run dev
# Production mode
npm start
```
The server will start on the configured port (default: 3117) and display:
- REST API endpoints: `http://localhost:3117/api/*`
- WebSocket MCP endpoint: `ws://localhost:3117/mcp`
- Health check: `http://localhost:3117/health`
## Docker Deployment
Run the MCP Plugin Server in Docker for consistent, isolated deployments across different environments.
> **Note on Port Configuration**: The default port is now 3117. When running Docker containers, make sure to map the correct port with `-p 3117:3117`. You can customize this by setting the `PORT` environment variable (e.g., `-e PORT=8080 -p 8080:8080`).
### Quick Start with Docker
The easiest way to get started with Docker is using the provided setup script:
```bash
# Make the setup script executable (if not already)
chmod +x docker-setup.sh
# Quick setup and start (production mode)
./docker-setup.sh setup
# Or start in development mode with hot reload
./docker-setup.sh dev
Manual Docker Setup
1. Environment Configuration
Create a .env
file from the example:
# Copy the example environment file
cp env.example .env
# Edit the file with your configuration
nano .env
Example .env
configuration:
```bash
NODE_ENV=production
PORT=3117
MASTER_KEY=your-secure-64-character-hex-master-key-here
ADMIN_TOKEN=your-secure-admin-token-here
ALLOWED_ORIGINS=http://localhost:3117,http://localhost:3001
#### 2. Production Deployment
```bash
# Build and start the production container
docker-compose up --build -d
# View logs
docker-compose logs -f
# Stop the container
docker-compose down
3. Development Mode
For development with hot reload and debugging:
# Start in development mode
docker-compose -f docker-compose.dev.yml up --build -d
# View logs
docker-compose -f docker-compose.dev.yml logs -f
# Stop development containers
docker-compose -f docker-compose.dev.yml down
Docker Setup Script Commands
The docker-setup.sh
script provides convenient commands for managing your Docker deployment:
# Setup and start (production mode)
./docker-setup.sh setup
# Start in development mode
./docker-setup.sh dev
# View logs (production)
./docker-setup.sh logs
# View development logs
./docker-setup.sh logs dev
# Stop containers
./docker-setup.sh stop
# Restart containers
./docker-setup.sh restart
# Check container status
./docker-setup.sh status
# Clean up Docker resources
./docker-setup.sh clean
# Show help
./docker-setup.sh help
Docker Features
- Multi-stage Build: Optimized production images with minimal size
- Security: Non-root user execution for enhanced security
- Health Checks: Built-in health monitoring for container orchestration
- Persistent Storage: Volume mounts for data, plugins, and configuration
- Development Support: Hot reload and debugging capabilities
- Environment Variables: Flexible configuration via environment variables
Volume Mounts
The Docker setup includes the following volume mounts:
volumes:
- ./data:/app/data # Persistent credential storage
- ./plugins:/app/plugins # Plugin files
- ./config:/app/config # Configuration files
Environment Variables
Key environment variables for Docker deployment:
| Variable | Description | Default |
| ----------------- | ------------------------------ | --------------------------------------------- |
| `NODE_ENV` | Environment mode | `production` |
| `PORT` | Server port | `3117` |
| `MASTER_KEY` | Encryption key for credentials | Auto-generated |
| `ADMIN_TOKEN` | Admin API access token | Auto-generated |
| `ALLOWED_ORIGINS` | CORS allowed origins | `http://localhost:3117,http://localhost:3001` |
Production Considerations
For production deployments:
- Security: Always set custom
MASTER_KEY
andADMIN_TOKEN
- Backup: Regularly backup the
./data
directory - Monitoring: Use the health check endpoint for monitoring
- Updates: Use
docker-compose pull
to update images - Logs: Configure log rotation and centralized logging
Kubernetes Deployment
For Kubernetes deployments, you can use the Docker image with these considerations:
# Example Kubernetes deployment snippet
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-plugin-server
spec:
replicas: 1
selector:
matchLabels:
app: mcp-plugin-server
template:
spec:
containers:
- name: mcp-plugin-server
image: mcp-plugin-server:latest
ports:
- containerPort: 3117
env:
- name: MASTER_KEY
valueFrom:
secretKeyRef:
name: mcp-secrets
key: master-key
- name: ADMIN_TOKEN
valueFrom:
secretKeyRef:
name: mcp-secrets
key: admin-token
volumeMounts:
- name: data-storage
mountPath: /app/data
- name: plugin-storage
mountPath: /app/plugins
livenessProbe:
httpGet:
path: /health
port: 3117
initialDelaySeconds: 30
periodSeconds: 10
API Documentation
Authentication
All admin endpoints require an Authorization
header:
Authorization: Bearer your_admin_token_here
Plugin Management
List Plugins
GET /api/plugins
Authorization: Bearer <admin_token>
Enable Plugin
POST /api/plugins/:id/enable
Authorization: Bearer <admin_token>
Disable Plugin
POST /api/plugins/:id/disable
Authorization: Bearer <admin_token>
Update Plugin Configuration
PUT /api/plugins/:id/config
Authorization: Bearer <admin_token>
Content-Type: application/json
{
"enabled": true,
"config": {
"key": "value"
}
}
Remove Plugin
DELETE /api/plugins/:id
Authorization: Bearer <admin_token>
Credential Management
Store Credential
POST /api/auth/credentials
Authorization: Bearer <admin_token>
Content-Type: application/json
{
"pluginId": "openweather",
"credentialId": "api_key",
"value": "your_api_key_here"
}
List Plugin Credentials
GET /api/auth/credentials/:pluginId
Authorization: Bearer <admin_token>
Delete Credential
DELETE /api/auth/credentials/:pluginId/:credentialId
Authorization: Bearer <admin_token>
MCP Protocol
List Available Tools
GET /api/mcp/tools
List Available Resources
GET /api/mcp/resources
Execute Tool
POST /api/mcp/tools/execute
Content-Type: application/json
{
"toolName": "openweather.get_weather",
"args": {
"location": "London,UK",
"units": "metric"
}
}
### WebSocket MCP Communication
Connect to `ws://localhost:3117/mcp` and send JSON-RPC 2.0 messages:
// List tools
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}
// Call a tool
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "openweather.get_weather",
"arguments": {
"location": "London,UK"
}
}
}
Plugin Development
Creating a Plugin
- Create a plugin directory in the
plugins/
folder - Implement the
MCPPlugin
interface:
import { MCPPlugin, PluginContext, Tool, Resource } from "../types/plugin";
export class MyPlugin implements MCPPlugin {
public readonly name = "my-plugin";
public readonly version = "1.0.0";
public readonly description = "My custom plugin";
public readonly dependencies: string[] = [];
private context: PluginContext | null = null;
async initialize(context: PluginContext): Promise<void> {
this.context = context;
// Initialize your plugin here
}
getTools(): Tool[] {
return [
{
name: "my_tool",
description: "Description of my tool",
inputSchema: {
type: "object",
properties: {
param: { type: "string", description: "Parameter description" },
},
required: ["param"],
},
},
];
}
getResources(): Resource[] {
return [];
}
async executeTool(name: string, args: any): Promise<any> {
switch (name) {
case "my_tool":
return { result: `Hello, ${args.param}!` };
default:
throw new Error(`Unknown tool: ${name}`);
}
}
async cleanup(): Promise<void> {
this.context = null;
}
}
- Create a
plugin.json
manifest:
{
"name": "my-plugin",
"version": "1.0.0",
"description": "My custom plugin",
"author": "Your Name",
"auth": {
"type": "api-key",
"required": true,
"fields": {
"api_key": {
"type": "password",
"required": true,
"description": "API key for the service"
}
}
}
}
- Build and load your plugin via the API
Plugin Context
The PluginContext
provides access to:
- Credentials: Secure storage for API keys and tokens
- Logger: Plugin-specific logging
- Configuration: Plugin configuration from the registry
- OAuth: OAuth 2.0 flow support (future feature)
// Store a credential
await context.setCredential("api_key", "your_key_here");
// Retrieve a credential
const apiKey = await context.getCredential("api_key");
// Log messages
context.logger.info("Plugin initialized");
context.logger.error("Something went wrong");
Example: Weather Plugin
The included OpenWeatherMap plugin demonstrates:
- API key authentication
- Multiple tools (current weather and forecast)
- Error handling
- Structured data return
Setup
- Get an API key from OpenWeatherMap
- Store the credential:
curl -X POST http://localhost:3117/api/auth/credentials \
-H "Authorization: Bearer your_admin_token" \
-H "Content-Type: application/json" \
-d '{
"pluginId": "openweather",
"credentialId": "api_key",
"value": "your_openweather_api_key"
}'
- Load and enable the plugin:
# This would be implemented in the plugin loading mechanism
curl -X POST http://localhost:3117/api/plugins/openweather/enable \
-H "Authorization: Bearer your_admin_token"
- Use the weather tools:
curl -X POST http://localhost:3117/api/mcp/tools/execute \
-H "Content-Type: application/json" \
-d '{
"toolName": "openweather.get_weather",
"args": {
"location": "London,UK",
"units": "metric"
}
}'
Security
- Credential Encryption: All credentials are encrypted using AES-256-GCM
- Plugin Isolation: Each plugin has isolated credential storage
- Authentication: Admin endpoints require token authentication
- Input Validation: Request validation and sanitization
- Rate Limiting: Protection against abuse
- CORS: Configurable cross-origin policies
Development Scripts
# Start development server with hot reloading
npm run dev
# Build the project
npm run build
# Start production server
npm start
# Run tests
npm test
# Run linting
npm run lint
Architecture
mcp-plugin-server/
├── src/
│ ├── core/
│ │ ├── registry.ts # Plugin discovery and loading
│ │ ├── plugin-manager.ts # Plugin lifecycle management
│ │ └── auth/
│ │ └── secure-storage.ts # Encrypted credential storage
│ ├── plugins/
│ │ └── examples/ # Example plugins
│ ├── types/ # TypeScript interfaces
│ └── server.ts # Express server and MCP protocol
├── plugins/ # External plugin directory
├── config/
│ └── plugins.json # Plugin configuration
└── data/
└── credentials/ # Encrypted credential storage
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
License
MIT License - see LICENSE file for details.
Roadmap
- [ ] OAuth 2.0 implementation
- [ ] Plugin marketplace/discovery
- [ ] Advanced sandboxing
- [ ] Cloud secrets integration
- [ ] Admin web interface
- [ ] Plugin hot-reloading
- [ ] Clustering support