Skip to main content
Glama

Practera MCP Server

by intersective
project-brief-service.ts3.88 kB
import fs from 'fs'; import { PROJECT_BRIEFS_PATH } from './data-paths.js'; import { isSkillMatch } from './search-utils.js'; export interface ProjectBrief { project_title: string; industry: string; project_type: string; client_background: string; problem_statement: string; project_scope: string; focus_area: string; other_notes: string; technical_skills_required: string[]; professional_skills_required: string[]; duration_weeks: number; deliverables: string[]; } /** * Service for searching and retrieving project briefs */ export class ProjectBriefService { private projectBriefs: ProjectBrief[] = []; private initialized: boolean = false; constructor(private dataPath: string = PROJECT_BRIEFS_PATH) {} /** * Initialize the service by loading project briefs from the JSON file */ async initialize(): Promise<void> { if (this.initialized) return; try { const fileData = await fs.promises.readFile(this.dataPath, 'utf8'); this.projectBriefs = JSON.parse(fileData); this.initialized = true; console.log(`Loaded ${this.projectBriefs.length} project briefs from ${this.dataPath}`); } catch (error) { console.error('Error loading project briefs:', error); // Initialize with empty array on error this.projectBriefs = []; this.initialized = true; } } /** * Search project briefs by skill using advanced matching techniques * @param skill The skill to search for * @param limit Maximum number of results to return * @returns Array of matching project briefs */ async searchBySkill(skill: string, limit: number = 5): Promise<ProjectBrief[]> { await this.initialize(); // Advanced search for exact matches first (using thesaurus, stemming, etc.) const exactMatches = await this.filterBriefsBySkill(this.projectBriefs, skill, true); // Get remaining briefs (not exact matches) const remainingBriefs = this.projectBriefs.filter(brief => !exactMatches.includes(brief) ); // Then look for partial matches const partialMatches = await this.filterBriefsBySkill(remainingBriefs, skill, false); // Combine results, prioritizing exact matches const results = [...exactMatches, ...partialMatches].slice(0, limit); return results; } /** * Helper method to filter briefs by skill * @param briefs The list of briefs to filter * @param skill The skill to search for * @param exactMatch Whether to perform exact matching * @returns Filtered array of briefs */ private async filterBriefsBySkill( briefs: ProjectBrief[], skill: string, exactMatch: boolean ): Promise<ProjectBrief[]> { const results: ProjectBrief[] = []; for (const brief of briefs) { // Check technical skills for (const techSkill of brief.technical_skills_required) { if (await isSkillMatch(techSkill, skill)) { results.push(brief); break; // Found a match, no need to check other skills } } // If already found in technical skills, skip to next brief if (results.includes(brief)) continue; // Check professional skills for (const profSkill of brief.professional_skills_required) { if (await isSkillMatch(profSkill, skill)) { results.push(brief); break; // Found a match, no need to check other skills } } } return results; } /** * Get all project briefs * @param limit Maximum number of results to return * @returns Array of all project briefs up to the limit */ async getAllBriefs(limit: number = 10): Promise<ProjectBrief[]> { await this.initialize(); return this.projectBriefs.slice(0, limit); } } // Export singleton instance export const projectBriefService = new ProjectBriefService();

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/intersective/practera-mcp-server'

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