auto-publishing-mcp-server
Version:
Enterprise-grade MCP Server for Auto-Publishing with pre-publish validation, multi-cloud deployment, and monitoring
518 lines (448 loc) âĸ 14.9 kB
JavaScript
/**
* MCP Server Core Orchestration
* Main server that coordinates all components
*/
import { JsonRpcHandler } from '../transport/json-rpc.js';
import { TransportManager } from '../transport/manager.js';
import { McpHandler } from '../protocol/mcp-handler.js';
import { SERVER_INFO } from '../protocol/capabilities.js';
import { ToolsRegistry } from '../tools/tools-registry.js';
import { PrometheusMetrics } from '../metrics/prometheus-metrics.js';
import { AuthenticationManager } from '../security/auth.js';
import { securityMiddleware } from '../security/security-middleware.js';
export class AutoPublishingMcpServer {
constructor(config = {}) {
this.config = {
// Server identification
name: SERVER_INFO.name,
version: SERVER_INFO.version,
// Transport configuration
transports: {
http: {
enabled: true,
port: config.httpPort || 3000,
host: config.host || 'localhost',
cors: config.cors !== false,
...config.http
},
websocket: {
enabled: true,
port: config.wsPort || 3001,
host: config.host || 'localhost',
...config.websocket
}
},
// Server behavior
gracefulShutdownTimeout: config.gracefulShutdownTimeout || 10000,
enableMetrics: config.enableMetrics !== false,
logLevel: config.logLevel || 'info',
// Auto-publishing specific
autoRegisterTools: config.autoRegisterTools !== false,
toolsDirectory: config.toolsDirectory || './src/tools',
...config
};
// Core components
this.jsonRpcHandler = new JsonRpcHandler();
this.mcpHandler = new McpHandler();
this.metricsCollector = new PrometheusMetrics();
this.authManager = new AuthenticationManager(this.config.authentication || {});
this.securityMiddleware = securityMiddleware;
this.transportManager = new TransportManager(this.jsonRpcHandler, this.config.transports);
this.toolsRegistry = new ToolsRegistry();
// Pass metrics collector to components
this.transportManager.setMetricsCollector(this.metricsCollector);
// Server state
this.isRunning = false;
this.startTime = null;
this.shutdownInProgress = false;
// Initialize server
this.initializeServer();
}
/**
* Initializes the server components
*/
initializeServer() {
console.log(`Initializing ${this.config.name} v${this.config.version}`);
// Register MCP protocol methods with JSON-RPC handler
this.registerMcpMethods();
// Setup middleware
this.setupMiddleware();
// Auto-register tools if enabled
if (this.config.autoRegisterTools) {
this.registerRealTools();
}
console.log('Server initialization complete');
}
/**
* Registers MCP protocol methods with the JSON-RPC handler
*/
registerMcpMethods() {
// Core MCP methods
this.jsonRpcHandler.registerMethod('initialize', (params, message) =>
this.mcpHandler.handleInitialize(params, message)
);
// Tool methods
this.jsonRpcHandler.registerMethod('tools/list', (params, message) =>
this.mcpHandler.handleToolsList(params, message)
);
this.jsonRpcHandler.registerMethod('tools/call', (params, message) =>
this.mcpHandler.handleToolCall(params, message)
);
// Resource methods
this.jsonRpcHandler.registerMethod('resources/list', (params, message) =>
this.mcpHandler.handleResourcesList(params, message)
);
this.jsonRpcHandler.registerMethod('resources/read', (params, message) =>
this.mcpHandler.handleResourceRead(params, message)
);
// Prompt methods
this.jsonRpcHandler.registerMethod('prompts/list', (params, message) =>
this.mcpHandler.handlePromptsList(params, message)
);
this.jsonRpcHandler.registerMethod('prompts/get', (params, message) =>
this.mcpHandler.handlePromptGet(params, message)
);
// Server introspection methods
this.jsonRpcHandler.registerMethod('server/info', async () => {
return {
name: this.config.name,
version: this.config.version,
uptime: this.startTime ? Date.now() - this.startTime : 0,
stats: this.getStats()
};
});
this.jsonRpcHandler.registerMethod('server/health', async () => {
return await this.healthCheck();
});
console.log('MCP protocol methods registered');
}
/**
* Sets up middleware for request processing
*/
setupMiddleware() {
// Logging middleware
if (this.config.logLevel !== 'silent') {
this.jsonRpcHandler.use(JsonRpcHandler.createLoggingMiddleware(console));
}
// Rate limiting middleware
if (this.config.rateLimit) {
this.jsonRpcHandler.use(JsonRpcHandler.createRateLimitMiddleware(
this.config.rateLimit.maxRequests || 100,
this.config.rateLimit.windowMs || 60000
));
}
// Authentication middleware (if configured)
if (this.config.authentication) {
this.jsonRpcHandler.use(JsonRpcHandler.createAuthMiddleware(
this.config.authentication.checker
));
}
// Session management middleware
this.jsonRpcHandler.use(async (message) => {
// Track session activity
if (message.connectionId) {
const session = this.mcpHandler.getSessionInfo(message.connectionId);
if (session) {
this.mcpHandler.updateSessionActivity(message.connectionId);
}
}
});
console.log('Middleware configured');
}
/**
* Registers real tools through the ToolsRegistry
*/
registerRealTools() {
console.log('đ§ Registering real Git and Docker tools...');
// Register all real tools through the registry
this.toolsRegistry.registerAllTools(this.mcpHandler);
console.log('â
Real tools registration complete');
}
/**
* Registers auto-publishing specific tools
*/
registerAutoPublishingTools() {
// Git tools
this.mcpHandler.registerTool(
"git/status",
"Get git repository status",
async (args, context) => {
return {
output: "Git status retrieved",
data: {
branch: "main",
staged: [],
unstaged: [],
untracked: []
}
};
},
{
type: "object",
properties: {
path: { type: "string", description: "Repository path" }
}
}
);
this.mcpHandler.registerTool(
"git/commit",
"Create a git commit",
async (args, context) => {
return {
output: `Commit created: ${args.message}`,
data: {
hash: "abc123",
message: args.message,
timestamp: new Date().toISOString()
}
};
},
{
type: "object",
properties: {
message: { type: "string", description: "Commit message" },
path: { type: "string", description: "Repository path" }
},
required: ["message"]
}
);
// Docker tools
this.mcpHandler.registerTool(
"docker/build",
"Build a Docker image",
async (args, context) => {
return {
output: `Docker image built: ${args.tag}`,
data: {
imageId: "sha256:abc123",
tag: args.tag,
size: "125MB"
}
};
},
{
type: "object",
properties: {
tag: { type: "string", description: "Image tag" },
dockerfile: { type: "string", description: "Dockerfile path" },
context: { type: "string", description: "Build context path" }
},
required: ["tag"]
}
);
// Deploy tools
this.mcpHandler.registerTool(
"deploy/to-environment",
"Deploy to specific environment",
async (args, context) => {
return {
output: `Deployment to ${args.environment} initiated`,
data: {
deploymentId: "deploy-" + Date.now(),
environment: args.environment,
status: "in-progress",
startTime: new Date().toISOString()
}
};
},
{
type: "object",
properties: {
environment: {
type: "string",
enum: ["dev", "staging", "prod"],
description: "Target environment"
},
image: { type: "string", description: "Docker image to deploy" },
config: { type: "object", description: "Deployment configuration" }
},
required: ["environment"]
}
);
// Monitor tools
this.mcpHandler.registerTool(
"monitor/health-check",
"Perform health check on deployed services",
async (args, context) => {
return {
output: "Health check completed",
data: {
status: "healthy",
services: {
"webapp-dev": { status: "healthy", responseTime: "120ms" },
"webapp-prod": { status: "healthy", responseTime: "95ms" }
},
timestamp: new Date().toISOString()
}
};
},
{
type: "object",
properties: {
service: { type: "string", description: "Specific service to check" },
environment: { type: "string", description: "Environment to check" }
}
}
);
}
/**
* Starts the MCP server
*/
async start() {
if (this.isRunning) {
throw new Error('Server is already running');
}
try {
console.log(`Starting ${this.config.name}...`);
// Initialize transport manager
await this.transportManager.initialize();
// Start all transports
await this.transportManager.start();
this.isRunning = true;
this.startTime = Date.now();
console.log('đ Auto-Publishing MCP Server started successfully!');
console.log(`đĄ HTTP endpoint: http://${this.config.transports.http.host}:${this.config.transports.http.port}/mcp`);
console.log(`đ WebSocket endpoint: ws://${this.config.transports.websocket.host}:${this.config.transports.websocket.port}`);
console.log(`âšī¸ Health check: http://${this.config.transports.http.host}:${this.config.transports.http.port}/health`);
// Log registered tools
const stats = this.getStats();
console.log(`đ ī¸ Registered tools: ${stats.mcp.tools.count}`);
console.log(`đ Registered resources: ${stats.mcp.resources.count}`);
} catch (error) {
console.error('Failed to start server:', error);
await this.stop();
throw error;
}
}
/**
* Stops the MCP server
*/
async stop() {
if (!this.isRunning && !this.shutdownInProgress) {
console.log('Server is not running');
return;
}
this.shutdownInProgress = true;
console.log('Stopping Auto-Publishing MCP Server...');
try {
// Stop transport manager
await this.transportManager.stop();
// Close all MCP sessions
const sessions = this.mcpHandler.getActiveSessions();
for (const session of sessions) {
this.mcpHandler.removeSession(session.id);
}
this.isRunning = false;
this.shutdownInProgress = false;
this.startTime = null;
console.log('đ Server stopped successfully');
} catch (error) {
console.error('Error stopping server:', error);
throw error;
}
}
/**
* Performs comprehensive health check
*/
async healthCheck() {
const health = {
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: this.startTime ? Date.now() - this.startTime : 0,
components: {}
};
let overallHealthy = true;
try {
// Check transport health
const transportHealth = await this.transportManager.healthCheck();
health.components.transports = transportHealth;
if (transportHealth.status !== 'healthy') {
overallHealthy = false;
}
// Check MCP handler health
const mcpStats = this.mcpHandler.getStats();
health.components.mcp = {
status: mcpStats.server.initialized ? 'healthy' : 'unhealthy',
sessions: mcpStats.sessions.active,
tools: mcpStats.tools.count,
resources: mcpStats.resources.count
};
// Check system resources
const memoryUsage = process.memoryUsage();
const memoryHealthy = memoryUsage.heapUsed < 500 * 1024 * 1024; // 500MB
health.components.system = {
status: memoryHealthy ? 'healthy' : 'degraded',
memory: {
used: Math.round(memoryUsage.heapUsed / 1024 / 1024) + 'MB',
total: Math.round(memoryUsage.heapTotal / 1024 / 1024) + 'MB'
},
uptime: process.uptime()
};
if (!memoryHealthy) {
overallHealthy = false;
}
} catch (error) {
health.components.error = {
status: 'unhealthy',
message: error.message
};
overallHealthy = false;
}
health.status = overallHealthy ? 'healthy' : 'degraded';
return health;
}
/**
* Gets comprehensive server statistics
*/
getStats() {
return {
server: {
name: this.config.name,
version: this.config.version,
uptime: this.startTime ? Date.now() - this.startTime : 0,
isRunning: this.isRunning,
startTime: this.startTime ? new Date(this.startTime).toISOString() : null
},
transports: this.transportManager.getStats(),
mcp: this.mcpHandler.getStats(),
system: {
nodeVersion: process.version,
platform: process.platform,
memory: process.memoryUsage(),
pid: process.pid
}
};
}
/**
* Dynamically registers a tool at runtime
*/
registerTool(name, description, handler, inputSchema) {
return this.mcpHandler.registerTool(name, description, handler, inputSchema);
}
/**
* Dynamically registers a resource at runtime
*/
registerResource(uri, name, description, handler, mimeType) {
return this.mcpHandler.registerResource(uri, name, description, handler, mimeType);
}
/**
* Dynamically registers a prompt at runtime
*/
registerPrompt(name, description, handler, argumentSchema) {
return this.mcpHandler.registerPrompt(name, description, handler, argumentSchema);
}
/**
* Gets the current server configuration
*/
getConfig() {
return { ...this.config };
}
/**
* Updates server configuration (requires restart for most changes)
*/
updateConfig(newConfig) {
Object.assign(this.config, newConfig);
console.log('Configuration updated (restart may be required for some changes)');
}
}
export default AutoPublishingMcpServer;