@swoft/platform-contracts
Version:
DDD-compliant dependency injection contracts for Swoft platform - Defines clean architecture boundaries
390 lines (345 loc) • 13 kB
text/typescript
/**
* MCP Semantic Startup Enforcer
*
* MANDATORY startup protocol for all MCP servers to ensure they load and understand
* their domain's semantic navigation before accepting any requests.
*
* This enforces the AGENT-STARTUP-PROTOCOL.md requirements with specific focus on
* semantic navigation to prevent agents from "taking shortcuts" and missing context.
*
* @author Technical Coordinator & Derick
* @since 2025-08-02
*/
import { IDomainSemantics, IEntityRelationship, IWorkflowDefinition } from './SemanticNavigation';
import { readFile } from 'fs/promises';
import { join } from 'path';
/**
* Startup validation result
*/
export interface SemanticStartupResult {
success: boolean;
domainId: string;
entityRelationships: number;
workflows: number;
customSemantics: number;
errors: string[];
warnings: string[];
}
/**
* Domain boundary information from DOMAIN-BOUNDARY-MAP.md
*/
interface DomainBoundary {
domainId: string;
domainRef: string; // e.g., DOM-001
packageName: string;
services: string[];
boundedContexts: string[];
publishedLanguage: string[];
database?: string;
}
/**
* Enforces semantic navigation loading at MCP server startup
*/
export class MCPSemanticStartupEnforcer {
private domainId: string;
private packageName: string;
private semantics?: IDomainSemantics;
private domainBoundary?: DomainBoundary;
private startupResult: SemanticStartupResult;
constructor(domainId: string, packageName: string) {
this.domainId = domainId;
this.packageName = packageName;
this.startupResult = {
success: false,
domainId,
entityRelationships: 0,
workflows: 0,
customSemantics: 0,
errors: [],
warnings: [],
};
}
/**
* Execute mandatory startup sequence
* Throws on critical errors, preventing server startup
*/
async enforceStartup(): Promise<SemanticStartupResult> {
console.log('');
console.log('🚀 ========================================');
console.log('🚀 MCP Semantic Startup Protocol v1.0');
console.log(`🚀 Domain: ${this.domainId}`);
console.log(`🚀 Package: ${this.packageName}`);
console.log('🚀 ========================================');
console.log('');
try {
// Step 1: Load Domain Boundary Map (from AGENT-STARTUP-PROTOCOL.md)
console.log('📍 Step 1: Loading Domain Boundary Map...');
await this.loadDomainBoundaryMap();
// Step 2: Load Domain Semantic Navigation
console.log('📍 Step 2: Loading Semantic Navigation...');
await this.loadSemanticNavigation();
// Step 3: Validate Semantic Understanding
console.log('📍 Step 3: Validating Semantic Understanding...');
await this.validateSemanticUnderstanding();
// Step 4: Display Semantic Summary
console.log('📍 Step 4: Semantic Navigation Summary...');
this.displaySemanticSummary();
this.startupResult.success = true;
console.log('');
console.log('✅ ========================================');
console.log('✅ Semantic startup complete!');
console.log('✅ Ready to provide context-aware navigation');
console.log('✅ ========================================');
console.log('');
} catch (error: any) {
this.startupResult.errors.push(error.message);
console.error('');
console.error('❌ ========================================');
console.error('❌ FATAL: Semantic startup failed!');
console.error(`❌ Error: ${error.message}`);
console.error('❌ Server CANNOT start without semantic context');
console.error('❌ ========================================');
console.error('');
throw error;
}
return this.startupResult;
}
/**
* Load and parse Domain Boundary Map
*/
private async loadDomainBoundaryMap(): Promise<void> {
const workspace = process.env.SWOFT_TEAM_WORKSPACE || process.cwd();
const mapPath = join(workspace, 'docs/architecture/DOMAIN-BOUNDARY-MAP.md');
try {
const content = await readFile(mapPath, 'utf-8');
// Parse the domain boundary map to find our domain
const domainInfo = this.parseDomainBoundaryMap(content);
if (!domainInfo) {
throw new Error(
`Domain '${this.domainId}' not found in Domain Boundary Map. ` +
`Expected format: DOM-XXX (e.g., DOM-001) or domain name (e.g., 'party-manager'). ` +
`Check docs/architecture/DOMAIN-BOUNDARY-MAP.md for valid domains.`
);
}
this.domainBoundary = domainInfo;
console.log(` ✅ Found domain ${domainInfo.domainRef}: ${domainInfo.domainId}`);
console.log(` ✅ Bounded contexts: ${domainInfo.boundedContexts.length}`);
console.log(` ✅ Published language terms: ${domainInfo.publishedLanguage.length}`);
} catch (error: any) {
throw new Error(`Failed to load Domain Boundary Map: ${error.message}`);
}
}
/**
* Parse domain boundary map markdown to extract domain info
*/
private parseDomainBoundaryMap(content: string): DomainBoundary | null {
// Simple parser for the markdown structure
// In production, this would be more robust
console.log(` 🔍 Searching for domain: ${this.domainId}`);
const lines = content.split('\n');
let currentDomain: Partial<DomainBoundary> | null = null;
let foundDomain = false;
for (const line of lines) {
// Look for domain headers like "#### DOM-004: Party Manager Domain"
const domainMatch = line.match(/^####\s+(DOM-\d+):\s+(.+)\s+Domain/);
if (domainMatch) {
const [, domainRef, domainName] = domainMatch;
// Check if the domainId matches either the DOM-XXX format or the domain name
if (
domainRef === this.domainId ||
(domainName &&
domainName.toLowerCase().includes(this.domainId.toLowerCase().replace('-', ' ')))
) {
foundDomain = true;
currentDomain = {
domainRef,
domainId: this.domainId,
packageName: '',
services: [],
boundedContexts: [],
publishedLanguage: [],
};
} else {
foundDomain = false;
}
}
if (foundDomain && currentDomain) {
// Extract package
if (line.includes('**Package**:')) {
const packageMatch = line.match(/`([^`]+)`/);
if (packageMatch) {
currentDomain.packageName = packageMatch[1];
}
}
// Extract bounded contexts
if (line.includes('**Bounded Contexts**:')) {
const contextsMatch = line.match(/contexts?\s*\(([^)]+)\)/);
if (contextsMatch && contextsMatch[1]) {
currentDomain.boundedContexts = contextsMatch[1].split(',').map((c) => c.trim());
}
}
// Extract published language
if (line.includes('**Published Language**:')) {
const termsMatch = line.match(/:\s*(.+)$/);
if (termsMatch && termsMatch[1]) {
currentDomain.publishedLanguage = termsMatch[1].split(',').map((t) => t.trim());
}
}
// Extract database
if (line.includes('**Database**:')) {
const dbMatch = line.match(/:\s*(.+)$/);
if (dbMatch && dbMatch[1]) {
currentDomain.database = dbMatch[1].trim();
}
}
}
}
return currentDomain as DomainBoundary | null;
}
/**
* Load semantic navigation from domain package
*/
private async loadSemanticNavigation(): Promise<void> {
try {
// Try to dynamically import the semantic navigation
// In real implementation, this would be more robust with proper error handling
const semanticExports = await this.tryLoadSemantics();
if (!semanticExports || !semanticExports.domainSemantics) {
// If dynamic import fails, create a minimal semantic definition
console.log(' ⚠️ No semantic navigation found, creating minimal definition');
this.createMinimalSemantics();
return;
}
this.semantics = semanticExports.domainSemantics;
// Count what we loaded
if (this.semantics) {
this.startupResult.entityRelationships = this.semantics.entityRelationships?.length || 0;
this.startupResult.workflows = this.semantics.workflows?.length || 0;
this.startupResult.customSemantics = this.semantics.customSemantics?.length || 0;
}
console.log(` ✅ Loaded ${this.startupResult.entityRelationships} entity relationships`);
console.log(` ✅ Loaded ${this.startupResult.workflows} workflows`);
console.log(` ✅ Loaded ${this.startupResult.customSemantics} custom semantic patterns`);
} catch (error: any) {
this.startupResult.warnings.push(`Could not load semantic navigation: ${error.message}`);
console.log(' ⚠️ Using minimal semantic navigation');
this.createMinimalSemantics();
}
}
/**
* Try to load semantics from various possible locations
*/
private async tryLoadSemantics(): Promise<any> {
// In a real implementation, this would try multiple paths
// For now, we'll return null to trigger minimal semantics
return null;
}
/**
* Create minimal semantic navigation when none exists
*/
private createMinimalSemantics(): void {
this.semantics = {
domainId: this.domainId as any,
mcpServerId: `mcp-${this.domainId}` as any,
entityRelationships: [],
workflows: [],
confidence: 0.5,
lastUpdated: new Date().toISOString(),
};
this.startupResult.warnings.push(
'Using minimal semantic navigation - domain should implement full semantics'
);
}
/**
* Validate the MCP server understands its semantic context
*/
private async validateSemanticUnderstanding(): Promise<void> {
const validations = [
{
name: 'Domain Identity',
check: () => this.domainBoundary !== null,
error: 'Domain not found in Domain Boundary Map',
},
{
name: 'Semantic Navigation',
check: () => this.semantics !== null,
error: 'No semantic navigation loaded',
},
{
name: 'Published Language',
check: () => (this.domainBoundary?.publishedLanguage?.length || 0) > 0,
error: 'No published language terms defined',
},
];
for (const validation of validations) {
if (!validation.check()) {
throw new Error(`Validation failed for ${validation.name}: ${validation.error}`);
}
console.log(` ✅ Validated: ${validation.name}`);
}
}
/**
* Display semantic navigation summary
*/
private displaySemanticSummary(): void {
console.log('');
console.log('📊 Semantic Navigation Summary:');
console.log('================================');
if (this.domainBoundary) {
console.log(`Domain: ${this.domainBoundary.domainRef} - ${this.domainBoundary.domainId}`);
console.log(`Package: ${this.domainBoundary.packageName}`);
console.log(`Database: ${this.domainBoundary.database || 'Not specified'}`);
console.log('');
console.log('Published Language Terms:');
this.domainBoundary.publishedLanguage.forEach((term) => {
console.log(` - ${term}`);
});
}
if (
this.semantics &&
this.semantics.entityRelationships &&
this.semantics.entityRelationships.length > 0
) {
console.log('');
console.log('Entity Relationships:');
this.semantics.entityRelationships.slice(0, 3).forEach((rel) => {
console.log(` - ${rel.sourceEntityType} ${rel.relationshipType} ${rel.targetEntityType}`);
});
if (this.semantics.entityRelationships.length > 3) {
console.log(` ... and ${this.semantics.entityRelationships.length - 3} more`);
}
}
if (this.semantics && this.semantics.workflows && this.semantics.workflows.length > 0) {
console.log('');
console.log('Available Workflows:');
this.semantics.workflows.slice(0, 3).forEach((wf) => {
console.log(` - ${wf.name} (${wf.steps.length} steps)`);
});
if (this.semantics.workflows.length > 3) {
console.log(` ... and ${this.semantics.workflows.length - 3} more`);
}
}
console.log('================================');
}
/**
* Get loaded semantic navigation for use by MCP server
*/
getSemantics(): IDomainSemantics | undefined {
return this.semantics;
}
/**
* Get domain boundary information
*/
getDomainBoundary(): DomainBoundary | undefined {
return this.domainBoundary;
}
}
/**
* Factory function for easy creation
*/
export function createSemanticEnforcer(
domainId: string,
packageName: string
): MCPSemanticStartupEnforcer {
return new MCPSemanticStartupEnforcer(domainId, packageName);
}