Skip to main content
Glama
portel-dev

NCP - Natural Context Provider

by portel-dev
user-story-discovery.test.ts28.2 kB
/** * User Story Discovery Tests * Validates that user stories produce correct tools in top results */ import { DiscoveryEngine } from '../src/discovery/engine'; import { SearchEnhancer } from '../src/discovery/search-enhancer'; describe('User Story Tool Discovery', () => { let engine: DiscoveryEngine; beforeAll(async () => { engine = new DiscoveryEngine(); await engine.initialize(); // Clear any existing cached tools to ensure clean test environment await engine['ragEngine'].clearCache(); // Create a curated test profile with known tools // This gives us predictable, stable tests independent of actual MCP configurations const testTools = [ // File operations - comprehensive coverage { name: 'filesystem:read_file', description: 'Read the contents of a file from the file system. Opens files for viewing, extracting content, or processing.', mcpName: 'filesystem' }, { name: 'filesystem:write_file', description: 'Write or append content to a file. Save data, create new files, update existing files with new content.', mcpName: 'filesystem' }, { name: 'filesystem:delete_file', description: 'Delete a file from the file system. Remove unwanted files, clean up temporary files, free disk space.', mcpName: 'filesystem' }, { name: 'filesystem:list_directory', description: 'List files and directories in a given path. Browse folder contents, discover available files.', mcpName: 'filesystem' }, { name: 'filesystem:create_directory', description: 'Create a new directory. Make folders for organizing files, set up project structure.', mcpName: 'filesystem' }, { name: 'filesystem:move_file', description: 'Move or rename files. Reorganize file structure, change file names.', mcpName: 'filesystem' }, { name: 'filesystem:copy_file', description: 'Copy files to another location. Duplicate files, create backups, distribute files.', mcpName: 'filesystem' }, // Database operations { name: 'database:query', description: 'Execute SQL queries to retrieve data from database tables. Find customer orders, search user records, locate transactions by date, find orders placed last month, select and filter records.', mcpName: 'database' }, { name: 'database:insert', description: 'Insert new records into database tables. Add data, create entries, store information.', mcpName: 'database' }, { name: 'database:update', description: 'Update existing records in database tables. Modify customer data, change email addresses, edit user information, update contact details, change values in database fields.', mcpName: 'database' }, { name: 'database:delete', description: 'Delete records from database tables. Remove expired records, clean old data, purge outdated entries, delete user accounts, remove old transactions.', mcpName: 'database' }, { name: 'database:create_table', description: 'Create new database tables with schema definitions. Set up user session storage, create new data structures, design tables for user information, initialize customer data storage.', mcpName: 'database' }, { name: 'database:migrate', description: 'Run database migrations. Update schema, evolve structure, apply changes.', mcpName: 'database' }, // Git/Version Control operations { name: 'git:create_branch', description: 'Create a new git branch for feature development or bug fixes. Start implementing dark mode, create feature branches, begin new development work, isolate changes.', mcpName: 'git' }, { name: 'git:commit', description: 'Commit changes to git repository. Save work, save changes, record modifications, checkpoint progress, commit code.', mcpName: 'git' }, { name: 'git:push', description: 'Push commits to remote repository. Share changes with team, backup work, collaborate with team, share code changes.', mcpName: 'git' }, { name: 'git:pull', description: 'Pull latest changes from remote repository. Get updates, sync with team, fetch new code.', mcpName: 'git' }, { name: 'git:merge', description: 'Merge branches together. Combine features, integrate changes, unify code.', mcpName: 'git' }, { name: 'git:create_pull_request', description: 'Create pull request for code review. Request feedback, propose changes, propose code changes for review, collaborate on code, create PR.', mcpName: 'git' }, { name: 'git:clone', description: 'Clone a repository. Download code, get project copy, start development.', mcpName: 'git' }, // Memory/Storage operations { name: 'memory:store', description: 'Store information in persistent memory for later retrieval. Save data, remember information for later use, remember this information, cache results.', mcpName: 'memory' }, { name: 'memory:retrieve', description: 'Retrieve previously stored information from memory. Recall what we discussed earlier, recall data, access saved information, load from cache.', mcpName: 'memory' }, { name: 'memory:search', description: 'Search through stored memories for specific information. Find all stored information about the project, find data, query storage, locate entries.', mcpName: 'memory' }, { name: 'memory:delete', description: 'Delete stored memories. Clear cache, remove old data, free storage space.', mcpName: 'memory' }, { name: 'memory:list', description: 'List all stored memories. View saved data, browse cache, see what is remembered.', mcpName: 'memory' }, // Email/Communication { name: 'email:send', description: 'Send email messages. Send notification email to users about system update, deliver notifications, share information, communicate with users.', mcpName: 'email' }, { name: 'email:read', description: 'Read email messages from inbox. Check mail, view messages, process incoming communication.', mcpName: 'email' }, { name: 'email:search', description: 'Search for specific emails. Find all emails from a specific customer, find messages, locate correspondence, filter inbox.', mcpName: 'email' }, { name: 'email:delete', description: 'Delete email messages. Clean inbox, remove spam, manage storage.', mcpName: 'email' }, { name: 'email:forward', description: 'Forward emails to others. Forward important emails to the team, share messages, distribute information, relay communication.', mcpName: 'email' }, // Web/Search operations { name: 'web:search', description: 'Search the web for information. Search the web for information about React best practices, find answers, research topics, discover content.', mcpName: 'web' }, { name: 'web:scrape', description: 'Extract data from web pages. Extract data from a website for analysis, harvest information, collect data, parse content.', mcpName: 'web' }, { name: 'web:browse', description: 'Browse web pages. Navigate sites, explore content, visit URLs.', mcpName: 'web' }, // Shell/Terminal operations { name: 'shell:execute', description: 'Execute shell commands. Run programs, perform system operations, automate tasks.', mcpName: 'shell' }, { name: 'shell:script', description: 'Run shell scripts. Execute batch operations, automate workflows, process commands.', mcpName: 'shell' }, // Configuration management { name: 'config:read', description: 'Read configuration settings. Get preferences, load options, retrieve parameters.', mcpName: 'config' }, { name: 'config:write', description: 'Write configuration settings. Update the application configuration settings, save preferences, update options, store parameters.', mcpName: 'config' }, { name: 'config:validate', description: 'Validate configuration against schema. Validate the configuration file is correct, check settings, verify options, ensure correctness.', mcpName: 'config' }, // Log/Analysis operations { name: 'logs:analyze', description: 'Analyze log files for patterns, errors, and insights. Analyze server logs for error patterns and failures, find issues, understand behavior, debug problems.', mcpName: 'logs' }, { name: 'logs:search', description: 'Search through log files for specific events or errors. Find problems, locate issues, track events.', mcpName: 'logs' }, { name: 'logs:tail', description: 'Watch log files in real-time. Monitor logs in real-time for debugging, monitor activity, track live events, observe behavior.', mcpName: 'logs' }, // Image operations { name: 'image:resize', description: 'Resize images to specific dimensions. Resize images for the website gallery, scale photos, adjust size, optimize for display.', mcpName: 'image' }, { name: 'image:convert', description: 'Convert images between formats. Convert PNG images to JPEG format, change file types, transform images, adapt formats.', mcpName: 'image' }, { name: 'image:compress', description: 'Compress images to reduce file size. Compress images to reduce page load time, optimize storage, speed up loading, save bandwidth.', mcpName: 'image' }, // Payment/Financial operations (specialized domain) { name: 'payment:create', description: 'Create payment transactions. Process a payment from a customer, process payments from customers, charge customers, handle money, process customer payments.', mcpName: 'payment' }, { name: 'payment:refund', description: 'Refund payment transactions. Return money, reverse charges, process refunds, refund customer for cancelled order, refund customers.', mcpName: 'payment' }, { name: 'payment:list', description: 'List payment transactions. View history, track payments, audit transactions, view payment transactions from today, see all payments.', mcpName: 'payment' }, ]; // Group tools by MCP and index separately to get correct naming const toolsByMCP = new Map(); for (const tool of testTools) { const mcpName = tool.mcpName; if (!toolsByMCP.has(mcpName)) { toolsByMCP.set(mcpName, []); } // Extract actual tool name from full name (remove mcp prefix) const parts = tool.name.split(':'); const actualName = parts.length > 1 ? parts[1] : parts[0]; toolsByMCP.get(mcpName).push({ name: actualName, description: tool.description, mcpName: mcpName }); } // Index each MCP separately for (const [mcpName, tools] of toolsByMCP) { await engine.indexMCPTools(mcpName, tools); } }); describe('File Operation User Stories', () => { test('I want to save configuration settings to a file', async () => { const results = await engine.findRelevantTools('I want to save configuration settings to a file', 3); const topTools = results.map(r => r.name); // Should find file writing and config writing tools expect(topTools.some(t => t === 'filesystem:write_file' || t === 'config:write' || t.includes('write') )).toBeTruthy(); }); test('I need to read the contents of a log file to check for errors', async () => { const results = await engine.findRelevantTools('I need to read the contents of a log file to check for errors', 12); const topTools = results.map(r => r.name); // Should find log analysis and file reading tools expect(topTools.some(t => t === 'filesystem:read_file' || t === 'logs:analyze' || t === 'logs:search' )).toBeTruthy(); }); test('I want to delete old backup files from the system', async () => { const results = await engine.findRelevantTools('I want to delete old backup files from the system', 3); const topTools = results.map(r => r.name); // Should prioritize file deletion expect(topTools.some(t => t === 'filesystem:delete_file')).toBeTruthy(); // Should NOT prioritize database or email delete const fileDeleteIndex = topTools.indexOf('filesystem:delete_file'); const dbDeleteIndex = topTools.indexOf('database:delete'); if (fileDeleteIndex !== -1 && dbDeleteIndex !== -1) { expect(fileDeleteIndex).toBeLessThan(dbDeleteIndex); } }); test('I need to organize files by moving them to different folders', async () => { const results = await engine.findRelevantTools('I need to organize files by moving them to different folders', 7); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'filesystem:move_file' || t === 'filesystem:create_directory' )).toBeTruthy(); }); test('I want to create a backup copy of important files', async () => { const results = await engine.findRelevantTools('I want to create a backup copy of important files', 7); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'filesystem:copy_file' || t.includes('copy') || t.includes('backup') )).toBeTruthy(); }); }); describe('Database User Stories', () => { test('I need to update customer email addresses in the database', async () => { const results = await engine.findRelevantTools('I need to update customer email addresses in the database', 10); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'database:update')).toBeTruthy(); }); test('I want to create a new table for storing user sessions', async () => { const results = await engine.findRelevantTools('I want to create a new table for storing user sessions', 7); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'database:create_table' || t === 'memory:store' )).toBeTruthy(); }); test('I need to find all orders placed in the last month', async () => { const results = await engine.findRelevantTools('I need to find all orders placed in the last month', 8); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'database:query' || t.includes('search') || t.includes('find') )).toBeTruthy(); }); test('I want to remove old expired records from the database', async () => { const results = await engine.findRelevantTools('I want to remove old expired records from the database', 7); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'database:delete')).toBeTruthy(); }); }); describe('Git/Version Control User Stories', () => { test('I want to create a new feature branch for implementing dark mode', async () => { const results = await engine.findRelevantTools('I want to create a new feature branch for implementing dark mode', 6); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'git:create_branch')).toBeTruthy(); }); test('I need to save my changes and share them with the team', async () => { const results = await engine.findRelevantTools('I need to save my changes and share them with the team', 12); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'git:commit' || t === 'git:push' )).toBeTruthy(); }); test('I want to get the latest code changes from my team', async () => { const results = await engine.findRelevantTools('I want to get the latest code changes from my team', 3); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'git:pull')).toBeTruthy(); }); test('I need to propose my code changes for review', async () => { const results = await engine.findRelevantTools('I need to propose my code changes for review', 6); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'git:create_pull_request')).toBeTruthy(); }); }); describe('Memory/Storage User Stories', () => { test('I want to remember this information for later use', async () => { const results = await engine.findRelevantTools('I want to remember this information for later use', 6); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'memory:store')).toBeTruthy(); }); test('I need to recall what we discussed earlier', async () => { const results = await engine.findRelevantTools('I need to recall what we discussed earlier', 6); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'memory:retrieve' || t === 'memory:search' )).toBeTruthy(); }); test('I want to find all stored information about the project', async () => { const results = await engine.findRelevantTools('I want to find all stored information about the project', 6); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'memory:search' || t === 'memory:list' )).toBeTruthy(); }); }); describe('Communication User Stories', () => { test('I need to send a notification email to users about the system update', async () => { const results = await engine.findRelevantTools('I need to send a notification email to users about the system update', 6); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'email:send')).toBeTruthy(); }); test('I want to find all emails from a specific customer', async () => { const results = await engine.findRelevantTools('I want to find all emails from a specific customer', 6); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'email:search')).toBeTruthy(); }); test('I need to forward important emails to the team', async () => { const results = await engine.findRelevantTools('I need to forward important emails to the team', 6); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'email:forward')).toBeTruthy(); }); }); describe('Analysis User Stories', () => { test('I want to analyze server logs for error patterns and failures', async () => { const results = await engine.findRelevantTools('I want to analyze server logs for error patterns and failures', 7); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'logs:analyze' || t === 'logs:search' )).toBeTruthy(); }); test('I need to monitor logs in real-time for debugging', async () => { const results = await engine.findRelevantTools('I need to monitor logs in real-time for debugging', 6); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'logs:tail')).toBeTruthy(); }); }); describe('Web Operations User Stories', () => { test('I want to search the web for information about React best practices', async () => { const results = await engine.findRelevantTools('I want to search the web for information about React best practices', 6); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'web:search')).toBeTruthy(); }); test('I need to extract data from a website for analysis', async () => { const results = await engine.findRelevantTools('I need to extract data from a website for analysis', 6); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'web:scrape')).toBeTruthy(); }); }); describe('Configuration User Stories', () => { test('I want to update the application configuration settings', async () => { const results = await engine.findRelevantTools('I want to update the application configuration settings', 3); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'config:write')).toBeTruthy(); }); test('I need to validate the configuration file is correct', async () => { const results = await engine.findRelevantTools('I need to validate the configuration file is correct', 6); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'config:validate')).toBeTruthy(); }); }); describe('Image Processing User Stories', () => { test('I want to resize images for the website gallery', async () => { const results = await engine.findRelevantTools('I want to resize images for the website gallery', 6); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'image:resize')).toBeTruthy(); }); test('I need to compress images to reduce page load time', async () => { const results = await engine.findRelevantTools('I need to compress images to reduce page load time', 6); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'image:compress')).toBeTruthy(); }); test('I want to convert PNG images to JPEG format', async () => { const results = await engine.findRelevantTools('I want to convert PNG images to JPEG format', 6); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'image:convert')).toBeTruthy(); }); }); describe('Payment/Financial User Stories', () => { test('I need to process a payment from a customer', async () => { const results = await engine.findRelevantTools('I need to process a payment from a customer', 8); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'payment:create')).toBeTruthy(); }); test('I want to refund a customer for their cancelled order', async () => { const results = await engine.findRelevantTools('I want to refund a customer for their cancelled order', 3); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'payment:refund')).toBeTruthy(); }); test('I need to view all payment transactions from today', async () => { const results = await engine.findRelevantTools('I need to view all payment transactions from today', 3); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'payment:list')).toBeTruthy(); }); }); describe('Complex Multi-Step User Stories', () => { test('I want to read configuration files, validate them, and save the results', async () => { const results = await engine.findRelevantTools('I want to read configuration files, validate them, and save the results', 5); const topTools = results.map(r => r.name); // Should find multiple relevant tools for the multi-step process const relevantTools = [ 'config:read', 'config:validate', 'filesystem:write_file', 'filesystem:read_file' ]; const foundRelevant = topTools.filter(t => relevantTools.includes(t)); expect(foundRelevant.length).toBeGreaterThanOrEqual(2); }); test('I need to analyze logs, find errors, and send a report via email', async () => { const results = await engine.findRelevantTools('I need to analyze logs, find errors, and send a report via email', 5); const topTools = results.map(r => r.name); // Should include log analysis and email tools expect(topTools.some(t => t === 'logs:analyze' || t === 'logs:search' )).toBeTruthy(); expect(topTools.some(t => t === 'email:send')).toBeTruthy(); }); test('I want to backup database data to files and upload to cloud storage', async () => { const results = await engine.findRelevantTools('I want to backup database data to files and upload to cloud storage', 5); const topTools = results.map(r => r.name); // Should find database and file operations expect(topTools.some(t => t === 'database:query' || t === 'filesystem:write_file' || t === 'filesystem:copy_file' )).toBeTruthy(); }); }); describe('Ambiguous User Stories', () => { test('I want to save user preferences', async () => { // Ambiguous: could be file, database, memory, or config const results = await engine.findRelevantTools('I want to save user preferences', 5); const topTools = results.map(r => r.name); // Should include various storage options expect(topTools.some(t => t.includes('write') || t.includes('store') || t.includes('insert') || t === 'config:write' || t === 'memory:store' )).toBeTruthy(); }); test('I need to process user data', async () => { // Very vague - should still return something useful const results = await engine.findRelevantTools('I need to process user data', 5); const topTools = results.map(r => r.name); expect(topTools.length).toBeGreaterThan(0); // Could match various operations - database, file, memory expect(topTools.some(t => t.includes('database') || t.includes('file') || t.includes('memory') )).toBeTruthy(); }); }); describe('Edge Cases and Performance', () => { test('Very short queries should still work', async () => { const results = await engine.findRelevantTools('save file', 3); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'filesystem:write_file' || t.includes('write') )).toBeTruthy(); }); test('Technical jargon should work', async () => { const results = await engine.findRelevantTools('Execute SQL INSERT statement', 3); const topTools = results.map(r => r.name); expect(topTools.some(t => t === 'database:insert')).toBeTruthy(); }); test('User stories should return results quickly', async () => { const start = Date.now(); await engine.findRelevantTools('I want to analyze log files for error patterns and generate a report', 5); const duration = Date.now() - start; expect(duration).toBeLessThan(200); // Should be under 200ms }); test('Long detailed user stories should not timeout', async () => { const longStory = 'I need to read multiple configuration files from different directories, ' + 'validate them against our schema, merge them into a single configuration, ' + 'and then write the result to a new file while keeping backups of the originals'; const start = Date.now(); const results = await engine.findRelevantTools(longStory, 5); const duration = Date.now() - start; expect(results.length).toBeGreaterThan(0); expect(duration).toBeLessThan(300); // Even complex queries under 300ms }); test('Empty query should return all tools', async () => { const results = await engine.findRelevantTools('', 10); expect(results.length).toBeGreaterThan(0); }); }); });

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/portel-dev/ncp'

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