Skip to main content
Glama

Analytical MCP Server

semantic_analysis_provider.ts9.92 kB
/** * Semantic Analysis Provider * * Handles semantic relationship extraction through apposition and possessive patterns. */ import { ValidationHelpers } from './validation_helpers.js'; import { RecognizedEntity, EntityType } from './advanced_ner.js'; import { RelationshipType, RelationshipSubtype, Relationship, RelationshipCreationOptions, RelationshipRoleContext } from './relationship_extractor.js'; /** * Context for sentence-level relationship extraction */ export interface SentenceExtractionContext { sentence: string; entities: RecognizedEntity[]; sentenceOffset: number; includeEvidence: boolean; } /** * Semantic Analysis Provider Class * Implements pattern-based semantic relationship extraction */ export class SemanticAnalysisProvider { /** * Extract relationships based on apposition patterns (X, the Y of Z) */ extractAppositionRelationships(context: SentenceExtractionContext, createRelationshipFn: (options: RelationshipCreationOptions) => Relationship): Relationship[] { // Apply ValidationHelpers validation ValidationHelpers.throwIfInvalid(ValidationHelpers.validateNonEmptyString(context.sentence)); ValidationHelpers.throwIfInvalid(ValidationHelpers.validateDataArray(context.entities)); const relationships: Relationship[] = []; // Look for patterns like "X, the Y of Z" const appositionRegex = /(\w+),\s+the\s+(\w+)\s+of\s+(\w+)/gi; let match; while ((match = appositionRegex.exec(context.sentence)) !== null) { const fullMatch = match[0]; const firstPart = match[1]; // X const role = match[2]; // Y const secondPart = match[3]; // Z // Find entities that match the first and second parts const firstEntity = context.entities.find(entity => context.sentence.substring( entity.startIndex - context.sentenceOffset, entity.endIndex - context.sentenceOffset ).includes(firstPart) ); const secondEntity = context.entities.find(entity => context.sentence.substring( entity.startIndex - context.sentenceOffset, entity.endIndex - context.sentenceOffset ).includes(secondPart) ); if (firstEntity && secondEntity) { // Determine relationship type based on entities and role const relationshipInfo = this.determineRelationshipFromRole({ role: role.toLowerCase(), firstEntity, secondEntity }); if (relationshipInfo) { const evidence = context.includeEvidence ? fullMatch : ''; const relationship = createRelationshipFn({ type: relationshipInfo.type, subtype: relationshipInfo.subtype, sourceEntity: firstEntity, sourceRole: relationshipInfo.sourceRole, targetEntity: secondEntity, targetRole: relationshipInfo.targetRole, evidence, confidence: relationshipInfo.confidence, startIndex: Math.min(firstEntity.startIndex, secondEntity.startIndex), endIndex: Math.max(firstEntity.endIndex, secondEntity.endIndex) }); relationships.push(relationship); } } } return relationships; } /** * Extract relationships based on possessive patterns (X's Y) */ extractPossessiveRelationships(context: SentenceExtractionContext, createRelationshipFn: (options: RelationshipCreationOptions) => Relationship): Relationship[] { // Apply ValidationHelpers validation ValidationHelpers.throwIfInvalid(ValidationHelpers.validateNonEmptyString(context.sentence)); ValidationHelpers.throwIfInvalid(ValidationHelpers.validateDataArray(context.entities)); const relationships: Relationship[] = []; // Look for possessive patterns like "X's Y" const possessiveRegex = /(\w+)'s\s+(\w+)/gi; let match; while ((match = possessiveRegex.exec(context.sentence)) !== null) { const fullMatch = match[0]; const owner = match[1]; // X const possession = match[2]; // Y // Find entities that match the owner and possession const ownerEntity = context.entities.find(entity => context.sentence.substring( entity.startIndex - context.sentenceOffset, entity.endIndex - context.sentenceOffset ).includes(owner) ); const possessionEntity = context.entities.find(entity => context.sentence.substring( entity.startIndex - context.sentenceOffset, entity.endIndex - context.sentenceOffset ).includes(possession) ); if (ownerEntity && possessionEntity) { // Determine relationship type based on entities const relationshipType = this.determinePossessiveRelationshipType( ownerEntity, possessionEntity ); const evidence = context.includeEvidence ? fullMatch : ''; const relationship = createRelationshipFn({ type: relationshipType.type, subtype: undefined, // No specific subtype for possessive relationships sourceEntity: ownerEntity, sourceRole: relationshipType.sourceRole, targetEntity: possessionEntity, targetRole: relationshipType.targetRole, evidence, confidence: 0.7, // Default confidence for possessive relationships startIndex: Math.min(ownerEntity.startIndex, possessionEntity.startIndex), endIndex: Math.max(ownerEntity.endIndex, possessionEntity.endIndex) }); relationships.push(relationship); } } return relationships; } /** * Determine relationship type and roles from apposition role context */ private determineRelationshipFromRole(context: RelationshipRoleContext): { type: RelationshipType; subtype?: RelationshipSubtype; sourceRole: string; targetRole: string; confidence: number; } | null { // Family relationships if ( context.role === 'father' || context.role === 'mother' || context.role === 'parent' ) { return { type: RelationshipType.FAMILY, subtype: RelationshipSubtype.PARENT, sourceRole: context.role, targetRole: 'child', confidence: 0.8 }; } if ( context.role === 'son' || context.role === 'daughter' || context.role === 'child' ) { return { type: RelationshipType.FAMILY, subtype: RelationshipSubtype.CHILD, sourceRole: context.role, targetRole: 'parent', confidence: 0.8 }; } if ( context.role === 'brother' || context.role === 'sister' || context.role === 'sibling' ) { return { type: RelationshipType.FAMILY, subtype: RelationshipSubtype.SIBLING, sourceRole: context.role, targetRole: 'sibling', confidence: 0.8 }; } // Employment relationships if ( context.role === 'ceo' || context.role === 'president' || context.role === 'director' ) { return { type: RelationshipType.EMPLOYMENT, subtype: RelationshipSubtype.CEO, sourceRole: context.role, targetRole: 'organization', confidence: 0.8 }; } if ( context.role === 'employee' || context.role === 'worker' || context.role === 'staff' ) { return { type: RelationshipType.EMPLOYMENT, subtype: RelationshipSubtype.EMPLOYEE, sourceRole: 'employee', targetRole: 'employer', confidence: 0.8 }; } if ( context.role === 'founder' || context.role === 'creator' || context.role === 'owner' ) { return { type: RelationshipType.OWNERSHIP, subtype: RelationshipSubtype.FOUNDER, sourceRole: context.role, targetRole: 'organization', confidence: 0.8 }; } // Generic relationship return { type: RelationshipType.OTHER, sourceRole: 'subject', targetRole: 'object', confidence: 0.6 }; } /** * Determine relationship type for possessive relationship */ private determinePossessiveRelationshipType( ownerEntity: RecognizedEntity, possessionEntity: RecognizedEntity ): { type: RelationshipType; sourceRole: string; targetRole: string; } { // Person owns Organization if ( ownerEntity.type === EntityType.PERSON && possessionEntity.type === EntityType.ORGANIZATION ) { return { type: RelationshipType.OWNERSHIP, sourceRole: 'owner', targetRole: 'owned' }; } // Organization owns Organization if ( ownerEntity.type === EntityType.ORGANIZATION && possessionEntity.type === EntityType.ORGANIZATION ) { return { type: RelationshipType.ACQUISITION, sourceRole: 'parent', targetRole: 'subsidiary' }; } // Person and Location if ( ownerEntity.type === EntityType.PERSON && possessionEntity.type === EntityType.LOCATION ) { return { type: RelationshipType.LOCATION, sourceRole: 'resident', targetRole: 'residence' }; } // Organization and Location if ( ownerEntity.type === EntityType.ORGANIZATION && possessionEntity.type === EntityType.LOCATION ) { return { type: RelationshipType.HEADQUARTERS, sourceRole: 'organization', targetRole: 'location' }; } // Default to general ownership return { type: RelationshipType.OWNERSHIP, sourceRole: 'owner', targetRole: 'possession' }; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/quanticsoul4772/analytical-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server