Skip to main content
Glama

Code Reasoning MCP Server

manager.ts11 kB
/** * @fileoverview Prompt manager for MCP prompts. * * This class handles the management of prompts, including registration, * validation, and application of prompt templates. * * It implements the standard MCP CompleteRequestSchema protocol for * providing auto-completion of prompt arguments with previously stored values. */ import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; import { Prompt, PromptResult } from './types.js'; import { CODE_REASONING_PROMPTS, PROMPT_TEMPLATES } from './templates.js'; import { CONFIG_DIR, PROMPT_VALUES_FILE } from '../utils/config.js'; interface StoredPromptValues { global: Record<string, string>; prompts: Record<string, Record<string, string>>; } /** * Manages prompt templates and their operations. * Uses the CompleteRequestSchema MCP protocol for argument completion. */ export class PromptManager { private prompts: Record<string, Prompt>; private templates: Record<string, (args: Record<string, string>) => PromptResult>; private storedValues: StoredPromptValues = { global: {}, prompts: {} }; private valuesFilePath?: string; private persistenceEnabled = false; /** * Creates a new PromptManager instance with default code reasoning prompts. * * @param configDir Optional directory for configuration files. Defaults to the centralized CONFIG_DIR. */ constructor(configDir?: string) { this.prompts = { ...CODE_REASONING_PROMPTS }; this.templates = { ...PROMPT_TEMPLATES }; // Use provided config directory or default to CONFIG_DIR const resolvedConfigDir = configDir || CONFIG_DIR; this.ensureDirectoryExists(resolvedConfigDir, 'main config directory'); console.info(`Using config directory: ${resolvedConfigDir}`); this.initializeValueStorage(resolvedConfigDir); console.info('PromptManager initialized with', Object.keys(this.prompts).length, 'prompts'); } private ensureDirectoryExists(directoryPath: string, description: string): void { try { const createdPath = fs.mkdirSync(directoryPath, { recursive: true }); if (createdPath) { console.info(`Created ${description}: ${directoryPath}`); } } catch (err) { const error = err as Error; throw new Error(`Failed to create ${description}: ${directoryPath}`, { cause: error }); } } private initializeValueStorage(configDir: string): void { const defaultPath = configDir === path.dirname(PROMPT_VALUES_FILE) ? PROMPT_VALUES_FILE : path.join(configDir, 'prompt_values.json'); const fallbackPath = path.join(os.tmpdir(), 'code-reasoning-prompt-values.json'); const candidates = [defaultPath]; if (!candidates.includes(fallbackPath)) { candidates.push(fallbackPath); } this.persistenceEnabled = false; this.valuesFilePath = undefined; const initializationErrors: Error[] = []; for (const candidate of candidates) { try { this.storedValues = this.loadStoredValues(candidate); this.valuesFilePath = candidate; this.persistenceEnabled = true; console.error(`Prompt values will be stored at: ${candidate}`); return; } catch (err) { const error = err as Error; initializationErrors.push(error); console.error(`Failed to initialize prompt values at ${candidate}:`, error); } } throw new AggregateError( initializationErrors, `Unable to initialize prompt value persistence. Tried: ${candidates.join(', ')}` ); } private loadStoredValues(filePath: string): StoredPromptValues { const defaults: StoredPromptValues = { global: {}, prompts: {} }; fs.mkdirSync(path.dirname(filePath), { recursive: true }); if (!fs.existsSync(filePath)) { fs.writeFileSync(filePath, JSON.stringify(defaults, null, 2)); return { global: {}, prompts: {} }; } const fileContent = fs.readFileSync(filePath, 'utf8'); const parsed = JSON.parse(fileContent) as Partial<StoredPromptValues>; const parsedGlobal = parsed.global && typeof parsed.global === 'object' ? parsed.global : {}; const parsedPrompts = parsed.prompts && typeof parsed.prompts === 'object' ? parsed.prompts : {}; const promptsCopy: Record<string, Record<string, string>> = {}; Object.entries(parsedPrompts).forEach(([key, value]) => { if (value && typeof value === 'object') { promptsCopy[key] = { ...(value as Record<string, string>) }; } }); return { global: { ...(parsedGlobal as Record<string, string>) }, prompts: promptsCopy, }; } private saveStoredValues(): void { if (!this.persistenceEnabled || !this.valuesFilePath) { throw new Error('Prompt value persistence is not configured.'); } try { fs.writeFileSync(this.valuesFilePath, JSON.stringify(this.storedValues, null, 2)); } catch (err) { const error = err as Error; throw new Error(`Error saving prompt values to ${this.valuesFilePath}: ${error.message}`, { cause: error, }); } } /** * Registers a new prompt and its template function. * * @param prompt The prompt definition * @param template The template function that applies arguments to generate a result */ registerPrompt(prompt: Prompt, template: (args: Record<string, string>) => PromptResult): void { this.prompts[prompt.name] = prompt; this.templates[prompt.name] = template; console.error(`Registered prompt: ${prompt.name}`); } /** * Gets all available prompts. * * Note: Previously stored values for prompt arguments are provided through * the CompleteRequestSchema MCP protocol, not through the prompt objects. * * @returns An array of all registered prompts */ getAllPrompts(): Prompt[] { // Simply return the prompts without adding defaultValues return Object.values(this.prompts); } /** * Gets a specific prompt by name. * * @param name The name of the prompt to retrieve * @returns The prompt or undefined if not found */ getPrompt(name: string): Prompt | undefined { return this.prompts[name]; } /** * Gets stored values for a specific prompt. * This method is used by the CompleteRequestSchema handler to provide * auto-completion of prompt arguments. * * @param name The name of the prompt * @returns The stored values for the prompt */ getStoredValues(name: string): Record<string, string> { const result: Record<string, string> = { ...this.storedValues.global }; const promptValues = this.storedValues.prompts[name]; if (promptValues) { Object.assign(result, promptValues); } return result; } /** * Merges provided arguments with stored values, with provided args taking precedence. * This is a helper method to simplify the applyPrompt method. * * @param promptName The name of the prompt * @param args The provided arguments * @returns The merged arguments */ private mergeWithStoredValues( prompt: Prompt, args: Record<string, string> ): Record<string, string> { // Get stored values const storedValues = this.getStoredValues(prompt.name); const validArgNames = new Set((prompt.arguments || []).map(arg => arg.name)); const filteredStoredValues: Record<string, string> = {}; Object.entries(storedValues).forEach(([key, value]) => { const isGlobalKey = Object.prototype.hasOwnProperty.call(this.storedValues.global, key); if (isGlobalKey && !validArgNames.has(key)) { return; } filteredStoredValues[key] = value; }); // Filter out empty args const filteredArgs: Record<string, string> = {}; Object.entries(args).forEach(([key, value]) => { if (value.trim() !== '') { filteredArgs[key] = value; } }); // Merge stored values with filtered args (filtered args take precedence) return { ...filteredStoredValues, ...filteredArgs }; } /** * Applies a prompt with the given arguments. * Merges provided arguments with previously stored values, * with provided arguments taking precedence. * * @param name The name of the prompt to apply * @param args The arguments to apply to the prompt template * @returns The result of applying the prompt * @throws Error if the prompt doesn't exist or arguments are invalid */ applyPrompt(name: string, args: Record<string, string> = {}): PromptResult { const prompt = this.getPrompt(name); if (!prompt) { throw new Error(`Prompt not found: ${name}`); } // Merge with stored values const mergedArgs = this.mergeWithStoredValues(prompt, args); // Validate arguments const validationErrors = this.validatePromptArguments(prompt, mergedArgs); if (validationErrors.length > 0) { throw new Error(`Validation errors:\n${validationErrors.join('\n')}`); } // Get the template function const templateFn = this.templates[name]; if (!templateFn) { throw new Error(`Template implementation not found for prompt: ${name}`); } // Update stored values with the new ones this.updateStoredValues(name, mergedArgs); // Apply the template with merged args return templateFn(mergedArgs); } private updateStoredValues(promptName: string, args: Record<string, string>): void { if (args.working_directory && args.working_directory.trim() !== '') { this.storedValues.global.working_directory = args.working_directory; } if (!this.storedValues.prompts[promptName]) { this.storedValues.prompts[promptName] = {}; } const promptValues = this.storedValues.prompts[promptName]; const globalKeys = new Set(Object.keys(this.storedValues.global)); Object.entries(args).forEach(([key, value]) => { if (!value || value.trim() === '') { return; } if (globalKeys.has(key)) { return; } promptValues[key] = value; }); this.saveStoredValues(); } /** * Validates prompt arguments against the prompt definition. * * @param prompt The prompt to validate against * @param args The arguments to validate * @returns Array of validation error messages, empty if valid */ private validatePromptArguments(prompt: Prompt, args: Record<string, string>): string[] { const errors: string[] = []; // Check for required arguments (prompt.arguments || []).forEach( (arg: { name: string; description: string; required: boolean }) => { if (arg.required && (!args[arg.name] || args[arg.name].trim() === '')) { errors.push(`Missing required argument: ${arg.name} (${arg.description})`); } } ); // Check for unknown arguments const validArgNames = new Set((prompt.arguments || []).map(arg => arg.name)); Object.keys(args).forEach(argName => { if (!validArgNames.has(argName)) { errors.push(`Unknown argument: ${argName}`); } }); return errors; } }

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/mettamatt/code-reasoning'

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