Skip to main content
Glama

3GPP MCP Server

by edhijlu
api-manager.ts13.6 kB
/** * 3GPP MCP Server - API Manager * * Copyright (c) 2024, 3GPP MCP Contributors * Licensed under the BSD-3-Clause License */ import { TSpecLLMClient, TSpecSearchRequest, TSpecSearchResponse } from './tspec-llm-client'; import { TGPPApiClient, SpecificationMetadata, ReleaseInfo, WorkingGroupInfo } from './tgpp-api-client'; export interface APIConfig { huggingFaceToken?: string; enableCaching?: boolean; cacheTimeout?: number; } export interface EnhancedSearchRequest { query: string; include_tspec_content?: boolean; include_official_metadata?: boolean; max_results?: number; series_filter?: string[]; release_filter?: string[]; } export interface EnhancedSearchResult { tspec_results?: TSpecSearchResponse; official_metadata?: SpecificationMetadata[]; related_specifications?: SpecificationMetadata[]; working_group_info?: WorkingGroupInfo[]; release_info?: ReleaseInfo[]; } export class APIManager { private tspecClient: TSpecLLMClient; private tgppClient: TGPPApiClient; private config: APIConfig; constructor(config: APIConfig = {}) { this.config = { enableCaching: true, cacheTimeout: 3600, ...config }; this.tspecClient = new TSpecLLMClient(config.huggingFaceToken); this.tgppClient = new TGPPApiClient(); } async enhancedSearch(request: EnhancedSearchRequest): Promise<EnhancedSearchResult> { const result: EnhancedSearchResult = {}; try { // Parallel execution for better performance const promises: Promise<any>[] = []; // TSpec-LLM content search if (request.include_tspec_content !== false) { const tspecRequest: TSpecSearchRequest = { query: request.query, max_results: request.max_results || 5, series_filter: request.series_filter, release_filter: request.release_filter }; promises.push( this.tspecClient.searchSpecifications(tspecRequest) .then(res => { result.tspec_results = res; }) .catch(err => console.warn('TSpec search failed:', err)) ); } // Official metadata search if (request.include_official_metadata !== false) { const filters = { release: request.release_filter?.[0], series: request.series_filter?.[0] }; promises.push( this.tgppClient.searchSpecifications(request.query, filters) .then(res => { result.official_metadata = res; }) .catch(err => console.warn('Official metadata search failed:', err)) ); } await Promise.all(promises); // Enhance with related information await this.addRelatedInformation(result, request); return result; } catch (error) { throw new Error(`Enhanced search failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async getSpecificationDetails(specId: string): Promise<{ metadata: SpecificationMetadata; tspec_content?: TSpecSearchResponse; working_group: WorkingGroupInfo; release: ReleaseInfo; }> { try { // Get official metadata const metadata = await this.tgppClient.getSpecificationMetadata(specId); // Get related TSpec content const tspecContent = await this.tspecClient.searchSpecifications({ query: specId, max_results: 3 }); // Get working group info const workingGroup = await this.tgppClient.getWorkingGroupInfo(metadata.working_group); // Get release info const release = await this.tgppClient.getReleaseInfo(metadata.release); return { metadata, tspec_content: tspecContent, working_group: workingGroup, release }; } catch (error) { throw new Error(`Failed to get specification details: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async compareSpecifications(specIds: string[]): Promise<{ specifications: SpecificationMetadata[]; comparison_matrix: any; evolution_analysis?: any; }> { try { // Get metadata for all specifications const specifications = await Promise.all( specIds.map(id => this.tgppClient.getSpecificationMetadata(id)) ); // Create comparison matrix const comparisonMatrix = this.createComparisonMatrix(specifications); // Add evolution analysis if comparing across releases const releases = [...new Set(specifications.map(spec => spec.release))]; let evolutionAnalysis; if (releases.length > 1) { evolutionAnalysis = await this.analyzeEvolution(specifications); } return { specifications, comparison_matrix: comparisonMatrix, evolution_analysis: evolutionAnalysis }; } catch (error) { throw new Error(`Specification comparison failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async findImplementationRequirements(feature: string, context?: { domain?: string; complexity_level?: 'basic' | 'intermediate' | 'advanced'; }): Promise<{ requirements: any[]; related_specifications: SpecificationMetadata[]; implementation_guidance: string[]; }> { try { const searchQuery = `${feature} implementation requirements ${context?.domain || ''}`; // Search TSpec-LLM for implementation details const tspecResults = await this.tspecClient.searchSpecifications({ query: searchQuery, max_results: 10 }); // Extract requirements from content const requirements = this.extractRequirements(tspecResults); // Find related specifications const relatedSpecs = await this.tgppClient.searchSpecifications(feature); // Generate implementation guidance const implementationGuidance = this.generateImplementationGuidance( feature, tspecResults, context ); return { requirements, related_specifications: relatedSpecs, implementation_guidance: implementationGuidance }; } catch (error) { throw new Error(`Failed to find implementation requirements: ${error instanceof Error ? error.message : 'Unknown error'}`); } } private async addRelatedInformation(result: EnhancedSearchResult, request: EnhancedSearchRequest): Promise<void> { try { // Extract specification IDs from TSpec results const specIds = result.tspec_results?.results .map(r => r.source_specification) .filter((id, index, arr) => arr.indexOf(id) === index) // Remove duplicates .slice(0, 5); // Limit to 5 specs if (specIds && specIds.length > 0) { // Get related specifications const relatedSpecs = await Promise.all( specIds.map(id => this.tgppClient.getSpecificationMetadata(id).catch(() => null)) ); result.related_specifications = relatedSpecs.filter(spec => spec !== null) as SpecificationMetadata[]; // Get working group information const workingGroups = [...new Set( result.related_specifications?.map(spec => spec.working_group) || [] )]; const wgInfo = await Promise.all( workingGroups.map(wg => this.tgppClient.getWorkingGroupInfo(wg).catch(() => null)) ); result.working_group_info = wgInfo.filter(wg => wg !== null) as WorkingGroupInfo[]; // Get release information const releases = [...new Set( result.related_specifications?.map(spec => spec.release) || [] )]; const releaseInfo = await Promise.all( releases.map(rel => this.tgppClient.getReleaseInfo(rel).catch(() => null)) ); result.release_info = releaseInfo.filter(rel => rel !== null) as ReleaseInfo[]; } } catch (error) { console.warn('Failed to add related information:', error); } } private createComparisonMatrix(specifications: SpecificationMetadata[]): any { return { working_groups: specifications.map(spec => ({ id: spec.id, working_group: spec.working_group })), releases: specifications.map(spec => ({ id: spec.id, release: spec.release, publication_date: spec.publication_date })), dependencies: specifications.map(spec => ({ id: spec.id, dependencies: spec.dependencies })), focus_areas: specifications.map(spec => ({ id: spec.id, keywords: spec.keywords })) }; } private async analyzeEvolution(specifications: SpecificationMetadata[]): Promise<any> { // Group by base specification (without release info) const groups = specifications.reduce((acc, spec) => { const baseId = spec.id.replace(/v\d+\.\d+\.\d+/, ''); if (!acc[baseId]) acc[baseId] = []; acc[baseId].push(spec); return acc; }, {} as { [key: string]: SpecificationMetadata[] }); return Object.entries(groups).map(([baseId, specs]) => ({ specification: baseId, evolution: specs .sort((a, b) => a.release.localeCompare(b.release)) .map(spec => ({ release: spec.release, version: spec.version, major_changes: this.inferMajorChanges(spec) })) })); } private inferMajorChanges(spec: SpecificationMetadata): string[] { // This would be enhanced with actual change analysis const changes: string[] = []; if (spec.release === 'Rel-17') { changes.push('Enhanced features for 5G Advanced'); } if (spec.working_group === 'SA5' && spec.keywords.includes('charging')) { changes.push('Converged charging enhancements'); } if (spec.keywords.includes('security')) { changes.push('Security protocol updates'); } return changes; } private extractRequirements(tspecResults: TSpecSearchResponse): any[] { return tspecResults.results.map(result => ({ source: result.source_specification, section: result.section, type: this.classifyRequirement(result.content), description: this.extractRequirementText(result.content), mandatory: this.isMandatoryRequirement(result.content), technical_details: this.extractTechnicalDetails(result.content) })); } private classifyRequirement(content: string): string { const lower = content.toLowerCase(); if (lower.includes('must') || lower.includes('mandatory')) return 'mandatory'; if (lower.includes('should') || lower.includes('recommended')) return 'recommended'; if (lower.includes('may') || lower.includes('optional')) return 'optional'; return 'informational'; } private extractRequirementText(content: string): string { // Extract the most relevant requirement sentence const sentences = content.split(/[.!?]+/); return sentences.find(sentence => sentence.toLowerCase().includes('requirement') || sentence.toLowerCase().includes('must') || sentence.toLowerCase().includes('shall') )?.trim() || sentences[0]?.trim() || ''; } private isMandatoryRequirement(content: string): boolean { const lower = content.toLowerCase(); return lower.includes('must') || lower.includes('shall') || lower.includes('mandatory'); } private extractTechnicalDetails(content: string): string[] { const details: string[] = []; const lines = content.split('\n'); lines.forEach(line => { const trimmed = line.trim(); if (trimmed.match(/^\d+\./) || trimmed.startsWith('- ') || trimmed.startsWith('* ')) { details.push(trimmed); } }); return details.slice(0, 5); // Limit technical details } private generateImplementationGuidance( feature: string, tspecResults: TSpecSearchResponse, context?: any ): string[] { const guidance: string[] = []; // Add basic guidance based on feature type if (feature.toLowerCase().includes('charging')) { guidance.push('1. Review TS 32.290 for 5G converged charging architecture'); guidance.push('2. Implement HTTP/2 REST API interfaces for CHF integration'); guidance.push('3. Ensure compliance with service-based architecture principles'); } if (feature.toLowerCase().includes('security')) { guidance.push('1. Implement 5G-AKA authentication as per TS 33.501'); guidance.push('2. Ensure SUCI/SUPI privacy protection mechanisms'); guidance.push('3. Validate key derivation and management procedures'); } if (feature.toLowerCase().includes('handover') || feature.toLowerCase().includes('mobility')) { guidance.push('1. Configure measurement parameters according to TS 38.331'); guidance.push('2. Implement preparation and execution phases within timing constraints'); guidance.push('3. Test with various radio conditions and load scenarios'); } // Add context-specific guidance if (context?.complexity_level === 'basic') { guidance.unshift('0. Start with fundamental concepts and basic implementation'); } else if (context?.complexity_level === 'advanced') { guidance.push('4. Consider advanced optimization and edge case handling'); } return guidance; } getCacheStats(): { tspec_cache: { keys: number; hits: number; misses: number }; tgpp_cache: { keys: number; hits: number; misses: number }; } { return { tspec_cache: this.tspecClient.getCacheStats(), tgpp_cache: this.tgppClient.getCacheStats() }; } clearAllCaches(): void { this.tspecClient.clearCache(); this.tgppClient.clearCache(); } }

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/edhijlu/3gpp-mcp-server'

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