Skip to main content
Glama
bsmi021

MCP File Context Server

by bsmi021

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
NameRequiredDescriptionDefault
refreshNoWhether to refresh file selection before generating context

Implementation Reference

  • 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'}`
            );
        }
    }
  • 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)];
        }
    } 
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden but only states what the tool does without disclosing behavioral traits. It doesn't mention whether this is a read-only operation, if it requires specific permissions, what 'repository context' includes (e.g., files, metadata), or any rate limits, leaving significant gaps in understanding its behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that states the tool's purpose without unnecessary words. It's appropriately sized and front-loaded, though it could be slightly more informative given the lack of other details, but it earns its place by being clear and direct.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no annotations, no output schema, and a vague purpose, the description is incomplete. It doesn't explain what 'repository context' returns, how it relates to 'current profile settings', or provide enough detail for an agent to understand the tool's full scope and usage in context with sibling tools, leaving significant gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 1 parameter with 100% description coverage, so the schema fully documents the 'refresh' parameter. The description adds no parameter-specific information beyond what the schema provides, but with only one well-documented parameter, the baseline is high, and the description doesn't detract from this.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose3/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states the tool 'Get repository context based on current profile settings', which provides a general purpose (verb+resource). However, it's vague about what 'repository context' entails and doesn't distinguish from sibling tools like 'read_context' or 'getFiles', leaving ambiguity about scope and differentiation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit guidance on when to use this tool versus alternatives like 'read_context' or 'getFiles' is provided. The description implies usage for obtaining context related to profiles, but lacks context about prerequisites, exclusions, or comparisons with sibling tools, offering minimal direction.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

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/bsmi021/mcp-file-context-server'

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