Support non-containerized MCP servers via externalUrl field and add streamable-http session management for HA MCP proof of concept. - Add externalUrl, command, containerPort fields to McpServer schema - Skip Docker orchestration for external servers (virtual instances) - Implement streamable-http proxy with Mcp-Session-Id session management - Parse SSE-framed responses from streamable-http endpoints - Add command passthrough to Docker container creation - Create HA MCP example manifest (examples/ha-mcp.yaml) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
117 lines
3.5 KiB
TypeScript
117 lines
3.5 KiB
TypeScript
import { PrismaClient } from '@prisma/client';
|
|
import { seedMcpServers } from '@mcpctl/db';
|
|
import { loadConfigFromEnv } from './config/index.js';
|
|
import { createServer } from './server.js';
|
|
import { setupGracefulShutdown } from './utils/index.js';
|
|
import {
|
|
McpServerRepository,
|
|
McpProfileRepository,
|
|
McpInstanceRepository,
|
|
ProjectRepository,
|
|
AuditLogRepository,
|
|
} from './repositories/index.js';
|
|
import {
|
|
McpServerService,
|
|
McpProfileService,
|
|
InstanceService,
|
|
ProjectService,
|
|
AuditLogService,
|
|
DockerContainerManager,
|
|
MetricsCollector,
|
|
HealthAggregator,
|
|
BackupService,
|
|
RestoreService,
|
|
AuthService,
|
|
McpProxyService,
|
|
} from './services/index.js';
|
|
import {
|
|
registerMcpServerRoutes,
|
|
registerMcpProfileRoutes,
|
|
registerInstanceRoutes,
|
|
registerProjectRoutes,
|
|
registerAuditLogRoutes,
|
|
registerHealthMonitoringRoutes,
|
|
registerBackupRoutes,
|
|
registerAuthRoutes,
|
|
registerMcpProxyRoutes,
|
|
} from './routes/index.js';
|
|
|
|
async function main(): Promise<void> {
|
|
const config = loadConfigFromEnv();
|
|
|
|
// Database
|
|
const prisma = new PrismaClient({
|
|
datasources: { db: { url: config.databaseUrl } },
|
|
});
|
|
await prisma.$connect();
|
|
|
|
// Seed default servers (upsert, safe to repeat)
|
|
await seedMcpServers(prisma);
|
|
|
|
// Repositories
|
|
const serverRepo = new McpServerRepository(prisma);
|
|
const profileRepo = new McpProfileRepository(prisma);
|
|
const instanceRepo = new McpInstanceRepository(prisma);
|
|
const projectRepo = new ProjectRepository(prisma);
|
|
const auditLogRepo = new AuditLogRepository(prisma);
|
|
|
|
// Orchestrator
|
|
const orchestrator = new DockerContainerManager();
|
|
|
|
// Services
|
|
const serverService = new McpServerService(serverRepo);
|
|
const profileService = new McpProfileService(profileRepo, serverRepo);
|
|
const instanceService = new InstanceService(instanceRepo, serverRepo, orchestrator);
|
|
const projectService = new ProjectService(projectRepo, profileRepo, serverRepo);
|
|
const auditLogService = new AuditLogService(auditLogRepo);
|
|
const metricsCollector = new MetricsCollector();
|
|
const healthAggregator = new HealthAggregator(metricsCollector, orchestrator);
|
|
const backupService = new BackupService(serverRepo, profileRepo, projectRepo);
|
|
const restoreService = new RestoreService(serverRepo, profileRepo, projectRepo);
|
|
const authService = new AuthService(prisma);
|
|
const mcpProxyService = new McpProxyService(instanceRepo, serverRepo);
|
|
|
|
// Server
|
|
const app = await createServer(config, {
|
|
health: {
|
|
checkDb: async () => {
|
|
try {
|
|
await prisma.$queryRaw`SELECT 1`;
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
},
|
|
},
|
|
});
|
|
|
|
// Routes
|
|
registerMcpServerRoutes(app, serverService);
|
|
registerMcpProfileRoutes(app, profileService);
|
|
registerInstanceRoutes(app, instanceService);
|
|
registerProjectRoutes(app, projectService);
|
|
registerAuditLogRoutes(app, auditLogService);
|
|
registerHealthMonitoringRoutes(app, { healthAggregator, metricsCollector });
|
|
registerBackupRoutes(app, { backupService, restoreService });
|
|
registerAuthRoutes(app, { authService });
|
|
registerMcpProxyRoutes(app, {
|
|
mcpProxyService,
|
|
auditLogService,
|
|
authDeps: { findSession: (token) => authService.findSession(token) },
|
|
});
|
|
|
|
// Start
|
|
await app.listen({ port: config.port, host: config.host });
|
|
app.log.info(`mcpd listening on ${config.host}:${config.port}`);
|
|
|
|
// Graceful shutdown
|
|
setupGracefulShutdown(app, {
|
|
disconnectDb: () => prisma.$disconnect(),
|
|
});
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error('Failed to start mcpd:', err);
|
|
process.exit(1);
|
|
});
|