get_profile_context
Retrieves repository context using current profile settings to analyze code files and support development workflows.
Instructions
Get repository context based on current profile settings
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| refresh | No | Whether to refresh file selection before generating context |
Implementation Reference
- src/index.ts:1044-1183 (handler)Main handler function that executes the get_profile_context tool logic: fetches profile state, processes full content and outline files with analysis, generates project context with stats, prompts, and structure.private async handleGetProfileContext(args: any) { try { const { refresh = false } = args; const spec = await this.profileService.getActiveProfile(); const state = this.profileService.getState(); if (refresh || !state.full_files) { await this.profileService.selectFiles(); } // Read full content files const files = await Promise.all(state.full_files.map(async (path) => { try { const metadata = await this.getFileMetadata(path); const content = await this.processFile(path, metadata); const analysis = await this.codeAnalysisService.analyzeCode(content.content, path); return { ...content, analysis: { metrics: analysis.metrics, complexity: analysis.complexity_metrics.cyclomaticComplexity, maintainability: analysis.complexity_metrics.maintainabilityIndex, quality_issues: analysis.metrics.quality.longLines + analysis.metrics.quality.duplicateLines + analysis.metrics.quality.complexFunctions } }; } catch (error) { await this.loggingService.error('Error processing file', error as Error, { filePath: path, operation: 'get_profile_context' }); return null; } })).then(results => results.filter((f): f is NonNullable<typeof f> => f !== null)); // Generate outlines for selected files const outlines = await Promise.all(state.outline_files.map(async (path) => { try { const metadata = await this.getFileMetadata(path); const content = await this.processFile(path, metadata); const analysis = await this.codeAnalysisService.analyzeCode(content.content, path); return { path, outline: analysis.outline, metadata, analysis: { metrics: analysis.metrics, complexity: analysis.complexity_metrics.cyclomaticComplexity, maintainability: analysis.complexity_metrics.maintainabilityIndex } }; } catch (error) { await this.loggingService.error('Error generating outline', error as Error, { filePath: path, operation: 'get_profile_context' }); return null; } })).then(results => results.filter((o): o is NonNullable<typeof o> => o !== null)); const structure = await this.generateStructure(spec.profile.settings.no_media); // Get prompt if profile specifies it let prompt = ''; if (spec.profile.prompt) { prompt = await this.templateService.getPrompt(); } // Enhanced context with LLM-friendly metadata const context = { project_name: path.basename(process.cwd()), project_root: process.cwd(), timestamp: new Date(state.timestamp).toISOString(), profile: { name: spec.profile.name, description: spec.profile.description || 'Default profile settings', settings: spec.profile.settings }, stats: { total_files: files.length + outlines.length, full_content_files: files.length, outline_files: outlines.length, excluded_files: state.excluded_files?.length || 0, code_metrics: { total_lines: files.reduce((sum, f) => sum + f.analysis.metrics.lineCount.total, 0), code_lines: files.reduce((sum, f) => sum + f.analysis.metrics.lineCount.code, 0), comment_lines: files.reduce((sum, f) => sum + f.analysis.metrics.lineCount.comment, 0), average_complexity: files.length > 0 ? files.reduce((sum, f) => sum + f.analysis.complexity, 0) / files.length : 0, quality_issues: files.reduce((sum, f) => sum + f.analysis.quality_issues, 0) } }, prompt, files: files.map(f => ({ ...f, language: path.extname(f.path).slice(1) || 'text', metadata: { ...f.metadata, relative_path: path.relative(process.cwd(), f.path), file_type: this.getFileType(f.path), last_modified_relative: this.getRelativeTime(new Date(f.metadata.modifiedTime)), analysis: f.analysis } })), highlights: outlines.map(o => ({ ...o, metadata: { ...o.metadata, relative_path: path.relative(process.cwd(), o.path), file_type: this.getFileType(o.path), last_modified_relative: this.getRelativeTime(new Date(o.metadata.modifiedTime)), analysis: o.analysis } })), folder_structure_diagram: structure, tools: { file_access: { name: 'lc-get-files', description: 'Retrieve specific file contents', example: { path: process.cwd(), files: ['example/path/file.ts'] } }, search: { name: 'search_context', description: 'Search for patterns in files', example: { pattern: 'searchTerm', path: process.cwd() } }, changes: { name: 'lc-list-modified-files', description: 'Track file changes since context generation', example: { timestamp: state.timestamp } } } }; return this.createJsonResponse(context); } catch (error) { throw new McpError( ErrorCode.InternalError, `Failed to get profile context: ${error instanceof Error ? error.message : 'Unknown error'}` ); } }
- src/index.ts:388-400 (schema)Input schema definition for the get_profile_context tool, declared in server capabilities.get_profile_context: { description: 'Get repository context based on current profile settings. Includes directory structure, file contents, and code outlines based on profile configuration.', inputSchema: { type: 'object', properties: { refresh: { type: 'boolean', description: 'Whether to refresh file selection before generating context', default: false } } } },
- src/index.ts:1616-1617 (registration)Registration of the get_profile_context handler in the CallToolRequestSchema switch statement.return await this.handleGetProfileContext(request.params.arguments); case 'generate_outline':
- src/index.ts:1532-1544 (registration)Tool listing and schema in ListToolsRequestSchema handler response.name: 'get_profile_context', description: 'Get repository context based on current profile settings', inputSchema: { type: 'object', properties: { refresh: { type: 'boolean', description: 'Whether to refresh file selection before generating context', default: false } } } },
- ProfileService class providing helper methods for profile management, file selection based on profiles, state handling - used extensively by the tool handler.export class ProfileService { private config: ProfileConfig; private state: ProfileState; private projectRoot: string; private activeProfile: Profile | null; private readonly configPath: string; private logger?: LoggingService; constructor(projectRoot: string, logger?: LoggingService) { this.logger = logger; this.logger?.debug('ProfileService initializing', { projectRoot, operation: 'profile_service_init' }); this.projectRoot = projectRoot; this.configPath = path.join(projectRoot, '.llm-context', 'config.toml'); this.config = this.createDefaultConfig(); this.state = { profile_name: 'code', full_files: [], outline_files: [], excluded_files: [], timestamp: Date.now() }; this.activeProfile = null; } private createDefaultConfig(): ProfileConfig { this.logger?.debug('Creating default configuration', { operation: 'create_default_config' }); const defaultProfile = this.createDefaultProfile(); return { profiles: { code: defaultProfile, 'code-prompt': { ...defaultProfile, name: 'code-prompt', prompt: 'prompt.md' } }, default_profile: 'code' }; } private createDefaultProfile(): Profile { return { name: 'code', gitignores: { full_files: DEFAULT_IGNORE_PATTERNS, outline_files: DEFAULT_IGNORE_PATTERNS }, only_includes: { full_files: INCLUDE_ALL, outline_files: INCLUDE_ALL }, settings: { no_media: true, with_user_notes: false } }; } public async initialize(): Promise<void> { await this.logger?.info('ProfileService starting initialization', { projectRoot: this.projectRoot, configPath: this.configPath, operation: 'profile_service_init' }); await this.loadConfig(); await this.loadState(); } private async loadConfig(): Promise<void> { const configPath = path.join(this.projectRoot, '.llm-context'); try { await fs.mkdir(configPath, { recursive: true }); await this.logger?.debug('Created config directory', { configPath, operation: 'load_config' }); // Create default config if it doesn't exist const configFile = path.join(configPath, 'config.json'); if (!await this.fileExists(configFile)) { await this.logger?.info('Creating default config file', { configFile, operation: 'load_config' }); const defaultConfig = this.createDefaultConfig(); await fs.writeFile(configFile, JSON.stringify(defaultConfig, null, 2)); this.config = defaultConfig; } else { await this.logger?.debug('Loading existing config file', { configFile, operation: 'load_config' }); const content = await fs.readFile(configFile, 'utf8'); this.config = JSON.parse(content); } // Log available profiles await this.logger?.info('Configuration loaded successfully', { availableProfiles: Object.keys(this.config.profiles), currentProfile: this.state.profile_name, operation: 'load_config' }); } catch (error) { await this.logger?.error('Failed to initialize configuration', error as Error, { projectRoot: this.projectRoot, configPath, operation: 'load_config' }); throw error; } } private async loadState(): Promise<void> { const statePath = path.join(this.projectRoot, '.llm-context', 'state.json'); if (!await this.fileExists(statePath)) { await this.logger?.info('Creating default state file', { statePath, operation: 'load_state' }); await fs.writeFile(statePath, JSON.stringify(this.state, null, 2)); } else { await this.logger?.debug('Loading existing state file', { statePath, operation: 'load_state' }); const content = await fs.readFile(statePath, 'utf8'); this.state = JSON.parse(content); } } private async fileExists(filePath: string): Promise<boolean> { try { await fs.access(filePath); return true; } catch { return false; } } public async setProfile(profileName: string): Promise<void> { await this.logger?.info('Attempting to set profile', { profileName, availableProfiles: Object.keys(this.config.profiles), operation: 'set_profile' }); if (!this.config.profiles[profileName]) { throw new Error(`Profile '${profileName}' does not exist. Available profiles: ${Object.keys(this.config.profiles).join(', ')}`); } this.state = { ...this.state, profile_name: profileName, timestamp: Date.now() }; await this.saveState(); await this.logger?.info('Successfully set profile', { profileName, operation: 'set_profile' }); } public getContextSpec(): ContextSpec { const profile = this.resolveProfile(this.state.profile_name); return { profile, state: this.state }; } private resolveProfile(profileName: string): Profile { const profile = this.config.profiles[profileName]; if (!profile) { this.logger?.warning('Profile not found, using default', { requestedProfile: profileName, defaultProfile: this.config.default_profile, operation: 'resolve_profile' }); return this.config.profiles[this.config.default_profile]; } return profile; } private async saveState(): Promise<void> { const statePath = path.join(this.projectRoot, '.llm-context', 'state.json'); await fs.writeFile(statePath, JSON.stringify(this.state, null, 2)); await this.logger?.debug('State saved successfully', { statePath, state: this.state, operation: 'save_state' }); } public async updateFileSelection(fullFiles: string[], outlineFiles: string[]): Promise<void> { this.state = { ...this.state, full_files: fullFiles, outline_files: outlineFiles, timestamp: Date.now() }; await this.saveState(); } public getProfile(): Profile { return this.resolveProfile(this.state.profile_name); } public getState(): ProfileState { return this.state; } public async getActiveProfile(): Promise<{ profile: Profile }> { if (!this.activeProfile) { throw new Error('No active profile'); } return { profile: this.activeProfile }; } public async selectFiles(): Promise<void> { if (!this.activeProfile) { throw new Error('No active profile'); } const fullFiles = await this.getFilteredFiles( this.activeProfile.gitignores.full_files, this.activeProfile.only_includes.full_files ); const outlineFiles = await this.getFilteredFiles( this.activeProfile.gitignores.outline_files, this.activeProfile.only_includes.outline_files ); this.state = { ...this.state, full_files: fullFiles, outline_files: outlineFiles, timestamp: Date.now() }; await this.saveState(); } private async getFilteredFiles(ignorePatterns: string[], includePatterns: string[]): Promise<string[]> { const allFiles: string[] = []; for (const pattern of includePatterns) { const files = await globAsync(pattern, { ignore: ignorePatterns, nodir: true, dot: true }) as string[]; allFiles.push(...files); } return [...new Set(allFiles)]; } }