This is page 9 of 12. Use http://codebase.md/portel-dev/ncp?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .dockerignore ├── .dxtignore ├── .github │ ├── FEATURE_STORY_TEMPLATE.md │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.yml │ │ ├── config.yml │ │ ├── feature_request.yml │ │ └── mcp_server_request.yml │ ├── pull_request_template.md │ └── workflows │ ├── ci.yml │ ├── publish-mcp-registry.yml │ └── release.yml ├── .gitignore ├── .mcpbignore ├── .npmignore ├── .release-it.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── COMPLETE-IMPLEMENTATION-SUMMARY.md ├── CONTRIBUTING.md ├── CRITICAL-ISSUES-FOUND.md ├── docs │ ├── clients │ │ ├── claude-desktop.md │ │ ├── cline.md │ │ ├── continue.md │ │ ├── cursor.md │ │ ├── perplexity.md │ │ └── README.md │ ├── download-stats.md │ ├── guides │ │ ├── clipboard-security-pattern.md │ │ ├── how-it-works.md │ │ ├── mcp-prompts-for-user-interaction.md │ │ ├── mcpb-installation.md │ │ ├── ncp-registry-command.md │ │ ├── pre-release-checklist.md │ │ ├── telemetry-design.md │ │ └── testing.md │ ├── images │ │ ├── ncp-add.png │ │ ├── ncp-find.png │ │ ├── ncp-help.png │ │ ├── ncp-import.png │ │ ├── ncp-list.png │ │ └── ncp-transformation-flow.png │ ├── mcp-registry-setup.md │ ├── pr-schema-additions.ts │ └── stories │ ├── 01-dream-and-discover.md │ ├── 02-secrets-in-plain-sight.md │ ├── 03-sync-and-forget.md │ ├── 04-double-click-install.md │ ├── 05-runtime-detective.md │ └── 06-official-registry.md ├── DYNAMIC-RUNTIME-SUMMARY.md ├── EXTENSION-CONFIG-DISCOVERY.md ├── INSTALL-EXTENSION.md ├── INTERNAL-MCP-ARCHITECTURE.md ├── jest.config.js ├── LICENSE ├── MANAGEMENT-TOOLS-COMPLETE.md ├── manifest.json ├── manifest.json.backup ├── MCP-CONFIG-SCHEMA-IMPLEMENTATION-EXAMPLE.ts ├── MCP-CONFIG-SCHEMA-SIMPLE-EXAMPLE.json ├── MCP-CONFIGURATION-SCHEMA-FORMAT.json ├── MCPB-ARCHITECTURE-DECISION.md ├── NCP-EXTENSION-COMPLETE.md ├── package-lock.json ├── package.json ├── parity-between-cli-and-mcp.txt ├── PROMPTS-IMPLEMENTATION.md ├── README-COMPARISON.md ├── README.md ├── README.new.md ├── REGISTRY-INTEGRATION-COMPLETE.md ├── RELEASE-PROCESS-IMPROVEMENTS.md ├── RELEASE-SUMMARY.md ├── RELEASE.md ├── RUNTIME-DETECTION-COMPLETE.md ├── scripts │ ├── cleanup │ │ └── scan-repository.js │ └── sync-server-version.cjs ├── SECURITY.md ├── server.json ├── src │ ├── analytics │ │ ├── analytics-formatter.ts │ │ ├── log-parser.ts │ │ └── visual-formatter.ts │ ├── auth │ │ ├── oauth-device-flow.ts │ │ └── token-store.ts │ ├── cache │ │ ├── cache-patcher.ts │ │ ├── csv-cache.ts │ │ └── schema-cache.ts │ ├── cli │ │ └── index.ts │ ├── discovery │ │ ├── engine.ts │ │ ├── mcp-domain-analyzer.ts │ │ ├── rag-engine.ts │ │ ├── search-enhancer.ts │ │ └── semantic-enhancement-engine.ts │ ├── extension │ │ └── extension-init.ts │ ├── index-mcp.ts │ ├── index.ts │ ├── internal-mcps │ │ ├── internal-mcp-manager.ts │ │ ├── ncp-management.ts │ │ └── types.ts │ ├── orchestrator │ │ └── ncp-orchestrator.ts │ ├── profiles │ │ └── profile-manager.ts │ ├── server │ │ ├── mcp-prompts.ts │ │ └── mcp-server.ts │ ├── services │ │ ├── config-prompter.ts │ │ ├── config-schema-reader.ts │ │ ├── error-handler.ts │ │ ├── output-formatter.ts │ │ ├── registry-client.ts │ │ ├── tool-context-resolver.ts │ │ ├── tool-finder.ts │ │ ├── tool-schema-parser.ts │ │ └── usage-tips-generator.ts │ ├── testing │ │ ├── create-real-mcp-definitions.ts │ │ ├── dummy-mcp-server.ts │ │ ├── mcp-definitions.json │ │ ├── real-mcp-analyzer.ts │ │ ├── real-mcp-definitions.json │ │ ├── real-mcps.csv │ │ ├── setup-dummy-mcps.ts │ │ ├── setup-tiered-profiles.ts │ │ ├── test-profile.json │ │ ├── test-semantic-enhancement.ts │ │ └── verify-profile-scaling.ts │ ├── transports │ │ └── filtered-stdio-transport.ts │ └── utils │ ├── claude-desktop-importer.ts │ ├── client-importer.ts │ ├── client-registry.ts │ ├── config-manager.ts │ ├── health-monitor.ts │ ├── highlighting.ts │ ├── logger.ts │ ├── markdown-renderer.ts │ ├── mcp-error-parser.ts │ ├── mcp-wrapper.ts │ ├── ncp-paths.ts │ ├── parameter-prompter.ts │ ├── paths.ts │ ├── progress-spinner.ts │ ├── response-formatter.ts │ ├── runtime-detector.ts │ ├── schema-examples.ts │ ├── security.ts │ ├── text-utils.ts │ ├── update-checker.ts │ ├── updater.ts │ └── version.ts ├── STORY-DRIVEN-DOCUMENTATION.md ├── STORY-FIRST-WORKFLOW.md ├── test │ ├── __mocks__ │ │ ├── chalk.js │ │ ├── transformers.js │ │ ├── updater.js │ │ └── version.ts │ ├── cache-loading-focused.test.ts │ ├── cache-optimization.test.ts │ ├── cli-help-validation.sh │ ├── coverage-boost.test.ts │ ├── curated-ecosystem-validation.test.ts │ ├── discovery-engine.test.ts │ ├── discovery-fallback-focused.test.ts │ ├── ecosystem-discovery-focused.test.ts │ ├── ecosystem-discovery-validation-simple.test.ts │ ├── final-80-percent-push.test.ts │ ├── final-coverage-push.test.ts │ ├── health-integration.test.ts │ ├── health-monitor.test.ts │ ├── helpers │ │ └── mock-server-manager.ts │ ├── integration │ │ └── mcp-client-simulation.test.cjs │ ├── logger.test.ts │ ├── mcp-ecosystem-discovery.test.ts │ ├── mcp-error-parser.test.ts │ ├── mcp-immediate-response-check.js │ ├── mcp-server-protocol.test.ts │ ├── mcp-timeout-scenarios.test.ts │ ├── mcp-wrapper.test.ts │ ├── mock-mcps │ │ ├── aws-server.js │ │ ├── base-mock-server.mjs │ │ ├── brave-search-server.js │ │ ├── docker-server.js │ │ ├── filesystem-server.js │ │ ├── git-server.mjs │ │ ├── github-server.js │ │ ├── neo4j-server.js │ │ ├── notion-server.js │ │ ├── playwright-server.js │ │ ├── postgres-server.js │ │ ├── shell-server.js │ │ ├── slack-server.js │ │ └── stripe-server.js │ ├── mock-smithery-mcp │ │ ├── index.js │ │ ├── package.json │ │ └── smithery.yaml │ ├── ncp-orchestrator.test.ts │ ├── orchestrator-health-integration.test.ts │ ├── orchestrator-simple-branches.test.ts │ ├── performance-benchmark.test.ts │ ├── quick-coverage.test.ts │ ├── rag-engine.test.ts │ ├── regression-snapshot.test.ts │ ├── search-enhancer.test.ts │ ├── session-id-passthrough.test.ts │ ├── setup.ts │ ├── tool-context-resolver.test.ts │ ├── tool-schema-parser.test.ts │ ├── user-story-discovery.test.ts │ └── version-util.test.ts └── tsconfig.json ``` # Files -------------------------------------------------------------------------------- /src/internal-mcps/ncp-management.ts: -------------------------------------------------------------------------------- ```typescript 1 | /** 2 | * NCP Management Internal MCP 3 | * 4 | * Provides tools for managing NCP configuration: 5 | * - add: Add single MCP 6 | * - remove: Remove MCP 7 | * - list: List configured MCPs 8 | * - import: Bulk import (clipboard/file/discovery) 9 | * - export: Export configuration 10 | */ 11 | 12 | import { InternalMCP, InternalTool, InternalToolResult } from './types.js'; 13 | import ProfileManager from '../profiles/profile-manager.js'; 14 | import { tryReadClipboardConfig, mergeWithClipboardConfig } from '../server/mcp-prompts.js'; 15 | import { logger } from '../utils/logger.js'; 16 | import { RegistryClient, RegistryMCPCandidate } from '../services/registry-client.js'; 17 | 18 | export class NCPManagementMCP implements InternalMCP { 19 | name = 'ncp'; 20 | description = 'NCP configuration management tools'; 21 | 22 | private profileManager: ProfileManager | null = null; 23 | 24 | tools: InternalTool[] = [ 25 | { 26 | name: 'add', 27 | description: 'Add a new MCP server to NCP configuration. IMPORTANT: AI must first call confirm_add_mcp prompt for user approval. User can securely provide API keys via clipboard before approving.', 28 | inputSchema: { 29 | type: 'object', 30 | properties: { 31 | mcp_name: { 32 | type: 'string', 33 | description: 'Name for the MCP server (e.g., "github", "filesystem")' 34 | }, 35 | command: { 36 | type: 'string', 37 | description: 'Command to execute (e.g., "npx", "node", "python")' 38 | }, 39 | args: { 40 | type: 'array', 41 | items: { type: 'string' }, 42 | description: 'Command arguments (e.g., ["-y", "@modelcontextprotocol/server-github"])' 43 | }, 44 | profile: { 45 | type: 'string', 46 | description: 'Target profile name (default: "all")', 47 | default: 'all' 48 | } 49 | }, 50 | required: ['mcp_name', 'command'] 51 | } 52 | }, 53 | { 54 | name: 'remove', 55 | description: 'Remove an MCP server from NCP configuration. IMPORTANT: AI must first call confirm_remove_mcp prompt for user approval.', 56 | inputSchema: { 57 | type: 'object', 58 | properties: { 59 | mcp_name: { 60 | type: 'string', 61 | description: 'Name of the MCP server to remove' 62 | }, 63 | profile: { 64 | type: 'string', 65 | description: 'Profile to remove from (default: "all")', 66 | default: 'all' 67 | } 68 | }, 69 | required: ['mcp_name'] 70 | } 71 | }, 72 | { 73 | name: 'list', 74 | description: 'List all configured MCP servers in a profile', 75 | inputSchema: { 76 | type: 'object', 77 | properties: { 78 | profile: { 79 | type: 'string', 80 | description: 'Profile name to list (default: "all")', 81 | default: 'all' 82 | } 83 | } 84 | } 85 | }, 86 | { 87 | name: 'import', 88 | description: 'Import MCPs from clipboard, file, or discovery. For discovery: first call shows numbered list, second call with selection imports chosen MCPs.', 89 | inputSchema: { 90 | type: 'object', 91 | properties: { 92 | from: { 93 | type: 'string', 94 | enum: ['clipboard', 'file', 'discovery'], 95 | default: 'clipboard', 96 | description: 'Import source: clipboard (default), file path, or discovery (search registry)' 97 | }, 98 | source: { 99 | type: 'string', 100 | description: 'File path (when from=file) or search query (when from=discovery). Not needed for clipboard.' 101 | }, 102 | selection: { 103 | type: 'string', 104 | description: 'Selection from discovery results (only for from=discovery). Format: "1,3,5" or "1-5" or "*" for all' 105 | } 106 | } 107 | } 108 | }, 109 | { 110 | name: 'export', 111 | description: 'Export current NCP configuration', 112 | inputSchema: { 113 | type: 'object', 114 | properties: { 115 | to: { 116 | type: 'string', 117 | enum: ['clipboard', 'file'], 118 | default: 'clipboard', 119 | description: 'Export destination: clipboard (default) or file' 120 | }, 121 | destination: { 122 | type: 'string', 123 | description: 'File path (only for to=file)' 124 | }, 125 | profile: { 126 | type: 'string', 127 | description: 'Profile to export (default: "all")', 128 | default: 'all' 129 | } 130 | } 131 | } 132 | } 133 | ]; 134 | 135 | /** 136 | * Set the ProfileManager instance 137 | * Called by orchestrator after initialization 138 | */ 139 | setProfileManager(profileManager: ProfileManager): void { 140 | this.profileManager = profileManager; 141 | } 142 | 143 | async executeTool(toolName: string, parameters: any): Promise<InternalToolResult> { 144 | if (!this.profileManager) { 145 | return { 146 | success: false, 147 | error: 'ProfileManager not initialized. Please try again.' 148 | }; 149 | } 150 | 151 | try { 152 | switch (toolName) { 153 | case 'add': 154 | return await this.handleAdd(parameters); 155 | 156 | case 'remove': 157 | return await this.handleRemove(parameters); 158 | 159 | case 'list': 160 | return await this.handleList(parameters); 161 | 162 | case 'import': 163 | return await this.handleImport(parameters); 164 | 165 | case 'export': 166 | return await this.handleExport(parameters); 167 | 168 | default: 169 | return { 170 | success: false, 171 | error: `Unknown tool: ${toolName}. Available tools: add, remove, list, import, export` 172 | }; 173 | } 174 | } catch (error: any) { 175 | logger.error(`Internal MCP tool execution failed: ${error.message}`); 176 | return { 177 | success: false, 178 | error: error.message || 'Tool execution failed' 179 | }; 180 | } 181 | } 182 | 183 | private async handleAdd(params: any): Promise<InternalToolResult> { 184 | if (!params?.mcp_name || !params?.command) { 185 | return { 186 | success: false, 187 | error: 'Missing required parameters: mcp_name and command are required' 188 | }; 189 | } 190 | 191 | const mcpName = params.mcp_name; 192 | const command = params.command; 193 | const commandArgs = params.args || []; 194 | const profile = params.profile || 'all'; 195 | 196 | // Try to read clipboard for additional config (env vars, args) 197 | // This is the clipboard security pattern - user was instructed to copy config before approving 198 | const clipboardConfig = await tryReadClipboardConfig(); 199 | 200 | // Build base config 201 | const baseConfig = { 202 | command, 203 | args: commandArgs, 204 | env: {} 205 | }; 206 | 207 | // Merge with clipboard config (clipboard takes precedence) 208 | const finalConfig = mergeWithClipboardConfig(baseConfig, clipboardConfig); 209 | 210 | // Add MCP to profile 211 | await this.profileManager!.addMCPToProfile(profile, mcpName, finalConfig); 212 | 213 | // Log success (without revealing secrets) 214 | const hasSecrets = clipboardConfig?.env ? ' with credentials' : ''; 215 | const successMessage = `✅ MCP server "${mcpName}" added to profile "${profile}"${hasSecrets}\n\n` + 216 | `Command: ${command} ${finalConfig.args?.join(' ') || ''}\n\n` + 217 | `The MCP server will be available after NCP is restarted.`; 218 | 219 | logger.info(`Added MCP "${mcpName}" to profile "${profile}"`); 220 | 221 | return { 222 | success: true, 223 | content: successMessage 224 | }; 225 | } 226 | 227 | private async handleRemove(params: any): Promise<InternalToolResult> { 228 | if (!params?.mcp_name) { 229 | return { 230 | success: false, 231 | error: 'Missing required parameter: mcp_name is required' 232 | }; 233 | } 234 | 235 | const mcpName = params.mcp_name; 236 | const profile = params.profile || 'all'; 237 | 238 | // Remove MCP from profile 239 | await this.profileManager!.removeMCPFromProfile(profile, mcpName); 240 | 241 | const successMessage = `✅ MCP server "${mcpName}" removed from profile "${profile}"\n\n` + 242 | `The change will take effect after NCP is restarted.`; 243 | 244 | logger.info(`Removed MCP "${mcpName}" from profile "${profile}"`); 245 | 246 | return { 247 | success: true, 248 | content: successMessage 249 | }; 250 | } 251 | 252 | private async handleList(params: any): Promise<InternalToolResult> { 253 | const profile = params?.profile || 'all'; 254 | 255 | const mcps = await this.profileManager!.getProfileMCPs(profile); 256 | 257 | if (!mcps || Object.keys(mcps).length === 0) { 258 | return { 259 | success: true, 260 | content: `No MCPs configured in profile "${profile}"` 261 | }; 262 | } 263 | 264 | const mcpList = Object.entries(mcps) 265 | .map(([name, config]) => { 266 | const argsStr = config.args?.join(' ') || ''; 267 | const envKeys = config.env ? Object.keys(config.env).join(', ') : ''; 268 | const envInfo = envKeys ? `\n Environment: ${envKeys}` : ''; 269 | return `• ${name}\n Command: ${config.command} ${argsStr}${envInfo}`; 270 | }) 271 | .join('\n\n'); 272 | 273 | const successMessage = `📋 Configured MCPs in profile "${profile}":\n\n${mcpList}`; 274 | 275 | return { 276 | success: true, 277 | content: successMessage 278 | }; 279 | } 280 | 281 | private async handleImport(params: any): Promise<InternalToolResult> { 282 | const from = params?.from || 'clipboard'; 283 | const source = params?.source; 284 | const selection = params?.selection; 285 | 286 | switch (from) { 287 | case 'clipboard': 288 | return await this.importFromClipboard(); 289 | 290 | case 'file': 291 | if (!source) { 292 | return { 293 | success: false, 294 | error: 'source parameter required when from=file' 295 | }; 296 | } 297 | return await this.importFromFile(source); 298 | 299 | case 'discovery': 300 | if (!source) { 301 | return { 302 | success: false, 303 | error: 'source parameter required when from=discovery (search query)' 304 | }; 305 | } 306 | return await this.importFromDiscovery(source, selection); 307 | 308 | default: 309 | return { 310 | success: false, 311 | error: `Invalid from parameter: ${from}. Use: clipboard, file, or discovery` 312 | }; 313 | } 314 | } 315 | 316 | private async importFromClipboard(): Promise<InternalToolResult> { 317 | try { 318 | const clipboardy = await import('clipboardy'); 319 | const clipboardContent = await clipboardy.default.read(); 320 | 321 | if (!clipboardContent || clipboardContent.trim().length === 0) { 322 | return { 323 | success: false, 324 | error: 'Clipboard is empty. Copy a valid MCP configuration JSON first.' 325 | }; 326 | } 327 | 328 | const config = JSON.parse(clipboardContent.trim()); 329 | 330 | // Validate and import 331 | if (!config.mcpServers || typeof config.mcpServers !== 'object') { 332 | return { 333 | success: false, 334 | error: 'Invalid config format. Expected: {"mcpServers": {...}}' 335 | }; 336 | } 337 | 338 | let imported = 0; 339 | for (const [name, mcpConfig] of Object.entries(config.mcpServers)) { 340 | if (typeof mcpConfig === 'object' && mcpConfig !== null && 'command' in mcpConfig) { 341 | await this.profileManager!.addMCPToProfile('all', name, mcpConfig as any); 342 | imported++; 343 | } 344 | } 345 | 346 | return { 347 | success: true, 348 | content: `✅ Imported ${imported} MCPs from clipboard` 349 | }; 350 | } catch (error: any) { 351 | return { 352 | success: false, 353 | error: `Failed to import from clipboard: ${error.message}` 354 | }; 355 | } 356 | } 357 | 358 | private async importFromFile(filePath: string): Promise<InternalToolResult> { 359 | try { 360 | const fs = await import('fs/promises'); 361 | const path = await import('path'); 362 | 363 | // Expand ~ to home directory 364 | const expandedPath = filePath.startsWith('~') 365 | ? path.join(process.env.HOME || process.env.USERPROFILE || '', filePath.slice(1)) 366 | : filePath; 367 | 368 | const content = await fs.readFile(expandedPath, 'utf-8'); 369 | const config = JSON.parse(content); 370 | 371 | // Validate and import 372 | if (!config.mcpServers || typeof config.mcpServers !== 'object') { 373 | return { 374 | success: false, 375 | error: 'Invalid config format. Expected: {"mcpServers": {...}}' 376 | }; 377 | } 378 | 379 | let imported = 0; 380 | for (const [name, mcpConfig] of Object.entries(config.mcpServers)) { 381 | if (typeof mcpConfig === 'object' && mcpConfig !== null && 'command' in mcpConfig) { 382 | await this.profileManager!.addMCPToProfile('all', name, mcpConfig as any); 383 | imported++; 384 | } 385 | } 386 | 387 | return { 388 | success: true, 389 | content: `✅ Imported ${imported} MCPs from ${filePath}` 390 | }; 391 | } catch (error: any) { 392 | return { 393 | success: false, 394 | error: `Failed to import from file: ${error.message}` 395 | }; 396 | } 397 | } 398 | 399 | private async importFromDiscovery(query: string, selection?: string): Promise<InternalToolResult> { 400 | try { 401 | const registryClient = new RegistryClient(); 402 | const candidates = await registryClient.searchForSelection(query); 403 | 404 | if (candidates.length === 0) { 405 | return { 406 | success: false, 407 | error: `No MCPs found for query: "${query}". Try a different search term.` 408 | }; 409 | } 410 | 411 | // If no selection, show numbered list 412 | if (!selection) { 413 | const listItems = candidates.map(c => { 414 | const statusBadge = c.status === 'active' ? '⭐' : '📦'; 415 | const envInfo = c.envVars?.length ? ` (${c.envVars.length} env vars required)` : ''; 416 | return `${c.number}. ${statusBadge} ${c.displayName}${envInfo}\n ${c.description}\n Version: ${c.version}`; 417 | }).join('\n\n'); 418 | 419 | const message = `📋 Found ${candidates.length} MCPs matching "${query}":\n\n${listItems}\n\n` + 420 | `⚙️ To import, call ncp:import again with selection:\n` + 421 | ` Example: { from: "discovery", source: "${query}", selection: "1,3,5" }\n\n` + 422 | ` - Select individual: "1,3,5"\n` + 423 | ` - Select range: "1-5"\n` + 424 | ` - Select all: "*"`; 425 | 426 | return { 427 | success: true, 428 | content: message 429 | }; 430 | } 431 | 432 | // Parse selection 433 | const selectedIndices = this.parseSelection(selection, candidates.length); 434 | if (selectedIndices.length === 0) { 435 | return { 436 | success: false, 437 | error: `Invalid selection: "${selection}". Use format like "1,3,5" or "1-5" or "*"` 438 | }; 439 | } 440 | 441 | const selectedCandidates = selectedIndices.map(i => candidates[i - 1]).filter(Boolean); 442 | 443 | if (selectedCandidates.length === 0) { 444 | return { 445 | success: false, 446 | error: `No valid MCPs selected. Check your selection numbers.` 447 | }; 448 | } 449 | 450 | // Import each selected MCP 451 | let imported = 0; 452 | const importedNames: string[] = []; 453 | const errors: string[] = []; 454 | 455 | for (const candidate of selectedCandidates) { 456 | try { 457 | // Get detailed info including env vars 458 | const details = await registryClient.getDetailedInfo(candidate.name); 459 | 460 | // For now, add without env vars (user can configure later or use clipboard) 461 | // TODO: Integrate with prompts to show confirm_add_mcp and get clipboard config 462 | const config = { 463 | command: details.command, 464 | args: details.args, 465 | env: {} 466 | }; 467 | 468 | await this.profileManager!.addMCPToProfile('all', candidate.displayName, config); 469 | imported++; 470 | importedNames.push(candidate.displayName); 471 | 472 | logger.info(`Imported ${candidate.displayName} from registry`); 473 | } catch (error: any) { 474 | errors.push(`${candidate.displayName}: ${error.message}`); 475 | logger.error(`Failed to import ${candidate.displayName}: ${error.message}`); 476 | } 477 | } 478 | 479 | let message = `✅ Imported ${imported}/${selectedCandidates.length} MCPs from registry:\n\n`; 480 | message += importedNames.map(name => ` ✓ ${name}`).join('\n'); 481 | 482 | if (errors.length > 0) { 483 | message += `\n\n❌ Failed to import ${errors.length} MCPs:\n`; 484 | message += errors.map(e => ` ✗ ${e}`).join('\n'); 485 | } 486 | 487 | message += `\n\n💡 Note: MCPs imported without environment variables. Use ncp:list to see configs, or use clipboard pattern with ncp:add to add secrets.`; 488 | 489 | return { 490 | success: imported > 0, 491 | content: message 492 | }; 493 | } catch (error: any) { 494 | return { 495 | success: false, 496 | error: `Failed to import from registry: ${error.message}` 497 | }; 498 | } 499 | } 500 | 501 | /** 502 | * Parse selection string into array of indices 503 | * Supports: "1,3,5" (individual), "1-5" (range), "*" (all) 504 | */ 505 | private parseSelection(selection: string, maxCount: number): number[] { 506 | const indices: number[] = []; 507 | 508 | // Handle "*" (all) 509 | if (selection.trim() === '*') { 510 | for (let i = 1; i <= maxCount; i++) { 511 | indices.push(i); 512 | } 513 | return indices; 514 | } 515 | 516 | // Split by comma 517 | const parts = selection.split(',').map(s => s.trim()); 518 | 519 | for (const part of parts) { 520 | // Check for range (e.g., "1-5") 521 | if (part.includes('-')) { 522 | const [start, end] = part.split('-').map(s => parseInt(s.trim(), 10)); 523 | if (!isNaN(start) && !isNaN(end) && start <= end && start >= 1 && end <= maxCount) { 524 | for (let i = start; i <= end; i++) { 525 | if (!indices.includes(i)) { 526 | indices.push(i); 527 | } 528 | } 529 | } 530 | } else { 531 | // Individual number 532 | const num = parseInt(part, 10); 533 | if (!isNaN(num) && num >= 1 && num <= maxCount && !indices.includes(num)) { 534 | indices.push(num); 535 | } 536 | } 537 | } 538 | 539 | return indices.sort((a, b) => a - b); 540 | } 541 | 542 | private async handleExport(params: any): Promise<InternalToolResult> { 543 | const to = params?.to || 'clipboard'; 544 | const destination = params?.destination; 545 | const profile = params?.profile || 'all'; 546 | 547 | try { 548 | const mcps = await this.profileManager!.getProfileMCPs(profile); 549 | 550 | if (!mcps || Object.keys(mcps).length === 0) { 551 | return { 552 | success: false, 553 | error: `No MCPs to export from profile "${profile}"` 554 | }; 555 | } 556 | 557 | const exportConfig = { 558 | mcpServers: mcps 559 | }; 560 | 561 | const jsonContent = JSON.stringify(exportConfig, null, 2); 562 | 563 | switch (to) { 564 | case 'clipboard': { 565 | const clipboardy = await import('clipboardy'); 566 | await clipboardy.default.write(jsonContent); 567 | return { 568 | success: true, 569 | content: `✅ Exported ${Object.keys(mcps).length} MCPs to clipboard` 570 | }; 571 | } 572 | 573 | case 'file': { 574 | if (!destination) { 575 | return { 576 | success: false, 577 | error: 'destination parameter required when to=file' 578 | }; 579 | } 580 | 581 | const fs = await import('fs/promises'); 582 | const path = await import('path'); 583 | 584 | // Expand ~ to home directory 585 | const expandedPath = destination.startsWith('~') 586 | ? path.join(process.env.HOME || process.env.USERPROFILE || '', destination.slice(1)) 587 | : destination; 588 | 589 | await fs.writeFile(expandedPath, jsonContent, 'utf-8'); 590 | 591 | return { 592 | success: true, 593 | content: `✅ Exported ${Object.keys(mcps).length} MCPs to ${destination}` 594 | }; 595 | } 596 | 597 | default: 598 | return { 599 | success: false, 600 | error: `Invalid to parameter: ${to}. Use: clipboard or file` 601 | }; 602 | } 603 | } catch (error: any) { 604 | return { 605 | success: false, 606 | error: `Failed to export: ${error.message}` 607 | }; 608 | } 609 | } 610 | } 611 | ``` -------------------------------------------------------------------------------- /test/discovery-engine.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | /** 2 | * Tests for DiscoveryEngine - RAG and semantic search functionality 3 | */ 4 | 5 | import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals'; 6 | import { DiscoveryEngine } from '../src/discovery/engine.js'; 7 | 8 | describe('DiscoveryEngine', () => { 9 | let discoveryEngine: DiscoveryEngine; 10 | 11 | beforeEach(() => { 12 | discoveryEngine = new DiscoveryEngine(); 13 | }); 14 | 15 | afterEach(() => { 16 | jest.clearAllMocks(); 17 | }); 18 | 19 | describe('initialization', () => { 20 | it('should create discovery engine', () => { 21 | expect(discoveryEngine).toBeDefined(); 22 | }); 23 | 24 | it('should initialize successfully', async () => { 25 | await expect(discoveryEngine.initialize()).resolves.not.toThrow(); 26 | }); 27 | }); 28 | 29 | describe('tool discovery', () => { 30 | const sampleTools = [ 31 | { 32 | name: 'read_file', 33 | description: 'Read contents of a file from the filesystem', 34 | mcpName: 'filesystem' 35 | }, 36 | { 37 | name: 'write_file', 38 | description: 'Write data to a file on the filesystem', 39 | mcpName: 'filesystem' 40 | }, 41 | { 42 | name: 'store_memory', 43 | description: 'Store information in persistent memory', 44 | mcpName: 'memory' 45 | } 46 | ]; 47 | 48 | beforeEach(async () => { 49 | await discoveryEngine.initialize(); 50 | // Index tools individually since that's the actual API 51 | for (const tool of sampleTools) { 52 | await discoveryEngine.indexTool(tool); 53 | } 54 | }); 55 | 56 | it('should find best tool for description', async () => { 57 | const result = await discoveryEngine.findBestTool('read file contents'); 58 | expect(result).toBeDefined(); 59 | if (result) { 60 | expect(result.name).toBeDefined(); 61 | expect(result.confidence).toBeGreaterThan(0); 62 | expect(result.reason).toBeDefined(); 63 | } 64 | }); 65 | 66 | it('should find relevant tools by description', async () => { 67 | const results = await discoveryEngine.findRelevantTools('file operations', 5); 68 | expect(Array.isArray(results)).toBe(true); 69 | expect(results.length).toBeLessThanOrEqual(5); 70 | }); 71 | 72 | it('should handle exact tool name search', async () => { 73 | const result = await discoveryEngine.findBestTool('read_file'); 74 | expect(result).toBeDefined(); 75 | if (result) { 76 | expect(result.confidence).toBeGreaterThan(0); 77 | } 78 | }); 79 | 80 | it('should return null for no matches', async () => { 81 | const result = await discoveryEngine.findBestTool('quantum_computing_operations'); 82 | // May return null or low confidence result 83 | if (result) { 84 | expect(result.confidence).toBeDefined(); 85 | } 86 | }); 87 | 88 | it('should find related tools', async () => { 89 | const results = await discoveryEngine.findRelatedTools('read_file'); 90 | expect(Array.isArray(results)).toBe(true); 91 | }); 92 | }); 93 | 94 | describe('tool indexing', () => { 95 | beforeEach(async () => { 96 | await discoveryEngine.initialize(); 97 | }); 98 | 99 | it('should index individual tools', async () => { 100 | const tool = { 101 | name: 'test_tool', 102 | description: 'Test tool description', 103 | mcpName: 'test' 104 | }; 105 | 106 | await expect(discoveryEngine.indexTool(tool)).resolves.not.toThrow(); 107 | }); 108 | 109 | it('should index MCP tools in bulk', async () => { 110 | const tools = [ 111 | { name: 'tool1', description: 'First tool', mcpName: 'test' }, 112 | { name: 'tool2', description: 'Second tool', mcpName: 'test' } 113 | ]; 114 | 115 | await expect(discoveryEngine.indexMCPTools('test', tools)).resolves.not.toThrow(); 116 | }); 117 | 118 | it('should handle tools with missing descriptions', async () => { 119 | const tool = { 120 | name: 'test_tool', 121 | description: '', 122 | mcpName: 'test' 123 | }; 124 | 125 | await expect(discoveryEngine.indexTool(tool)).resolves.not.toThrow(); 126 | }); 127 | }); 128 | 129 | describe('cache management', () => { 130 | beforeEach(async () => { 131 | await discoveryEngine.initialize(); 132 | }); 133 | 134 | it('should clear RAG cache', async () => { 135 | await expect(discoveryEngine.clearRagCache()).resolves.not.toThrow(); 136 | }); 137 | 138 | it('should refresh RAG cache', async () => { 139 | await expect(discoveryEngine.refreshRagCache()).resolves.not.toThrow(); 140 | }); 141 | }); 142 | 143 | describe('edge cases', () => { 144 | beforeEach(async () => { 145 | await discoveryEngine.initialize(); 146 | }); 147 | 148 | it('should handle search before indexing', async () => { 149 | const result = await discoveryEngine.findBestTool('test'); 150 | // May return null or empty result 151 | expect(result === null || typeof result === 'object').toBe(true); 152 | }); 153 | 154 | it('should handle special characters in query', async () => { 155 | await discoveryEngine.indexTool({ 156 | name: 'test_tool', 157 | description: 'Test tool', 158 | mcpName: 'test' 159 | }); 160 | 161 | const result = await discoveryEngine.findBestTool('test!@#$%'); 162 | expect(result === null || typeof result === 'object').toBe(true); 163 | }); 164 | 165 | it('should handle very long queries', async () => { 166 | await discoveryEngine.indexTool({ 167 | name: 'test_tool', 168 | description: 'Test tool', 169 | mcpName: 'test' 170 | }); 171 | 172 | const longQuery = 'test '.repeat(100); 173 | const result = await discoveryEngine.findBestTool(longQuery); 174 | expect(result === null || typeof result === 'object').toBe(true); 175 | }); 176 | 177 | it('should handle empty queries', async () => { 178 | const result = await discoveryEngine.findBestTool(''); 179 | expect(result === null || typeof result === 'object').toBe(true); 180 | }); 181 | 182 | it('should handle whitespace queries', async () => { 183 | const result = await discoveryEngine.findBestTool(' '); 184 | expect(result === null || typeof result === 'object').toBe(true); 185 | }); 186 | }); 187 | 188 | describe('error handling', () => { 189 | it('should handle keyword matching fallback', async () => { 190 | // Add a tool that should match by keyword 191 | await discoveryEngine.indexTool({ 192 | name: 'keyword:search', 193 | description: 'A tool that searches for keywords in documents', 194 | mcpName: 'keyword' 195 | }); 196 | 197 | // Test with a keyword that should work 198 | const result = await discoveryEngine.findBestTool('keyword search'); 199 | expect(result === null || typeof result === 'object').toBe(true); 200 | }); 201 | 202 | it('should handle exact tool name matching', async () => { 203 | await discoveryEngine.indexTool({ 204 | name: 'exact:match', 205 | description: 'Tool for exact matching operations', 206 | mcpName: 'exact' 207 | }); 208 | 209 | const result = await discoveryEngine.findBestTool('exact:match'); 210 | expect(result === null || typeof result === 'object').toBe(true); 211 | }); 212 | 213 | it('should handle multiple similar tools', async () => { 214 | // Add multiple similar tools 215 | await discoveryEngine.indexTool({ 216 | name: 'file:read', 217 | description: 'Read files from disk', 218 | mcpName: 'filesystem' 219 | }); 220 | 221 | await discoveryEngine.indexTool({ 222 | name: 'file:write', 223 | description: 'Write files to disk', 224 | mcpName: 'filesystem' 225 | }); 226 | 227 | await discoveryEngine.indexTool({ 228 | name: 'file:delete', 229 | description: 'Delete files from disk', 230 | mcpName: 'filesystem' 231 | }); 232 | 233 | const results = await discoveryEngine.findRelevantTools('file operations'); 234 | expect(Array.isArray(results)).toBe(true); 235 | }); 236 | 237 | it('should handle finding related tools', async () => { 238 | await discoveryEngine.indexTool({ 239 | name: 'related:main', 240 | description: 'Main tool for testing related functionality', 241 | mcpName: 'related' 242 | }); 243 | 244 | await discoveryEngine.indexTool({ 245 | name: 'related:helper', 246 | description: 'Helper tool for related operations', 247 | mcpName: 'related' 248 | }); 249 | 250 | const results = await discoveryEngine.findRelatedTools('related:main'); 251 | expect(Array.isArray(results)).toBe(true); 252 | }); 253 | 254 | it('should handle bulk indexing of MCP tools', async () => { 255 | const tools = [ 256 | { 257 | id: 'bulk1', 258 | name: 'bulk:tool1', 259 | description: 'First bulk tool', 260 | mcpServer: 'bulk', 261 | inputSchema: {} 262 | }, 263 | { 264 | id: 'bulk2', 265 | name: 'bulk:tool2', 266 | description: 'Second bulk tool', 267 | mcpServer: 'bulk', 268 | inputSchema: {} 269 | } 270 | ]; 271 | 272 | await discoveryEngine.indexMCPTools('bulk', tools); 273 | 274 | const result = await discoveryEngine.findBestTool('bulk tool'); 275 | expect(result === null || typeof result === 'object').toBe(true); 276 | }); 277 | 278 | it('should handle no matches scenario', async () => { 279 | // Search for something that definitely won't match 280 | const result = await discoveryEngine.findBestTool('nonexistent_unique_search_term_12345'); 281 | expect(result).toBeNull(); 282 | }); 283 | }); 284 | 285 | describe('fallback mechanism testing', () => { 286 | beforeEach(async () => { 287 | await discoveryEngine.initialize(); 288 | }); 289 | 290 | it('should trigger RAG fallback to keyword matching on error', async () => { 291 | // Index tools to enable fallback matching 292 | await discoveryEngine.indexTool({ 293 | name: 'file:read', 294 | description: 'Read file content operations', 295 | mcpName: 'filesystem' 296 | }); 297 | 298 | // Force RAG error by creating conditions that cause RAG failure 299 | // This should trigger fallback path (lines 50-58) 300 | const result = await discoveryEngine.findBestTool('read file'); 301 | 302 | // Should still work via fallback even if RAG fails 303 | expect(result === null || typeof result === 'object').toBe(true); 304 | }); 305 | 306 | it('should handle pattern matching fallback', async () => { 307 | // Index tools with recognizable patterns 308 | await discoveryEngine.indexTool({ 309 | name: 'pattern:match', 310 | description: 'Pattern matching tool with keywords', 311 | mcpName: 'pattern' 312 | }); 313 | 314 | // This should trigger pattern matching logic (lines 85-106) 315 | const result = await discoveryEngine.findBestTool('pattern tool for matching'); 316 | expect(result === null || typeof result === 'object').toBe(true); 317 | }); 318 | 319 | it('should handle similarity matching with scored results', async () => { 320 | // Add tools with similar descriptions for similarity scoring 321 | await discoveryEngine.indexTool({ 322 | name: 'similarity:high', 323 | description: 'Database query operations for data retrieval', 324 | mcpName: 'database' 325 | }); 326 | 327 | await discoveryEngine.indexTool({ 328 | name: 'similarity:medium', 329 | description: 'File system operations for data storage', 330 | mcpName: 'filesystem' 331 | }); 332 | 333 | // This should trigger similarity matching (lines 108-132) 334 | const result = await discoveryEngine.findBestTool('database operations for data'); 335 | expect(result === null || typeof result === 'object').toBe(true); 336 | 337 | if (result) { 338 | expect(result.confidence).toBeGreaterThan(0); 339 | expect(result.reason).toBeDefined(); 340 | } 341 | }); 342 | 343 | it('should calculate similarity scores correctly', async () => { 344 | // Add tool for similarity calculation testing 345 | await discoveryEngine.indexTool({ 346 | name: 'jaccard:test', 347 | description: 'advanced machine learning algorithms for data processing', 348 | mcpName: 'ml' 349 | }); 350 | 351 | // Test Jaccard similarity calculation (lines 134-143) 352 | const result = await discoveryEngine.findBestTool('machine learning data processing algorithms'); 353 | 354 | // Test verifies the search was attempted and returns expected format 355 | expect(result === null || typeof result === 'object').toBe(true); 356 | 357 | // If we get a result, it should have proper structure 358 | if (result) { 359 | expect(typeof result.confidence).toBe('number'); 360 | expect(result.confidence).toBeGreaterThanOrEqual(0); 361 | expect(result.confidence).toBeLessThanOrEqual(1); 362 | // Name and reason may be undefined if no match found 363 | if (result.name) expect(typeof result.name).toBe('string'); 364 | if (result.reason) expect(typeof result.reason).toBe('string'); 365 | } 366 | }); 367 | 368 | it('should handle keyword matching when other methods fail', async () => { 369 | // Add tool with specific keywords 370 | await discoveryEngine.indexTool({ 371 | name: 'keyword:fallback', 372 | description: 'Specialized tool for keyword-based search operations', 373 | mcpName: 'search' 374 | }); 375 | 376 | // This should eventually hit keyword matching fallback (lines 145+) 377 | const result = await discoveryEngine.findBestTool('specialized keyword search'); 378 | expect(result === null || typeof result === 'object').toBe(true); 379 | }); 380 | 381 | it('should handle RAG discovery success path', async () => { 382 | // Index comprehensive tools for RAG success 383 | await discoveryEngine.indexTool({ 384 | name: 'rag:success', 385 | description: 'Document processing and analysis tool', 386 | mcpName: 'docs' 387 | }); 388 | 389 | // This should trigger successful RAG path (lines 32-39) 390 | const result = await discoveryEngine.findBestTool('document analysis'); 391 | 392 | // Test verifies the search was attempted 393 | expect(result === null || typeof result === 'object').toBe(true); 394 | 395 | // If we get a result, it should have the expected structure 396 | if (result) { 397 | expect(typeof result.confidence).toBe('number'); 398 | expect(result.confidence).toBeGreaterThanOrEqual(0); 399 | expect(result.confidence).toBeLessThanOrEqual(1); 400 | // Name and reason may be undefined if no match found 401 | if (result.name) expect(typeof result.name).toBe('string'); 402 | if (result.reason) expect(typeof result.reason).toBe('string'); 403 | } 404 | }); 405 | 406 | it('should handle multi-tool discovery with error fallback', async () => { 407 | // Index multiple tools for multi-discovery testing 408 | const tools = [ 409 | { 410 | name: 'multi:tool1', 411 | description: 'First tool for multi discovery', 412 | mcpName: 'multi' 413 | }, 414 | { 415 | name: 'multi:tool2', 416 | description: 'Second tool for multi discovery', 417 | mcpName: 'multi' 418 | }, 419 | { 420 | name: 'multi:tool3', 421 | description: 'Third tool for multi discovery', 422 | mcpName: 'multi' 423 | } 424 | ]; 425 | 426 | for (const tool of tools) { 427 | await discoveryEngine.indexTool(tool); 428 | } 429 | 430 | // This should test multi-discovery error handling (lines 80-82) 431 | const results = await discoveryEngine.findRelevantTools('multi discovery tools', 3); 432 | expect(Array.isArray(results)).toBe(true); 433 | expect(results.length).toBeLessThanOrEqual(3); 434 | }); 435 | }); 436 | 437 | describe('advanced discovery scenarios', () => { 438 | beforeEach(async () => { 439 | await discoveryEngine.initialize(); 440 | }); 441 | 442 | it('should handle complex tool patterns and matching', async () => { 443 | // Add tools with complex patterns 444 | await discoveryEngine.indexTool({ 445 | name: 'complex:pattern:tool', 446 | description: 'Complex pattern matching with multiple keywords and advanced features', 447 | mcpName: 'complex' 448 | }); 449 | 450 | // Test complex pattern detection 451 | const result = await discoveryEngine.findBestTool('complex advanced pattern features'); 452 | expect(result === null || typeof result === 'object').toBe(true); 453 | }); 454 | 455 | it('should handle empty and edge case queries properly', async () => { 456 | await discoveryEngine.indexTool({ 457 | name: 'edge:case', 458 | description: 'Tool for edge case handling', 459 | mcpName: 'edge' 460 | }); 461 | 462 | // Test empty query 463 | const emptyResult = await discoveryEngine.findBestTool(''); 464 | expect(emptyResult === null || typeof emptyResult === 'object').toBe(true); 465 | 466 | // Test single character query 467 | const singleResult = await discoveryEngine.findBestTool('a'); 468 | expect(singleResult === null || typeof singleResult === 'object').toBe(true); 469 | 470 | // Test whitespace query 471 | const spaceResult = await discoveryEngine.findBestTool(' '); 472 | expect(spaceResult === null || typeof spaceResult === 'object').toBe(true); 473 | }); 474 | 475 | it('should handle high confidence scoring scenarios', async () => { 476 | // Add exact match tool for high confidence 477 | await discoveryEngine.indexTool({ 478 | name: 'exact:confidence', 479 | description: 'Exact confidence scoring tool', 480 | mcpName: 'confidence' 481 | }); 482 | 483 | // Test exact match for highest confidence 484 | const result = await discoveryEngine.findBestTool('exact confidence scoring tool'); 485 | expect(result === null || typeof result === 'object').toBe(true); 486 | 487 | if (result) { 488 | expect(result.confidence).toBeGreaterThan(0); 489 | expect(result.confidence).toBeLessThanOrEqual(1); 490 | } 491 | }); 492 | }); 493 | 494 | describe('Coverage boost: Core functionality tests', () => { 495 | beforeEach(async () => { 496 | await discoveryEngine.initialize(); 497 | }); 498 | 499 | it('should handle getRagStats method', async () => { 500 | // Test the getRagStats method (line 252) 501 | const stats = discoveryEngine.getRagStats(); 502 | expect(typeof stats === 'object' || stats === undefined).toBe(true); 503 | }); 504 | 505 | it('should handle clearRagCache method', async () => { 506 | // Test clearRagCache method (lines 258-260) 507 | await expect(discoveryEngine.clearRagCache()).resolves.not.toThrow(); 508 | }); 509 | 510 | it('should handle refreshRagCache method', async () => { 511 | // Test refreshRagCache method (lines 265-267) 512 | await expect(discoveryEngine.refreshRagCache()).resolves.not.toThrow(); 513 | }); 514 | 515 | it('should test pattern extraction from descriptions', async () => { 516 | // This will exercise extractPatternsFromDescription (lines 272-345) 517 | await discoveryEngine.indexTool({ 518 | name: 'pattern:extractor', 519 | description: 'create files and edit directories with multiple operations for data processing', 520 | mcpName: 'pattern' 521 | }); 522 | 523 | const result = await discoveryEngine.findBestTool('create multiple files'); 524 | expect(result === null || typeof result === 'object').toBe(true); 525 | }); 526 | 527 | it('should test pattern extraction from names', async () => { 528 | // This will exercise extractPatternsFromName (lines 350-369) 529 | await discoveryEngine.indexTool({ 530 | name: 'camelCaseToolName_with-hyphens', 531 | description: 'Tool with complex naming patterns', 532 | mcpName: 'naming' 533 | }); 534 | 535 | const result = await discoveryEngine.findBestTool('camelCase tool'); 536 | expect(result === null || typeof result === 'object').toBe(true); 537 | }); 538 | 539 | it('should exercise findRelatedTools method', async () => { 540 | // Test findRelatedTools (lines 189-213) 541 | await discoveryEngine.indexTool({ 542 | name: 'related:tool1', 543 | description: 'database query operations for data analysis', 544 | mcpName: 'db' 545 | }); 546 | 547 | await discoveryEngine.indexTool({ 548 | name: 'related:tool2', 549 | description: 'database storage operations for data management', 550 | mcpName: 'storage' 551 | }); 552 | 553 | const related = await discoveryEngine.findRelatedTools('related:tool1'); 554 | expect(Array.isArray(related)).toBe(true); 555 | }); 556 | 557 | it('should test getStats method', async () => { 558 | // Test getStats method (lines 459-466) 559 | const stats = discoveryEngine.getStats(); 560 | expect(typeof stats).toBe('object'); 561 | expect(typeof stats.totalTools).toBe('number'); 562 | expect(typeof stats.totalPatterns).toBe('number'); 563 | expect(typeof stats.toolsWithPatterns).toBe('number'); 564 | }); 565 | 566 | it('should test git operation overrides', async () => { 567 | // Test checkGitOperationOverride (lines 379-411) 568 | const gitQueries = [ 569 | 'git commit', 570 | 'git push', 571 | 'git status', 572 | 'commit changes', 573 | 'check git status' 574 | ]; 575 | 576 | for (const query of gitQueries) { 577 | const result = await discoveryEngine.findBestTool(query); 578 | // Git operations should either return Shell:run_command or null/fallback 579 | expect(result === null || typeof result === 'object').toBe(true); 580 | } 581 | }); 582 | 583 | it('should test single file operation overrides', async () => { 584 | // Test checkSingleFileOperationOverride (lines 416-454) 585 | const fileQueries = [ 586 | 'show file', 587 | 'view file content', 588 | 'read file', 589 | 'display single file' 590 | ]; 591 | 592 | for (const query of fileQueries) { 593 | const result = await discoveryEngine.findBestTool(query); 594 | // Should either return desktop-commander:read_file or fallback 595 | expect(result === null || typeof result === 'object').toBe(true); 596 | } 597 | }); 598 | }); 599 | }); ``` -------------------------------------------------------------------------------- /src/testing/mcp-definitions.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "mcps": { 3 | "shell": { 4 | "name": "shell", 5 | "version": "1.0.0", 6 | "description": "Execute shell commands and system operations", 7 | "category": "system-operations", 8 | "tools": { 9 | "run_command": { 10 | "name": "run_command", 11 | "description": "Execute shell commands with environment control and output capture", 12 | "inputSchema": { 13 | "type": "object", 14 | "properties": { 15 | "command": { 16 | "type": "string", 17 | "description": "Shell command to execute" 18 | }, 19 | "working_directory": { 20 | "type": "string", 21 | "description": "Working directory for command execution" 22 | }, 23 | "environment": { 24 | "type": "object", 25 | "description": "Environment variables" 26 | } 27 | }, 28 | "required": ["command"] 29 | } 30 | } 31 | } 32 | }, 33 | "git": { 34 | "name": "git", 35 | "version": "1.0.0", 36 | "description": "Git version control operations including commits, branches, and repository management", 37 | "category": "developer-tools", 38 | "tools": { 39 | "commit": { 40 | "name": "commit", 41 | "description": "Create a git commit with specified message and files", 42 | "inputSchema": { 43 | "type": "object", 44 | "properties": { 45 | "message": { 46 | "type": "string", 47 | "description": "Commit message" 48 | }, 49 | "files": { 50 | "type": "array", 51 | "items": {"type": "string"}, 52 | "description": "Files to include in commit" 53 | }, 54 | "add_all": { 55 | "type": "boolean", 56 | "description": "Add all modified files" 57 | } 58 | }, 59 | "required": ["message"] 60 | } 61 | }, 62 | "push": { 63 | "name": "push", 64 | "description": "Push commits to remote repository", 65 | "inputSchema": { 66 | "type": "object", 67 | "properties": { 68 | "remote": { 69 | "type": "string", 70 | "description": "Remote repository name", 71 | "default": "origin" 72 | }, 73 | "branch": { 74 | "type": "string", 75 | "description": "Branch to push" 76 | }, 77 | "force": { 78 | "type": "boolean", 79 | "description": "Force push" 80 | } 81 | } 82 | } 83 | }, 84 | "pull": { 85 | "name": "pull", 86 | "description": "Pull changes from remote repository", 87 | "inputSchema": { 88 | "type": "object", 89 | "properties": { 90 | "remote": { 91 | "type": "string", 92 | "description": "Remote repository name", 93 | "default": "origin" 94 | }, 95 | "branch": { 96 | "type": "string", 97 | "description": "Branch to pull from" 98 | } 99 | } 100 | } 101 | } 102 | } 103 | }, 104 | "postgres": { 105 | "name": "postgres", 106 | "version": "1.0.0", 107 | "description": "PostgreSQL database operations including queries, schema management, and data manipulation", 108 | "category": "database", 109 | "tools": { 110 | "query": { 111 | "name": "query", 112 | "description": "Execute SQL query against PostgreSQL database", 113 | "inputSchema": { 114 | "type": "object", 115 | "properties": { 116 | "sql": { 117 | "type": "string", 118 | "description": "SQL query to execute" 119 | }, 120 | "parameters": { 121 | "type": "array", 122 | "description": "Query parameters for prepared statements" 123 | }, 124 | "database": { 125 | "type": "string", 126 | "description": "Database name" 127 | } 128 | }, 129 | "required": ["sql"] 130 | } 131 | }, 132 | "insert": { 133 | "name": "insert", 134 | "description": "Insert data into PostgreSQL table", 135 | "inputSchema": { 136 | "type": "object", 137 | "properties": { 138 | "table": { 139 | "type": "string", 140 | "description": "Table name" 141 | }, 142 | "data": { 143 | "type": "object", 144 | "description": "Data to insert" 145 | }, 146 | "returning": { 147 | "type": "array", 148 | "items": {"type": "string"}, 149 | "description": "Columns to return" 150 | } 151 | }, 152 | "required": ["table", "data"] 153 | } 154 | } 155 | } 156 | }, 157 | "github": { 158 | "name": "github", 159 | "version": "1.0.0", 160 | "description": "GitHub API integration for repository management, file operations, issues, and pull requests", 161 | "category": "developer-tools", 162 | "tools": { 163 | "create_repository": { 164 | "name": "create_repository", 165 | "description": "Create a new GitHub repository", 166 | "inputSchema": { 167 | "type": "object", 168 | "properties": { 169 | "name": { 170 | "type": "string", 171 | "description": "Repository name" 172 | }, 173 | "description": { 174 | "type": "string", 175 | "description": "Repository description" 176 | }, 177 | "private": { 178 | "type": "boolean", 179 | "description": "Make repository private" 180 | }, 181 | "initialize": { 182 | "type": "boolean", 183 | "description": "Initialize with README" 184 | } 185 | }, 186 | "required": ["name"] 187 | } 188 | }, 189 | "create_issue": { 190 | "name": "create_issue", 191 | "description": "Create a new issue in GitHub repository", 192 | "inputSchema": { 193 | "type": "object", 194 | "properties": { 195 | "title": { 196 | "type": "string", 197 | "description": "Issue title" 198 | }, 199 | "body": { 200 | "type": "string", 201 | "description": "Issue description" 202 | }, 203 | "labels": { 204 | "type": "array", 205 | "items": {"type": "string"}, 206 | "description": "Issue labels" 207 | }, 208 | "repository": { 209 | "type": "string", 210 | "description": "Repository name (owner/repo)" 211 | } 212 | }, 213 | "required": ["title", "repository"] 214 | } 215 | } 216 | } 217 | }, 218 | "openai": { 219 | "name": "openai", 220 | "version": "1.0.0", 221 | "description": "OpenAI API integration for language models, embeddings, and AI operations", 222 | "category": "ai-ml", 223 | "tools": { 224 | "completion": { 225 | "name": "completion", 226 | "description": "Generate text completion using OpenAI language models", 227 | "inputSchema": { 228 | "type": "object", 229 | "properties": { 230 | "prompt": { 231 | "type": "string", 232 | "description": "Input prompt for completion" 233 | }, 234 | "model": { 235 | "type": "string", 236 | "description": "OpenAI model to use", 237 | "default": "gpt-4" 238 | }, 239 | "max_tokens": { 240 | "type": "integer", 241 | "description": "Maximum tokens to generate" 242 | }, 243 | "temperature": { 244 | "type": "number", 245 | "description": "Sampling temperature" 246 | } 247 | }, 248 | "required": ["prompt"] 249 | } 250 | }, 251 | "generate": { 252 | "name": "generate", 253 | "description": "Generate content using OpenAI models with advanced parameters", 254 | "inputSchema": { 255 | "type": "object", 256 | "properties": { 257 | "messages": { 258 | "type": "array", 259 | "description": "Chat messages for conversation" 260 | }, 261 | "system_prompt": { 262 | "type": "string", 263 | "description": "System instruction" 264 | }, 265 | "model": { 266 | "type": "string", 267 | "description": "Model identifier" 268 | } 269 | }, 270 | "required": ["messages"] 271 | } 272 | } 273 | } 274 | }, 275 | "stripe": { 276 | "name": "stripe", 277 | "version": "1.0.0", 278 | "description": "Complete payment processing for online businesses including charges, subscriptions, and refunds", 279 | "category": "financial", 280 | "tools": { 281 | "charge": { 282 | "name": "charge", 283 | "description": "Process a payment charge using Stripe", 284 | "inputSchema": { 285 | "type": "object", 286 | "properties": { 287 | "amount": { 288 | "type": "integer", 289 | "description": "Amount in cents" 290 | }, 291 | "currency": { 292 | "type": "string", 293 | "description": "Currency code", 294 | "default": "usd" 295 | }, 296 | "source": { 297 | "type": "string", 298 | "description": "Payment source (card token)" 299 | }, 300 | "description": { 301 | "type": "string", 302 | "description": "Charge description" 303 | } 304 | }, 305 | "required": ["amount", "source"] 306 | } 307 | }, 308 | "refund": { 309 | "name": "refund", 310 | "description": "Process a refund for a Stripe charge", 311 | "inputSchema": { 312 | "type": "object", 313 | "properties": { 314 | "charge_id": { 315 | "type": "string", 316 | "description": "Stripe charge ID to refund" 317 | }, 318 | "amount": { 319 | "type": "integer", 320 | "description": "Refund amount in cents (partial refund)" 321 | }, 322 | "reason": { 323 | "type": "string", 324 | "description": "Refund reason" 325 | } 326 | }, 327 | "required": ["charge_id"] 328 | } 329 | } 330 | } 331 | }, 332 | "aws": { 333 | "name": "aws", 334 | "version": "1.0.0", 335 | "description": "Amazon Web Services integration for EC2, S3, Lambda, and cloud resource management", 336 | "category": "cloud-infrastructure", 337 | "tools": { 338 | "deploy": { 339 | "name": "deploy", 340 | "description": "Deploy application to AWS infrastructure", 341 | "inputSchema": { 342 | "type": "object", 343 | "properties": { 344 | "service": { 345 | "type": "string", 346 | "description": "AWS service (ec2, lambda, ecs)" 347 | }, 348 | "region": { 349 | "type": "string", 350 | "description": "AWS region" 351 | }, 352 | "config": { 353 | "type": "object", 354 | "description": "Deployment configuration" 355 | } 356 | }, 357 | "required": ["service", "region"] 358 | } 359 | }, 360 | "s3_upload": { 361 | "name": "s3_upload", 362 | "description": "Upload files to Amazon S3 bucket", 363 | "inputSchema": { 364 | "type": "object", 365 | "properties": { 366 | "bucket": { 367 | "type": "string", 368 | "description": "S3 bucket name" 369 | }, 370 | "key": { 371 | "type": "string", 372 | "description": "Object key (path)" 373 | }, 374 | "file_path": { 375 | "type": "string", 376 | "description": "Local file path to upload" 377 | }, 378 | "public": { 379 | "type": "boolean", 380 | "description": "Make object public" 381 | } 382 | }, 383 | "required": ["bucket", "key", "file_path"] 384 | } 385 | } 386 | } 387 | }, 388 | "docker": { 389 | "name": "docker", 390 | "version": "1.0.0", 391 | "description": "Container management including Docker operations, image building, and deployment", 392 | "category": "system-operations", 393 | "tools": { 394 | "build": { 395 | "name": "build", 396 | "description": "Build Docker image from Dockerfile", 397 | "inputSchema": { 398 | "type": "object", 399 | "properties": { 400 | "tag": { 401 | "type": "string", 402 | "description": "Image tag" 403 | }, 404 | "dockerfile": { 405 | "type": "string", 406 | "description": "Path to Dockerfile" 407 | }, 408 | "context": { 409 | "type": "string", 410 | "description": "Build context path" 411 | }, 412 | "build_args": { 413 | "type": "object", 414 | "description": "Build arguments" 415 | } 416 | }, 417 | "required": ["tag"] 418 | } 419 | }, 420 | "run": { 421 | "name": "run", 422 | "description": "Run Docker container", 423 | "inputSchema": { 424 | "type": "object", 425 | "properties": { 426 | "image": { 427 | "type": "string", 428 | "description": "Docker image to run" 429 | }, 430 | "ports": { 431 | "type": "array", 432 | "items": {"type": "string"}, 433 | "description": "Port mappings" 434 | }, 435 | "volumes": { 436 | "type": "array", 437 | "items": {"type": "string"}, 438 | "description": "Volume mounts" 439 | }, 440 | "environment": { 441 | "type": "object", 442 | "description": "Environment variables" 443 | } 444 | }, 445 | "required": ["image"] 446 | } 447 | } 448 | } 449 | }, 450 | 451 | "neo4j": { 452 | "name": "neo4j", 453 | "version": "1.0.0", 454 | "description": "Neo4j graph database server with schema management and read/write cypher operations", 455 | "category": "database", 456 | "tools": { 457 | "cypher": { 458 | "name": "cypher", 459 | "description": "Execute Cypher query against Neo4j graph database", 460 | "inputSchema": { 461 | "type": "object", 462 | "properties": { 463 | "query": {"type": "string", "description": "Cypher query to execute"}, 464 | "parameters": {"type": "object", "description": "Query parameters"} 465 | }, 466 | "required": ["query"] 467 | } 468 | } 469 | } 470 | }, 471 | 472 | "sqlite": { 473 | "name": "sqlite", 474 | "version": "1.0.0", 475 | "description": "SQLite local database operations for lightweight data storage and queries", 476 | "category": "database", 477 | "tools": { 478 | "query": { 479 | "name": "query", 480 | "description": "Execute SQL query against SQLite database", 481 | "inputSchema": { 482 | "type": "object", 483 | "properties": { 484 | "sql": {"type": "string", "description": "SQL query to execute"}, 485 | "database_path": {"type": "string", "description": "Path to SQLite database file"} 486 | }, 487 | "required": ["sql"] 488 | } 489 | } 490 | } 491 | }, 492 | 493 | "mongodb": { 494 | "name": "mongodb", 495 | "version": "1.0.0", 496 | "description": "MongoDB document database operations with aggregation and indexing", 497 | "category": "database", 498 | "tools": { 499 | "find": { 500 | "name": "find", 501 | "description": "Find documents in MongoDB collection", 502 | "inputSchema": { 503 | "type": "object", 504 | "properties": { 505 | "collection": {"type": "string", "description": "Collection name"}, 506 | "filter": {"type": "object", "description": "Query filter"}, 507 | "limit": {"type": "integer", "description": "Max results to return"} 508 | }, 509 | "required": ["collection"] 510 | } 511 | }, 512 | "insert": { 513 | "name": "insert", 514 | "description": "Insert documents into MongoDB collection", 515 | "inputSchema": { 516 | "type": "object", 517 | "properties": { 518 | "collection": {"type": "string", "description": "Collection name"}, 519 | "documents": {"type": "array", "description": "Documents to insert"} 520 | }, 521 | "required": ["collection", "documents"] 522 | } 523 | } 524 | } 525 | }, 526 | 527 | "slack": { 528 | "name": "slack", 529 | "version": "1.0.0", 530 | "description": "Slack integration for messaging, channel management, and team communication", 531 | "category": "communication", 532 | "tools": { 533 | "send_message": { 534 | "name": "send_message", 535 | "description": "Send message to Slack channel or user", 536 | "inputSchema": { 537 | "type": "object", 538 | "properties": { 539 | "channel": {"type": "string", "description": "Channel or user ID"}, 540 | "text": {"type": "string", "description": "Message text"}, 541 | "attachments": {"type": "array", "description": "Message attachments"} 542 | }, 543 | "required": ["channel", "text"] 544 | } 545 | } 546 | } 547 | }, 548 | 549 | "notion": { 550 | "name": "notion", 551 | "version": "1.0.0", 552 | "description": "Notion workspace management for documents, databases, and collaborative content", 553 | "category": "productivity", 554 | "tools": { 555 | "create_page": { 556 | "name": "create_page", 557 | "description": "Create new page in Notion workspace", 558 | "inputSchema": { 559 | "type": "object", 560 | "properties": { 561 | "parent": {"type": "string", "description": "Parent page or database ID"}, 562 | "title": {"type": "string", "description": "Page title"}, 563 | "content": {"type": "array", "description": "Page content blocks"} 564 | }, 565 | "required": ["parent", "title"] 566 | } 567 | } 568 | } 569 | }, 570 | 571 | "filesystem": { 572 | "name": "filesystem", 573 | "version": "1.0.0", 574 | "description": "Local file system operations including reading, writing, and directory management", 575 | "category": "file-operations", 576 | "tools": { 577 | "read_file": { 578 | "name": "read_file", 579 | "description": "Read contents of a file", 580 | "inputSchema": { 581 | "type": "object", 582 | "properties": { 583 | "path": {"type": "string", "description": "File path to read"}, 584 | "encoding": {"type": "string", "description": "File encoding", "default": "utf8"} 585 | }, 586 | "required": ["path"] 587 | } 588 | }, 589 | "write_file": { 590 | "name": "write_file", 591 | "description": "Write content to a file", 592 | "inputSchema": { 593 | "type": "object", 594 | "properties": { 595 | "path": {"type": "string", "description": "File path to write"}, 596 | "content": {"type": "string", "description": "Content to write"}, 597 | "mode": {"type": "string", "description": "Write mode", "default": "w"} 598 | }, 599 | "required": ["path", "content"] 600 | } 601 | } 602 | } 603 | }, 604 | 605 | "playwright": { 606 | "name": "playwright", 607 | "version": "1.0.0", 608 | "description": "Browser automation and web scraping with cross-browser support", 609 | "category": "web-automation", 610 | "tools": { 611 | "navigate": { 612 | "name": "navigate", 613 | "description": "Navigate browser to URL", 614 | "inputSchema": { 615 | "type": "object", 616 | "properties": { 617 | "url": {"type": "string", "description": "URL to navigate to"}, 618 | "browser": {"type": "string", "description": "Browser type", "default": "chromium"} 619 | }, 620 | "required": ["url"] 621 | } 622 | }, 623 | "screenshot": { 624 | "name": "screenshot", 625 | "description": "Take screenshot of current page", 626 | "inputSchema": { 627 | "type": "object", 628 | "properties": { 629 | "path": {"type": "string", "description": "Screenshot save path"}, 630 | "full_page": {"type": "boolean", "description": "Capture full page"} 631 | }, 632 | "required": ["path"] 633 | } 634 | } 635 | } 636 | }, 637 | 638 | "kubernetes": { 639 | "name": "kubernetes", 640 | "version": "1.0.0", 641 | "description": "Kubernetes cluster management and container orchestration", 642 | "category": "cloud-infrastructure", 643 | "tools": { 644 | "deploy": { 645 | "name": "deploy", 646 | "description": "Deploy application to Kubernetes cluster", 647 | "inputSchema": { 648 | "type": "object", 649 | "properties": { 650 | "manifest": {"type": "string", "description": "Kubernetes manifest YAML"}, 651 | "namespace": {"type": "string", "description": "Target namespace"}, 652 | "context": {"type": "string", "description": "Kubectl context"} 653 | }, 654 | "required": ["manifest"] 655 | } 656 | }, 657 | "scale": { 658 | "name": "scale", 659 | "description": "Scale Kubernetes deployment replicas", 660 | "inputSchema": { 661 | "type": "object", 662 | "properties": { 663 | "deployment": {"type": "string", "description": "Deployment name"}, 664 | "replicas": {"type": "integer", "description": "Number of replicas"}, 665 | "namespace": {"type": "string", "description": "Namespace"} 666 | }, 667 | "required": ["deployment", "replicas"] 668 | } 669 | } 670 | } 671 | }, 672 | 673 | "elasticsearch": { 674 | "name": "elasticsearch", 675 | "version": "1.0.0", 676 | "description": "Elasticsearch search and analytics engine operations", 677 | "category": "search", 678 | "tools": { 679 | "search": { 680 | "name": "search", 681 | "description": "Search documents in Elasticsearch index", 682 | "inputSchema": { 683 | "type": "object", 684 | "properties": { 685 | "index": {"type": "string", "description": "Index name"}, 686 | "query": {"type": "object", "description": "Elasticsearch query DSL"}, 687 | "size": {"type": "integer", "description": "Max results"} 688 | }, 689 | "required": ["index", "query"] 690 | } 691 | } 692 | } 693 | } 694 | } 695 | } ``` -------------------------------------------------------------------------------- /src/discovery/semantic-enhancement-engine.ts: -------------------------------------------------------------------------------- ```typescript 1 | /** 2 | * Semantic Enhancement Engine for Tool Discovery 3 | * 4 | * INDUSTRY PURPOSE: Addresses semantic gaps in vector-based tool discovery through 5 | * two complementary enhancement mechanisms: 6 | * 7 | * 1. CAPABILITY INFERENCE SYSTEM (Global Domain Knowledge) 8 | * - Infers implicit capabilities from tool categories/types 9 | * - Example: shell MCP → can perform git, docker, ffmpeg operations 10 | * - Fills knowledge gaps that vector similarity cannot capture 11 | * 12 | * 2. SEMANTIC INTENT RESOLUTION (Context-Specific Language Mapping) 13 | * - Maps natural language expressions to domain-specific operations 14 | * - Example: "upload my code" → git:push, github:create_repository 15 | * - Resolves contextual language that differs from tool naming 16 | * 17 | * This follows established NLP/IR patterns for query expansion and semantic matching. 18 | */ 19 | 20 | import { logger } from '../utils/logger.js'; 21 | 22 | /** 23 | * Domain Capability Inference - Maps tool types to their implicit capabilities 24 | * INDUSTRY TERM: Capability Inference / Domain Knowledge Graph 25 | */ 26 | interface CapabilityInferenceRule { 27 | implicitDomains: string[]; // Capabilities that can be inferred from this tool type 28 | confidenceScore: number; // Inference confidence (0.0-1.0) 29 | applicableContext?: string; // Context where this inference applies 30 | } 31 | 32 | /** 33 | * Semantic Intent Resolution - Maps user language to specific tool operations 34 | * INDUSTRY TERM: Intent Entity Resolution / Contextual Semantic Mapping 35 | */ 36 | interface SemanticResolutionRule { 37 | targetOperations: string[]; // Specific tool operations this resolves to 38 | resolutionRationale: string; // Why this mapping exists 39 | confidenceScore: number; // Resolution confidence (0.0-1.0) 40 | domainContext?: string; // Domain where this resolution applies 41 | } 42 | 43 | /** 44 | * Enhancement Result - Output of semantic enhancement process 45 | * INDUSTRY TERM: Semantic Augmentation / Relevance Enhancement 46 | */ 47 | interface SemanticEnhancement { 48 | enhancementType: 'capability_inference' | 'intent_resolution'; 49 | relevanceBoost: number; // Similarity score boost to apply 50 | enhancementReason: string; // Human-readable explanation 51 | confidenceLevel: number; // Enhancement confidence 52 | } 53 | 54 | export class SemanticEnhancementEngine { 55 | 56 | /** 57 | * CAPABILITY INFERENCE SYSTEM 58 | * Maps tool types/categories to their implicit capability domains 59 | * 60 | * PURPOSE: Vector search doesn't know that 'shell' can do git operations, 61 | * but humans intuitively understand this domain knowledge. 62 | */ 63 | private capabilityInferenceRules: Record<string, CapabilityInferenceRule> = { 64 | 65 | // Shell/Terminal capability inference 66 | 'shell': { 67 | implicitDomains: [ 68 | 'git version control operations', 69 | 'file system management and navigation', 70 | 'archive operations (tar, zip, gzip, compression)', 71 | 'package management (npm, yarn, pip, cargo, apt)', 72 | 'network operations (curl, wget, ssh, scp)', 73 | 'text processing (grep, sed, awk, find)', 74 | 'process management (ps, kill, top, htop)', 75 | 'system administration and monitoring', 76 | 'ffmpeg video processing and conversion', 77 | 'imagemagick image manipulation', 78 | 'docker container management', 79 | 'kubernetes cluster operations', 80 | 'terraform infrastructure provisioning', 81 | 'ansible automation and configuration', 82 | 'database CLI tools (psql, mysql, mongo)', 83 | 'cloud CLI tools (aws, gcloud, azure)', 84 | 'build tools (make, cmake, gradle, webpack)', 85 | 'testing frameworks (jest, pytest, cargo test)', 86 | 'linters and formatters (eslint, prettier, black)', 87 | 'performance monitoring (iostat, netstat, vmstat)', 88 | 'log analysis and monitoring (tail, journalctl)', 89 | 'cron job management and scheduling', 90 | 'systemd service control and management' 91 | ], 92 | confidenceScore: 0.75, // High confidence, but not all shells have all tools 93 | applicableContext: 'unix-like systems with developer tooling' 94 | }, 95 | 96 | // Database capability inference 97 | 'postgres': { 98 | implicitDomains: [ 99 | 'SQL query execution and optimization', 100 | 'ACID transaction management', 101 | 'stored procedures and user-defined functions', 102 | 'triggers and database constraints', 103 | 'JSON and JSONB document operations', 104 | 'full-text search capabilities', 105 | 'materialized views and query caching', 106 | 'table partitioning and sharding', 107 | 'streaming replication and failover', 108 | 'database backup and recovery', 109 | 'performance monitoring and tuning', 110 | 'extension management (PostGIS, pgvector, timescale)', 111 | 'user authentication and role management', 112 | 'connection pooling and resource management' 113 | ], 114 | confidenceScore: 0.95, 115 | applicableContext: 'PostgreSQL database operations' 116 | }, 117 | 118 | 'mongodb': { 119 | implicitDomains: [ 120 | 'document-oriented data operations', 121 | 'aggregation pipeline processing', 122 | 'flexible indexing strategies', 123 | 'replica set configuration and management', 124 | 'horizontal sharding and distribution', 125 | 'change stream event processing', 126 | 'multi-document ACID transactions', 127 | 'GridFS large file storage', 128 | 'geospatial query operations', 129 | 'text search and indexing', 130 | 'schema validation and enforcement', 131 | 'database backup and restore operations' 132 | ], 133 | confidenceScore: 0.95, 134 | applicableContext: 'MongoDB NoSQL document database' 135 | }, 136 | 137 | // Cloud infrastructure capability inference 138 | 'aws': { 139 | implicitDomains: [ 140 | 'EC2 virtual machine management', 141 | 'S3 object storage operations', 142 | 'Lambda serverless function deployment', 143 | 'RDS managed database services', 144 | 'DynamoDB NoSQL database operations', 145 | 'ECS and EKS container orchestration', 146 | 'CloudFormation infrastructure as code', 147 | 'IAM identity and access management', 148 | 'VPC networking and security groups', 149 | 'CloudWatch monitoring and logging', 150 | 'SQS and SNS messaging services', 151 | 'API Gateway management and deployment', 152 | 'Route53 DNS management', 153 | 'CloudFront CDN configuration', 154 | 'Elastic Load Balancing configuration' 155 | ], 156 | confidenceScore: 0.9, 157 | applicableContext: 'Amazon Web Services cloud platform' 158 | }, 159 | 160 | // AI/ML capability inference 161 | 'openai': { 162 | implicitDomains: [ 163 | 'large language model text generation', 164 | 'conversational AI and chat completion', 165 | 'text embeddings and semantic similarity', 166 | 'model fine-tuning and customization', 167 | 'function calling and tool integration', 168 | 'vision and image analysis capabilities', 169 | 'audio transcription and speech processing', 170 | 'code generation and programming assistance', 171 | 'content moderation and safety filtering', 172 | 'token usage tracking and cost optimization', 173 | 'model selection and parameter tuning', 174 | 'prompt engineering and optimization' 175 | ], 176 | confidenceScore: 0.9, 177 | applicableContext: 'OpenAI API and language model operations' 178 | }, 179 | 180 | // Communication capability inference 181 | 'slack': { 182 | implicitDomains: [ 183 | 'team messaging and communication', 184 | 'channel management and organization', 185 | 'file sharing and document collaboration', 186 | 'workflow automation and bot integration', 187 | 'user and workspace management', 188 | 'notification and alert systems', 189 | 'thread discussions and replies', 190 | 'emoji reactions and status updates', 191 | 'direct messaging and group chats', 192 | 'integration with external tools and services' 193 | ], 194 | confidenceScore: 0.95, 195 | applicableContext: 'team collaboration and workplace communication' 196 | }, 197 | 198 | 'discord': { 199 | implicitDomains: [ 200 | 'community messaging and voice chat', 201 | 'server and channel management', 202 | 'user roles and permission management', 203 | 'bot development and automation', 204 | 'voice and video communication', 205 | 'screen sharing and streaming', 206 | 'community moderation tools', 207 | 'gaming integration and rich presence', 208 | 'webhook integrations and notifications' 209 | ], 210 | confidenceScore: 0.9, 211 | applicableContext: 'community management and gaming communication' 212 | }, 213 | 214 | 'gmail': { 215 | implicitDomains: [ 216 | 'email sending and receiving operations', 217 | 'inbox management and organization', 218 | 'email filtering and label management', 219 | 'attachment handling and file sharing', 220 | 'contact management and address books', 221 | 'calendar integration and scheduling', 222 | 'thread management and conversations', 223 | 'search and archival operations', 224 | 'spam detection and security filtering' 225 | ], 226 | confidenceScore: 0.95, 227 | applicableContext: 'professional email communication and management' 228 | }, 229 | 230 | // Financial capability inference 231 | 'stripe': { 232 | implicitDomains: [ 233 | 'payment processing and transactions', 234 | 'subscription billing and recurring payments', 235 | 'customer management and profiles', 236 | 'invoice generation and management', 237 | 'dispute handling and chargeback management', 238 | 'financial reporting and analytics', 239 | 'tax calculation and compliance', 240 | 'multi-currency support and conversion', 241 | 'fraud detection and security measures', 242 | 'webhook events and payment notifications', 243 | 'marketplace and platform payments', 244 | 'PCI compliance and secure tokenization' 245 | ], 246 | confidenceScore: 0.95, 247 | applicableContext: 'e-commerce and online payment processing' 248 | }, 249 | 250 | // Calendar capability inference 251 | 'calendar': { 252 | implicitDomains: [ 253 | 'event creation and scheduling', 254 | 'meeting management and invitations', 255 | 'calendar sharing and permissions', 256 | 'recurring event management', 257 | 'reminder and notification systems', 258 | 'availability checking and conflict resolution', 259 | 'time zone management and conversion', 260 | 'calendar synchronization across platforms', 261 | 'resource booking and room management', 262 | 'integration with email and communication tools' 263 | ], 264 | confidenceScore: 0.9, 265 | applicableContext: 'scheduling and time management' 266 | }, 267 | 268 | // Productivity capability inference 269 | 'google-sheets': { 270 | implicitDomains: [ 271 | 'spreadsheet data manipulation and analysis', 272 | 'formula calculation and data processing', 273 | 'chart creation and data visualization', 274 | 'collaborative editing and sharing', 275 | 'data import and export operations', 276 | 'cell formatting and conditional styling', 277 | 'pivot table creation and analysis', 278 | 'data validation and input constraints', 279 | 'automation with Google Apps Script', 280 | 'integration with other Google Workspace tools' 281 | ], 282 | confidenceScore: 0.9, 283 | applicableContext: 'data analysis and collaborative spreadsheet work' 284 | }, 285 | 286 | // Enhanced cloud capability inference 287 | 'cloudflare': { 288 | implicitDomains: [ 289 | 'DNS management and domain configuration', 290 | 'CDN and content delivery optimization', 291 | 'DDoS protection and security filtering', 292 | 'SSL certificate management', 293 | 'website performance optimization', 294 | 'worker scripts and edge computing', 295 | 'load balancing and traffic distribution', 296 | 'firewall rules and access control', 297 | 'analytics and performance monitoring' 298 | ], 299 | confidenceScore: 0.9, 300 | applicableContext: 'web performance and security services' 301 | } 302 | }; 303 | 304 | /** 305 | * SEMANTIC INTENT RESOLUTION SYSTEM 306 | * Maps natural language user expressions to specific tool operations 307 | * 308 | * PURPOSE: Users say "upload my code" but tools are named "git push". 309 | * This bridges the language gap with contextual semantic mapping. 310 | */ 311 | private semanticResolutionRules: Record<string, SemanticResolutionRule> = { 312 | 313 | // Version control semantic resolutions 314 | 'commit my changes': { 315 | targetOperations: ['git:commit', 'github:commit', 'gitlab:commit', 'shell:run_command'], 316 | resolutionRationale: 'In development context, committing changes refers to version control operations', 317 | confidenceScore: 0.85, 318 | domainContext: 'software development and version control' 319 | }, 320 | 321 | 'save to git': { 322 | targetOperations: ['git:add', 'git:commit', 'shell:run_command'], 323 | resolutionRationale: 'Saving to Git involves staging and committing changes to repository', 324 | confidenceScore: 0.9, 325 | domainContext: 'version control workflow' 326 | }, 327 | 328 | 'upload my code': { 329 | targetOperations: ['git:push', 'github:create_repository', 'gitlab:push'], 330 | resolutionRationale: 'In repository context, uploading code means pushing to remote repository', 331 | confidenceScore: 0.8, 332 | domainContext: 'code sharing and collaboration' 333 | }, 334 | 335 | // Data persistence semantic resolutions 336 | 'store customer data': { 337 | targetOperations: ['postgres:insert', 'mongodb:insert', 'mysql:insert', 'dynamodb:put_item'], 338 | resolutionRationale: 'Storing customer data requires database persistence operations', 339 | confidenceScore: 0.9, 340 | domainContext: 'data persistence and customer management' 341 | }, 342 | 343 | 'analyze sales data': { 344 | targetOperations: ['postgres:query', 'mongodb:aggregate', 'elasticsearch:search', 'influxdb:query'], 345 | resolutionRationale: 'Data analysis requires querying and aggregation capabilities', 346 | confidenceScore: 0.85, 347 | domainContext: 'business intelligence and analytics' 348 | }, 349 | 350 | // Cloud deployment semantic resolutions 351 | 'deploy my application': { 352 | targetOperations: ['docker:build', 'aws:deploy', 'kubernetes:deploy', 'shell:run_command'], 353 | resolutionRationale: 'Application deployment uses containerization and cloud platform services', 354 | confidenceScore: 0.8, 355 | domainContext: 'application deployment and DevOps' 356 | }, 357 | 358 | 'scale my service': { 359 | targetOperations: ['kubernetes:scale', 'aws:autoscaling', 'docker:scale'], 360 | resolutionRationale: 'Service scaling requires orchestration platform operations', 361 | confidenceScore: 0.85, 362 | domainContext: 'infrastructure scaling and performance' 363 | }, 364 | 365 | // AI/ML semantic resolutions 366 | 'generate text': { 367 | targetOperations: ['openai:completion', 'anthropic:generate', 'huggingface:generate'], 368 | resolutionRationale: 'Text generation requires large language model API operations', 369 | confidenceScore: 0.9, 370 | domainContext: 'artificial intelligence and content generation' 371 | }, 372 | 373 | 'train a model': { 374 | targetOperations: ['huggingface:train', 'tensorflow:train', 'pytorch:train', 'mlflow:log_model'], 375 | resolutionRationale: 'Model training requires machine learning framework operations', 376 | confidenceScore: 0.85, 377 | domainContext: 'machine learning and model development' 378 | }, 379 | 380 | // Communication semantic resolutions 381 | 'send a message to my team': { 382 | targetOperations: ['slack:send_message', 'discord:send_message', 'teams:send_message', 'gmail:send_email'], 383 | resolutionRationale: 'Team messaging requires communication platform operations', 384 | confidenceScore: 0.9, 385 | domainContext: 'team communication and collaboration' 386 | }, 387 | 388 | 'message my team': { 389 | targetOperations: ['slack:send_message', 'discord:send_message', 'teams:send_message'], 390 | resolutionRationale: 'Messaging teams uses workplace communication tools', 391 | confidenceScore: 0.85, 392 | domainContext: 'workplace communication' 393 | }, 394 | 395 | 'notify the team': { 396 | targetOperations: ['slack:send_message', 'discord:send_message', 'teams:send_message', 'gmail:send_email'], 397 | resolutionRationale: 'Team notifications require communication channels', 398 | confidenceScore: 0.8, 399 | domainContext: 'team coordination and alerts' 400 | }, 401 | 402 | // Cloud operations semantic resolutions 403 | 'list my EC2 instances': { 404 | targetOperations: ['aws:list_ec2_instances', 'aws:describe_instances', 'shell:run_command'], 405 | resolutionRationale: 'EC2 instance listing requires AWS cloud operations', 406 | confidenceScore: 0.95, 407 | domainContext: 'AWS cloud infrastructure management' 408 | }, 409 | 410 | 'list my S3 buckets': { 411 | targetOperations: ['aws:list_s3_buckets', 'aws:list_buckets', 'shell:run_command'], 412 | resolutionRationale: 'S3 bucket listing requires AWS storage operations', 413 | confidenceScore: 0.95, 414 | domainContext: 'AWS cloud storage management' 415 | }, 416 | 417 | 'show my cloud resources': { 418 | targetOperations: ['aws:list_ec2_instances', 'aws:list_s3_buckets', 'azure:list_resources', 'gcp:list_instances'], 419 | resolutionRationale: 'Cloud resource viewing requires cloud platform API operations', 420 | confidenceScore: 0.85, 421 | domainContext: 'multi-cloud infrastructure management' 422 | }, 423 | 424 | // Financial operations semantic resolutions 425 | 'process a customer payment': { 426 | targetOperations: ['stripe:create_charge', 'stripe:create_payment_intent', 'stripe:process_payment'], 427 | resolutionRationale: 'Customer payment processing requires payment gateway operations', 428 | confidenceScore: 0.9, 429 | domainContext: 'e-commerce payment processing' 430 | }, 431 | 432 | 'charge a customer': { 433 | targetOperations: ['stripe:create_charge', 'stripe:create_payment_intent'], 434 | resolutionRationale: 'Customer charging requires payment processing operations', 435 | confidenceScore: 0.9, 436 | domainContext: 'payment and billing' 437 | }, 438 | 439 | 'collect payment': { 440 | targetOperations: ['stripe:create_charge', 'stripe:create_payment_intent', 'stripe:create_invoice'], 441 | resolutionRationale: 'Payment collection requires financial transaction operations', 442 | confidenceScore: 0.85, 443 | domainContext: 'revenue collection and billing' 444 | }, 445 | 446 | // Calendar operations semantic resolutions 447 | 'schedule a team meeting': { 448 | targetOperations: ['calendar:create_event', 'calendar:schedule_meeting', 'gmail:create_event'], 449 | resolutionRationale: 'Meeting scheduling requires calendar management operations', 450 | confidenceScore: 0.9, 451 | domainContext: 'team coordination and scheduling' 452 | }, 453 | 454 | 'book a meeting': { 455 | targetOperations: ['calendar:create_event', 'calendar:schedule_meeting'], 456 | resolutionRationale: 'Meeting booking requires calendar scheduling operations', 457 | confidenceScore: 0.85, 458 | domainContext: 'appointment and meeting management' 459 | }, 460 | 461 | 'create a calendar event': { 462 | targetOperations: ['calendar:create_event', 'gmail:create_event'], 463 | resolutionRationale: 'Event creation requires calendar management operations', 464 | confidenceScore: 0.95, 465 | domainContext: 'schedule and event management' 466 | }, 467 | 468 | // Productivity tools semantic resolutions 469 | 'update the quarterly report spreadsheet': { 470 | targetOperations: ['google-sheets:update_sheet', 'google-sheets:write_sheet', 'google-sheets:update_cells'], 471 | resolutionRationale: 'Spreadsheet updating requires sheet manipulation operations', 472 | confidenceScore: 0.9, 473 | domainContext: 'business reporting and data management' 474 | }, 475 | 476 | 'update spreadsheet': { 477 | targetOperations: ['google-sheets:update_sheet', 'google-sheets:write_sheet'], 478 | resolutionRationale: 'Spreadsheet updates require sheet data operations', 479 | confidenceScore: 0.85, 480 | domainContext: 'data management and analysis' 481 | }, 482 | 483 | 'edit the report': { 484 | targetOperations: ['google-sheets:update_sheet', 'notion:update_page', 'gdrive:update_file'], 485 | resolutionRationale: 'Report editing requires document manipulation operations', 486 | confidenceScore: 0.8, 487 | domainContext: 'document and report management' 488 | } 489 | }; 490 | 491 | /** 492 | * Apply semantic enhancement to a query-tool pair 493 | * 494 | * PROCESS: 495 | * 1. Capability Inference: Check if tool type has implicit capabilities matching query 496 | * 2. Intent Resolution: Check if query maps to specific operations this tool provides 497 | * 3. Combine enhancements with confidence weighting 498 | * 4. Apply anti-pattern prevention (confidence capping) 499 | * 500 | * @param userQuery Natural language user query 501 | * @param toolIdentifier Tool identifier (format: "mcp:tool" or just tool name) 502 | * @param toolDescription Tool description for context 503 | * @returns Array of semantic enhancements to apply 504 | */ 505 | applySemanticalEnhancement( 506 | userQuery: string, 507 | toolIdentifier: string, 508 | toolDescription: string 509 | ): SemanticEnhancement[] { 510 | 511 | const enhancements: SemanticEnhancement[] = []; 512 | const queryLower = userQuery.toLowerCase(); 513 | const [mcpName, toolName] = toolIdentifier.split(':'); 514 | 515 | // 1. CAPABILITY INFERENCE: Check domain knowledge for this tool type 516 | const inferenceRule = this.capabilityInferenceRules[mcpName] || 517 | (toolName ? this.capabilityInferenceRules[toolName.toLowerCase()] : undefined); 518 | 519 | if (inferenceRule) { 520 | // Check if query relates to any implicit domain capabilities 521 | for (const implicitDomain of inferenceRule.implicitDomains) { 522 | const domainKeywords = implicitDomain.toLowerCase().split(/[\s,()]+/); 523 | const matchingKeywords = domainKeywords.filter(keyword => 524 | keyword.length > 3 && queryLower.includes(keyword) 525 | ); 526 | 527 | if (matchingKeywords.length > 0) { 528 | const domainRelevance = matchingKeywords.length / domainKeywords.length; 529 | const enhancementBoost = 0.1 * domainRelevance * inferenceRule.confidenceScore; 530 | 531 | enhancements.push({ 532 | enhancementType: 'capability_inference', 533 | relevanceBoost: enhancementBoost, 534 | enhancementReason: `${mcpName} has implicit capability: ${implicitDomain}`, 535 | confidenceLevel: inferenceRule.confidenceScore 536 | }); 537 | 538 | logger.debug(`Capability inference match: ${implicitDomain} for ${toolIdentifier} (relevance: ${domainRelevance})`); 539 | } 540 | } 541 | } 542 | 543 | // 2. INTENT RESOLUTION: Check semantic mappings for user expressions 544 | for (const [semanticPattern, resolutionRule] of Object.entries(this.semanticResolutionRules)) { 545 | 546 | if (this.matchesSemanticPattern(queryLower, semanticPattern)) { 547 | // Check if this tool is a target for this semantic resolution 548 | const isTargetOperation = resolutionRule.targetOperations.some(targetOp => 549 | toolIdentifier === targetOp || 550 | toolIdentifier.includes(targetOp.split(':')[1]) || 551 | targetOp.includes(mcpName) 552 | ); 553 | 554 | if (isTargetOperation) { 555 | const enhancementBoost = 0.15 * resolutionRule.confidenceScore; 556 | 557 | enhancements.push({ 558 | enhancementType: 'intent_resolution', 559 | relevanceBoost: enhancementBoost, 560 | enhancementReason: resolutionRule.resolutionRationale, 561 | confidenceLevel: resolutionRule.confidenceScore 562 | }); 563 | 564 | logger.debug(`Intent resolution match: "${semanticPattern}" → ${toolIdentifier}`); 565 | } 566 | } 567 | } 568 | 569 | // 3. ANTI-PATTERN PREVENTION: Cap total enhancement to prevent over-boosting 570 | const totalBoost = enhancements.reduce((sum, e) => sum + e.relevanceBoost, 0); 571 | const MAX_ENHANCEMENT_BOOST = 0.25; 572 | 573 | if (totalBoost > MAX_ENHANCEMENT_BOOST) { 574 | const scalingFactor = MAX_ENHANCEMENT_BOOST / totalBoost; 575 | enhancements.forEach(enhancement => { 576 | enhancement.relevanceBoost *= scalingFactor; 577 | }); 578 | 579 | logger.debug(`Applied enhancement capping for ${toolIdentifier} (scaling factor: ${scalingFactor})`); 580 | } 581 | 582 | return enhancements; 583 | } 584 | 585 | /** 586 | * Check if user query matches a semantic pattern 587 | * Uses improved fuzzy keyword matching with flexible thresholds 588 | */ 589 | private matchesSemanticPattern(userQuery: string, semanticPattern: string): boolean { 590 | const patternKeywords = semanticPattern.toLowerCase().split(/\s+/); 591 | const queryWords = userQuery.toLowerCase().split(/\s+/); 592 | 593 | // Enhanced matching: exact matches, partial matches, and synonyms 594 | const matchingKeywords = patternKeywords.filter(keyword => { 595 | // Direct inclusion check 596 | if (userQuery.includes(keyword)) return true; 597 | 598 | // Check if any query word contains or is contained in the pattern keyword 599 | return queryWords.some(queryWord => 600 | queryWord.includes(keyword) || keyword.includes(queryWord) 601 | ); 602 | }); 603 | 604 | // Dynamic threshold based on pattern length 605 | let matchThreshold; 606 | if (patternKeywords.length <= 2) { 607 | // For short patterns, require all keywords to match 608 | matchThreshold = patternKeywords.length; 609 | } else if (patternKeywords.length <= 4) { 610 | // For medium patterns, require 60% match 611 | matchThreshold = Math.ceil(patternKeywords.length * 0.6); 612 | } else { 613 | // For long patterns, require 50% match 614 | matchThreshold = Math.ceil(patternKeywords.length * 0.5); 615 | } 616 | 617 | return matchingKeywords.length >= matchThreshold; 618 | } 619 | 620 | /** 621 | * Add new capability inference rule (for dynamic expansion) 622 | */ 623 | addCapabilityInferenceRule(toolType: string, rule: CapabilityInferenceRule): void { 624 | if (this.capabilityInferenceRules[toolType]) { 625 | logger.warn(`Overwriting existing capability inference rule: ${toolType}`); 626 | } 627 | this.capabilityInferenceRules[toolType] = rule; 628 | logger.info(`Added capability inference rule: ${toolType} with ${rule.implicitDomains.length} domains`); 629 | } 630 | 631 | /** 632 | * Add new semantic resolution rule (for dynamic expansion) 633 | */ 634 | addSemanticResolutionRule(semanticPattern: string, rule: SemanticResolutionRule): void { 635 | if (this.semanticResolutionRules[semanticPattern]) { 636 | logger.warn(`Overwriting existing semantic resolution rule: ${semanticPattern}`); 637 | } 638 | this.semanticResolutionRules[semanticPattern] = rule; 639 | logger.info(`Added semantic resolution rule: "${semanticPattern}" → ${rule.targetOperations.join(', ')}`); 640 | } 641 | 642 | /** 643 | * Get enhancement engine statistics 644 | */ 645 | getEnhancementStatistics() { 646 | const totalImplicitDomains = Object.values(this.capabilityInferenceRules) 647 | .reduce((sum, rule) => sum + rule.implicitDomains.length, 0); 648 | 649 | const totalTargetOperations = Object.values(this.semanticResolutionRules) 650 | .reduce((sum, rule) => sum + rule.targetOperations.length, 0); 651 | 652 | return { 653 | capabilityInferenceRules: Object.keys(this.capabilityInferenceRules).length, 654 | semanticResolutionRules: Object.keys(this.semanticResolutionRules).length, 655 | totalImplicitDomains, 656 | totalTargetOperations, 657 | averageConfidence: { 658 | capabilityInference: Object.values(this.capabilityInferenceRules) 659 | .reduce((sum, rule) => sum + rule.confidenceScore, 0) / Object.keys(this.capabilityInferenceRules).length, 660 | intentResolution: Object.values(this.semanticResolutionRules) 661 | .reduce((sum, rule) => sum + rule.confidenceScore, 0) / Object.keys(this.semanticResolutionRules).length 662 | } 663 | }; 664 | } 665 | } ``` -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- ```markdown 1 | # Changelog 2 | 3 | ## [1.5.3](https://github.com/portel-dev/ncp/compare/1.5.2...1.5.3) (2025-10-14) 4 | 5 | ### Bug Fixes 6 | 7 | * ensure MCP protocol compliance with immediate stdio listener ([43a71c7](https://github.com/portel-dev/ncp/commit/43a71c7a290cb22b256f07eae95e501c8818fdb7)) 8 | 9 | ## [1.5.2](https://github.com/portel-dev/ncp/compare/1.5.1...1.5.2) (2025-10-12) 10 | 11 | ### Bug Fixes 12 | 13 | * resolve script symlink to find actual installation directory ([ad7f3c8](https://github.com/portel-dev/ncp/commit/ad7f3c894ec60b92817473ce418e58f90f1221e3)) 14 | 15 | ## [1.5.1](https://github.com/portel-dev/ncp/compare/1.5.0...1.5.1) (2025-10-12) 16 | 17 | ### Bug Fixes 18 | 19 | * enhance version utility to prefer global package version and improve local fallback logic in tests ([8765c69](https://github.com/portel-dev/ncp/commit/8765c6964c0166251222ec58da91a4bd7dc88d96)) 20 | * look for package.json after resolving the symlinks if found and update version in server.json ([0f0c9c8](https://github.com/portel-dev/ncp/commit/0f0c9c8e49cf9a7a6a721b44248fe7f88aafe5bd)) 21 | 22 | ## [1.5.0](https://github.com/portel-dev/ncp/compare/1.4.3...1.5.0) (2025-10-11) 23 | 24 | ### Features 25 | 26 | * add installation metadata to server.json ([fe0e25b](https://github.com/portel-dev/ncp/commit/fe0e25b7e15003f3ab3c92664784f8cf7ca3221e)) 27 | * enhance MockServerManager with improved timeout management and error handling ([edb0fb4](https://github.com/portel-dev/ncp/commit/edb0fb4bff8ea67b8c1486a36d52fc4be9204864)) 28 | * enhance MockServerManager with robust server startup and error handling; add Git mock server implementation ([3488512](https://github.com/portel-dev/ncp/commit/3488512af0fdd75efaf61bafe6a3ce43e09836ff)) 29 | * enhance test configurations with improved Jest settings and mock server management ([ad7e893](https://github.com/portel-dev/ncp/commit/ad7e893a713b39987ca09ae22c842bde0ec113b9)) 30 | * implement MockServerManager to manage mock MCP server processes for tests ([89d5b38](https://github.com/portel-dev/ncp/commit/89d5b383472fbeb8ef749dc84fab4eb3233f9de5)) 31 | * improve timeout handling in MCPServer and MCPHealthMonitor; enhance find command test assertions ([d089f6b](https://github.com/portel-dev/ncp/commit/d089f6b7bddbfdd6010121e1aed058912ebfdd6a)) 32 | * update .npmignore and package.json to include TypeScript support and specify files for packaging ([ae9bbcf](https://github.com/portel-dev/ncp/commit/ae9bbcf94ba6c212c7eec1ef41485e665d474f9a)) 33 | 34 | ### Bug Fixes 35 | 36 | * correct testMatch pattern to include both .js and .ts files ([dec5625](https://github.com/portel-dev/ncp/commit/dec56254a8cde1240dc56b26fe11d980f72252cc)) 37 | 38 | ## [1.4.1](https://github.com/portel-dev/ncp/compare/1.4.0...1.4.1) (2025-10-03) 39 | 40 | ### Bug Fixes 41 | 42 | * mock createWriteStream in orchestrator tests ([bd6ea80](https://github.com/portel-dev/ncp/commit/bd6ea80c02435932702cdaa12390e159dc5a845a)) 43 | 44 | ## [1.4.0](https://github.com/portel-dev/ncp/compare/1.3.2...1.4.0) (2025-10-03) 45 | 46 | ### Features 47 | 48 | * add --working-dir parameter and fix GitHub releases ([0a1e4db](https://github.com/portel-dev/ncp/commit/0a1e4db9f83ac6ba67ac85db519b0bdfdc79a89a)) 49 | * add automated MCP registry publishing ([56f410c](https://github.com/portel-dev/ncp/commit/56f410c56e1657def7a617fa5af4bd9f0751bd18)) 50 | * add GitHub Actions workflow for publishing releases ([a7eab05](https://github.com/portel-dev/ncp/commit/a7eab05d65246660f451c3d2152e19ac9ba10d3d)) 51 | * add graceful shutdown on Ctrl+C for clean cache finalization ([b37d6d2](https://github.com/portel-dev/ncp/commit/b37d6d26dc4f9ff401dd149e7d4a0740e5cfc7b1)) 52 | * add repair command skeleton and improve error logging ([303ce05](https://github.com/portel-dev/ncp/commit/303ce05c44927b252b66b67e3d97a8bc1c9d7402)) 53 | * add repair command with generic error parser ([ef89a62](https://github.com/portel-dev/ncp/commit/ef89a62d9f85f18a4e345f8af2a6cc52cb1a1580)) 54 | * add session ID transparency for stateful MCP servers ([af72da9](https://github.com/portel-dev/ncp/commit/af72da964aba3b16b2fa5a4c51ffc9f8aba4faa8)) 55 | * add Smithery config detection to ncp repair command ([2ff58da](https://github.com/portel-dev/ncp/commit/2ff58da91083eea8c608d804937f42f5f9828883)) 56 | * add Smithery config schema detection (3-tier system) ([ef008e8](https://github.com/portel-dev/ncp/commit/ef008e832dcb5bff37598857a3890deaf841693e)) 57 | * enhance error parser to detect file-not-found patterns ([d9ca05b](https://github.com/portel-dev/ncp/commit/d9ca05b1acb7d5f9d0cdb7c665be8c1f6fdef372)) 58 | * implement CSV-based incremental caching for resumable indexing ([064e347](https://github.com/portel-dev/ncp/commit/064e34710392b74643d6b4f3b8cf370f30e5d1c1)), closes [#30](https://github.com/portel-dev/ncp/issues/30) 59 | * integrate health monitor with repair command ([48ce0d1](https://github.com/portel-dev/ncp/commit/48ce0d1f894806484209c8a976fd92fd12cc61d0)) 60 | * intelligent failed MCP tracking with scheduled retry and --force-retry flag ([91c7a65](https://github.com/portel-dev/ncp/commit/91c7a65076d4e838f8819606737f919b7e4a176b)) 61 | * retry slow MCPs with longer timeout to capture healthy-but-slow servers ([844e347](https://github.com/portel-dev/ncp/commit/844e347be969f02cdd5ba2d462df7c48aa561ceb)) 62 | 63 | ### Bug Fixes 64 | 65 | * add 3s timeout to update checker to prevent CLI hangs ([05210d8](https://github.com/portel-dev/ncp/commit/05210d8d27c6cbb9a7df6b2290f5eb6b30497068)) 66 | * add fallback authentication for MCP registry publishing ([f138b37](https://github.com/portel-dev/ncp/commit/f138b37259d251564d9fbcfaa4d9a1381271e33e)) 67 | * add newline before progress spinner to avoid overwriting command ([676f0a5](https://github.com/portel-dev/ncp/commit/676f0a5bb0f208ef127357bfd64e6ded36f9fe83)) 68 | * add proper blank line spacing before progress spinner ([8eea9aa](https://github.com/portel-dev/ncp/commit/8eea9aa380ed3611769f29b112a40fc2c69958e9)) 69 | * add repair command to CLI mode check to prevent hanging ([3c2aa60](https://github.com/portel-dev/ncp/commit/3c2aa60be3b7df6c6a9cbeb8845d4aad1b1e1cd8)) 70 | * also fsync metadata file to ensure cache progress is saved ([e47a180](https://github.com/portel-dev/ncp/commit/e47a180399f599d90401bd73060853e22574527f)) 71 | * clarify progress messages for cache resume ([51f0e90](https://github.com/portel-dev/ncp/commit/51f0e900450431a7a22e38bd8b7fc82a4717128c)) 72 | * CLI find command now waits for indexing to complete ([8f2e671](https://github.com/portel-dev/ncp/commit/8f2e671af4ea9f2277bfa22c97d9337402cec68d)) 73 | * correct cached MCP count in first progress message ([925e819](https://github.com/portel-dev/ncp/commit/925e819b67ef18188aeb8a38997acd9073feb1e0)) 74 | * correct version reading in update checker and updater ([5f7c737](https://github.com/portel-dev/ncp/commit/5f7c7376b657b3a9b8eecb684e433ed07f021db5)) 75 | * correctly calculate starting position excluding failed MCPs being retried ([0706653](https://github.com/portel-dev/ncp/commit/0706653dbde56e8c9eb2f0b189d1c529098022cf)) 76 | * detect multiple arguments in Usage message for clone/copy MCPs ([579b27c](https://github.com/portel-dev/ncp/commit/579b27cace62fea90586b370d23333d00033bfed)) 77 | * disable indexing progress in run command for clean CLI output ([7cde964](https://github.com/portel-dev/ncp/commit/7cde96454d76b632e5303262c148401275694b89)) 78 | * display correct progress message and add newline in CLI ([0dcd686](https://github.com/portel-dev/ncp/commit/0dcd686e0ba077c8f78cb5fe923f5feb6d45171b)) 79 | * force flush CSV cache to disk after each MCP for crash safety ([e7f7649](https://github.com/portel-dev/ncp/commit/e7f7649cf3d89a1052e62b032871f4cdca80c8e8)) 80 | * preserve newline before spinner by skipping clearLines on first render ([33b85a3](https://github.com/portel-dev/ncp/commit/33b85a30ee2a7f9257e9eccf8d585e21fd49a6ae)) 81 | * prevent duplicate API key/token detection in env var parser ([246b3d1](https://github.com/portel-dev/ncp/commit/246b3d1b2d3284ec2adcb43096a47aed15bf126a)) 82 | * prevent duplicate path detection in error parser ([f017606](https://github.com/portel-dev/ncp/commit/f017606fa5012d67baf6eda67626741daaf944ee)) 83 | * read version from package.json instead of hardcoding ([c2a0e46](https://github.com/portel-dev/ncp/commit/c2a0e4693c004a402a2e1c5501c69473a87671cc)) 84 | * remove deprecated string-similarity and correct version issues ([486a866](https://github.com/portel-dev/ncp/commit/486a866fb0c027f5dec09bd97450cd4d9631b9e3)) 85 | * restore and correct server.json for MCP registry ([60e16e0](https://github.com/portel-dev/ncp/commit/60e16e07e3b2a597886010f897ea99e667096e35)) 86 | * show actual MCP count loaded from CSV, not stale metadata count ([a2a4c62](https://github.com/portel-dev/ncp/commit/a2a4c62645568a5d4670883fd9e0c8bb2e6e82f8)) 87 | * show cached count as starting position, not 0 ([3f0d2bd](https://github.com/portel-dev/ncp/commit/3f0d2bd204baf9eef7bc443fc1938ab4f08373f4)) 88 | * show total processed count (cached+failed) as starting position ([c3e52ca](https://github.com/portel-dev/ncp/commit/c3e52cacdd95285938b5c5487051f5bf66c2788f)) 89 | * simplify progress messages to show absolute position only ([43007d9](https://github.com/portel-dev/ncp/commit/43007d9411ddc80bdab25a902e3e5f21888bbd99)) 90 | * update progress counter for failed/skipped MCPs too ([9cbf744](https://github.com/portel-dev/ncp/commit/9cbf744606bb65e161da60510e72dd140065d303)) 91 | * update progress only AFTER MCP is appended to cache ([1db3c73](https://github.com/portel-dev/ncp/commit/1db3c73e7690e0324091a0c1c6cb60e8232c96a9)) 92 | * wait for write stream to finish in finalize() ([0174a87](https://github.com/portel-dev/ncp/commit/0174a872c35000341cbe415b4a0d06acde647628)) 93 | 94 | ## [1.3.1](https://github.com/portel-dev/ncp/compare/v1.3.0...1.3.1) (2025-09-27) 95 | 96 | ### Bug Fixes 97 | 98 | * make protocol tests more flexible for hotfix release ([165274b](https://github.com/portel-dev/ncp/commit/165274b7fb1eb49e7ee8ba1193d2a4587a3b858b)) 99 | * replace import.meta with process.cwd for Jest compatibility ([db51c3c](https://github.com/portel-dev/ncp/commit/db51c3cdc7bb67e3ae85cafbfd4fd93aa9cf73ac)) 100 | * resolve critical MCP server blocking during indexing ([e24b733](https://github.com/portel-dev/ncp/commit/e24b733ba254830564a45ba42c17723a09adb39e)) 101 | * skip problematic test for hotfix release ([7ff1079](https://github.com/portel-dev/ncp/commit/7ff1079d8e0390d0e23fe40187e8f95573b56481)) 102 | * update checker changes ([a744e42](https://github.com/portel-dev/ncp/commit/a744e4216c779e192539ffe211c3c3176f85a2c0)) 103 | 104 | ## [1.2.1](https://github.com/portel-dev/ncp/compare/1.1.0...1.2.1) (2025-09-25) 105 | 106 | ### Bug Fixes 107 | 108 | * configure proper semantic versioning based on conventional commits ([2a6feda](https://github.com/portel-dev/ncp/commit/2a6fedaa78d09fdf091d8e59c69b6d86c8507ee2)) 109 | * critical packaging fixes for npm package integrity ([de5fa8f](https://github.com/portel-dev/ncp/commit/de5fa8f7eb100eb48d466dde27b5d3584e681607)) 110 | * remove unused production dependencies ([4e3ffb2](https://github.com/portel-dev/ncp/commit/4e3ffb248cd41ad0a5cdb56118b1fd22ff91c469)) 111 | * resolve discovery engine tool indexing and test issues ([7dbc42f](https://github.com/portel-dev/ncp/commit/7dbc42fabd9c9c29ccc887842a09d80898d58cd1)) 112 | 113 | ## [1.2.0](https://github.com/portel-dev/ncp/compare/1.1.0...1.2.0) (2025-09-25) 114 | 115 | ### Bug Fixes 116 | 117 | * critical packaging fixes for npm package integrity ([de5fa8f](https://github.com/portel-dev/ncp/commit/de5fa8f7eb100eb48d466dde27b5d3584e681607)) 118 | 119 | ## 1.1.0 (2025-09-25) 120 | 121 | ### Features 122 | 123 | * Achieve 80.5% user story discovery pass rate ([b22aa41](https://github.com/portel-dev/ncp/commit/b22aa41e23eae692895ad898d2a563fca0c93c05)), closes [#4](https://github.com/portel-dev/ncp/issues/4) [-#10](https://github.com/portel-dev/-/issues/10) 124 | * add \"Did You Mean?\" fuzzy matching for tool suggestions ([820fe96](https://github.com/portel-dev/ncp/commit/820fe96d6a1a598fc42561bb09a8a5407034eaf2)) 125 | * add beautiful JSON syntax highlighting for tool results ([2604496](https://github.com/portel-dev/ncp/commit/2604496f7b4439d4d2d1d343b3228ade9e41658e)) 126 | * Add comprehensive HOW-IT-WORKS.md technical guide ([951eb72](https://github.com/portel-dev/ncp/commit/951eb72b4421026752a3d659a9c67d50df7bb92c)) 127 | * add comprehensive MCP content type support and user output control ([c5dbe02](https://github.com/portel-dev/ncp/commit/c5dbe02601d6b58450c71a019bc91984b132d5b9)) 128 | * Add comprehensive MCP interface testing strategy ([179ce95](https://github.com/portel-dev/ncp/commit/179ce95b49dd5aac14a5673b06bb58b6df3b5011)) 129 | * add contextual run command examples in find tips ([1877b84](https://github.com/portel-dev/ncp/commit/1877b8409a5abd58d8f2f97dadcc0307735bf432)) 130 | * add intelligent markdown rendering for tool responses ([208aac5](https://github.com/portel-dev/ncp/commit/208aac59e4be871be2d3f3bb6ab60820943c237f)) 131 | * add MCP health status indicators to find results ([5b5fc96](https://github.com/portel-dev/ncp/commit/5b5fc96f64b40d5aeb4d258c315b4a013b3bd521)) 132 | * add media auto-opening and enhanced run command help ([c9a1a43](https://github.com/portel-dev/ncp/commit/c9a1a4362741f6442ca0b4776a69b775a08bd7f1)) 133 | * add OutputFormatter service for consistent UX ([7df2f68](https://github.com/portel-dev/ncp/commit/7df2f68c2b9a18b2f7ba1d3bd44825fd48d86a93)) 134 | * add parameter validation before tool execution ([5dc91b6](https://github.com/portel-dev/ncp/commit/5dc91b61d4d9067692a21e19057ac49e6cef82b3)) 135 | * Add SearchEnhancer for clean, extensible search optimization ([1936813](https://github.com/portel-dev/ncp/commit/1936813ab365e674d6174d77e4c3cf81aa1969a5)) 136 | * Add semantic mapping for 'save' → 'edit' actions ([d2b3cd3](https://github.com/portel-dev/ncp/commit/d2b3cd364d3163fd69850b404050aa19928a0189)) 137 | * Add terminal window frame scripts for professional screenshots ([1815498](https://github.com/portel-dev/ncp/commit/18154988cb4c6b8aa23ef0229260a3507c352f97)) 138 | * auto-handle empty parameters for tools without required params ([6b23a23](https://github.com/portel-dev/ncp/commit/6b23a232e7a5ae276e102a7be2f01834a7cae3f2)) 139 | * Complete intent-aware search system with comprehensive testing ([49b8c40](https://github.com/portel-dev/ncp/commit/49b8c40a87b11be6e85773d0b70d10f2c7d15848)) 140 | * Comprehensive user story discovery tests with curated MCP profile ([f3f6a4d](https://github.com/portel-dev/ncp/commit/f3f6a4dc6481d8abff71ab88b1040748c102aa27)) 141 | * create standalone Smithery MCP server entry point ([f27f5b3](https://github.com/portel-dev/ncp/commit/f27f5b3006a2a95fc9ab5f46edc9a0a0e886f13f)) 142 | * enhance confidence threshold documentation and CLI support ([4206557](https://github.com/portel-dev/ncp/commit/4206557c814f30d381e36a09662a4183d592b737)) 143 | * Enhance config import with rich display and security improvements ([45f76e3](https://github.com/portel-dev/ncp/commit/45f76e30e4e129af0be40c60ed1c8ca95b415b5f)) 144 | * enhance error messages for invalid MCPs and tools ([cb2ebe3](https://github.com/portel-dev/ncp/commit/cb2ebe36d3036cf4f69b2cea9883cc3bdf746f2f)) 145 | * enhance generic error messages with contextual guidance ([bbaeeea](https://github.com/portel-dev/ncp/commit/bbaeeeae574775faa02b555e79f6adf7c7dcfdb0)) 146 | * enhance MCP tool descriptions to document full intelligent capabilities ([05a77ff](https://github.com/portel-dev/ncp/commit/05a77ff03a2e2ae81b7609c1bcabc48400e5ac31)) 147 | * Enhance search ranking with action word weighting ([ead58af](https://github.com/portel-dev/ncp/commit/ead58afef16d225e66bf02d32f4216da0f2eb8a4)) 148 | * Enhanced error handling with vector search suggestions ([f65bcf7](https://github.com/portel-dev/ncp/commit/f65bcf7ad2bcd4801fce05ced874edf949cff05e)) 149 | * Expand MCP ecosystem to 1069 MCPs with enhanced semantic engine ([36b5c8c](https://github.com/portel-dev/ncp/commit/36b5c8c8600912e5f7ce1cde52ce313507eaeaeb)) 150 | * implement bidirectional domain-to-capability mapping for intelligent tool discovery ([7dc0992](https://github.com/portel-dev/ncp/commit/7dc09928ff763ee8262934a619a4bed0ec7b2fae)) 151 | * implement intelligent parameter prediction system ([bb23d0d](https://github.com/portel-dev/ncp/commit/bb23d0de55ba3643bbc0cc242888cb7187518338)) 152 | * implement interactive parameter prompting system ([3f233a3](https://github.com/portel-dev/ncp/commit/3f233a3044cd59745e4d0b9d1014a8079c934c75)) 153 | * implement project-level .ncp configuration ([865494e](https://github.com/portel-dev/ncp/commit/865494e6a6932efe769b6f01b20ae4346d2251f5)) 154 | * implement smart text extraction for tool responses ([e8ac67c](https://github.com/portel-dev/ncp/commit/e8ac67c8c2dd1b78a9539d7f0b85299f9d341a73)) 155 | * Improve list command navigation title ([564a5d1](https://github.com/portel-dev/ncp/commit/564a5d10f03ac1eaeef8a3fe955733883c6edf8e)) 156 | * Integrate health monitoring for real-time MCP error reporting ([8f07247](https://github.com/portel-dev/ncp/commit/8f07247dad870f0bf54a24b07a8b1d16e6e383b4)) 157 | * Natural Context Provider v1.0.3 - N-to-1 MCP orchestration for AI assistants ([93a3f8f](https://github.com/portel-dev/ncp/commit/93a3f8f59de59e8d28054b5c1739493ffc1166a0)) 158 | * Natural Context Provider v1.0.4 - Enhanced Documentation & Production Ready ([7e4617b](https://github.com/portel-dev/ncp/commit/7e4617b8eb37c55afb7c97acee7965b9f590b593)) 159 | * optimize find command with dual-mode operation and improved depth levels ([9bb0630](https://github.com/portel-dev/ncp/commit/9bb0630c154d0f745b73a29e2ba3654440510a42)) 160 | * Optimize ncp list performance and enhance security display ([90bec0d](https://github.com/portel-dev/ncp/commit/90bec0d6debd101f1a74f4d0a4865acad18f8922)) 161 | * Pain-point driven README and prominent import feature in CLI help ([b9af32d](https://github.com/portel-dev/ncp/commit/b9af32d8a4a2156641764407521c6b2a5c730c42)) 162 | * Perfect CLI command enhancements for intelligent failure recovery ([0adb385](https://github.com/portel-dev/ncp/commit/0adb38533babce87c127856f284078dc40fda14d)) 163 | * Pivot to user story format for improved discovery ([505ba25](https://github.com/portel-dev/ncp/commit/505ba2582535f20286d74bd6b2ebeace3e29aa28)) 164 | * port comprehensive CLI interface from ncp-oss3 ([351bca2](https://github.com/portel-dev/ncp/commit/351bca26875c320714eb9e907c2320821b14d8b0)) 165 | * restore and enhance CLI help command polish ([c7a3843](https://github.com/portel-dev/ncp/commit/c7a384366229591fd56da1b14d093dbc754264ef)) 166 | * Simplify config import to clipboard-first with enhanced UX ([0f78d1c](https://github.com/portel-dev/ncp/commit/0f78d1cf6c8fdca9cef04c454e4bf59c4198a17b)) 167 | 168 | ### Bug Fixes 169 | 170 | * add missing version command and improve CLI argument detection ([4f98ece](https://github.com/portel-dev/ncp/commit/4f98ece602448e60a7511bd7c7baa486d8a6d699)) 171 | * Add newline separation after progress spinner in config import ([ab8ca8c](https://github.com/portel-dev/ncp/commit/ab8ca8c431abade617543da38ebbf22bfcbcbe31)) 172 | * Add newline separation between command and response in file import ([e954a11](https://github.com/portel-dev/ncp/commit/e954a110f6216a27d16d24d26e6761fac8506fb9)) 173 | * add semi-transparent background to infographic for better visibility ([d8e6ff5](https://github.com/portel-dev/ncp/commit/d8e6ff5da852385030603f8d745811d007e8fa8c)) 174 | * clean up dependencies for Smithery deployment ([edb759e](https://github.com/portel-dev/ncp/commit/edb759e2316e0d36271a76895a008b1c388b9b55)) 175 | * clean up npm package to exclude shell scripts and redundant files ([d2a2d4b](https://github.com/portel-dev/ncp/commit/d2a2d4b84976bc0603a4604e7ce6389ea14343cd)) 176 | * complete .gitignore for NCP generated files ([8284feb](https://github.com/portel-dev/ncp/commit/8284feb5ca0a22dc13be7d608889c29bbe560c56)) 177 | * Correct gitignore to keep docs/images while ignoring root images ([2e969d2](https://github.com/portel-dev/ncp/commit/2e969d2b7f6e5828c948406982ba8bc63ecad318)) 178 | * correct list command depth levels for MCP-focused display ([f52b69a](https://github.com/portel-dev/ncp/commit/f52b69a530e4f7859cf5d79aad4ce7fb6fd86c40)) 179 | * Default to MCP server mode when no CLI commands provided ([e41f93f](https://github.com/portel-dev/ncp/commit/e41f93f84855ee09063e8f743f4e5840970a63d5)) 180 | * detect and display errors in tool content properly ([b70ccf5](https://github.com/portel-dev/ncp/commit/b70ccf50a95b691c046bd53ff0b21d157a844448)) 181 | * Enable import from Claude Desktop config format ([3ef4325](https://github.com/portel-dev/ncp/commit/3ef4325c5c7abaa72b6fe072ac3a85ade89933eb)) 182 | * Ensure CLI/AI parity with confidence-based result ordering ([13cc0da](https://github.com/portel-dev/ncp/commit/13cc0da5d8d397c23f3670a1ca4752043bf7ca28)), closes [#5](https://github.com/portel-dev/ncp/issues/5) [#2](https://github.com/portel-dev/ncp/issues/2) 183 | * ensure TypeScript builds during Smithery deployment ([e2eb66f](https://github.com/portel-dev/ncp/commit/e2eb66f968861e5b17e4e51a3ef302d3cdab2fb8)) 184 | * exclude NCP generated files from repository ([4f2fb2d](https://github.com/portel-dev/ncp/commit/4f2fb2d078cbc3cc58fd0d929a9000f0d4f41fd1)) 185 | * exclude problematic files from TypeScript compilation ([4e17ef9](https://github.com/portel-dev/ncp/commit/4e17ef9a86f84f0f80b2fa17c50507a1d27a10fc)) 186 | * externalize native dependencies in Smithery build configuration ([5d8936c](https://github.com/portel-dev/ncp/commit/5d8936cf4db0a27635c7dc1c6f3ecfbc7863cc44)) 187 | * Handle file imports same as clipboard imports ([e10c0d3](https://github.com/portel-dev/ncp/commit/e10c0d34ce8f9602a8e20125b3e99423c4875d1e)) 188 | * improve find tool descriptions for better discoverability ([5316e4e](https://github.com/portel-dev/ncp/commit/5316e4ee9c61d5d5f2932cea181d40dd71e2ed16)) 189 | * improve parameter examples for tools with no required parameters ([7346df0](https://github.com/portel-dev/ncp/commit/7346df00d07ab33264c6f51b4be3d7d2b5a441e4)) 190 | * improve Smithery standalone entry point configuration ([4e83217](https://github.com/portel-dev/ncp/commit/4e832170cbc2fc614179e766ed9d40ac2437574e)) 191 | * improve usage tips accuracy and clarity ([bf11846](https://github.com/portel-dev/ncp/commit/bf11846d6184477ec94189abd4398715e4f5eca1)) 192 | * make find query optional to support listing mode ([08239b0](https://github.com/portel-dev/ncp/commit/08239b04fde4e965972bbdbffbce6174c9712eb9)) 193 | * move @xenova/transformers to devDependencies to resolve Smithery build ([e49befa](https://github.com/portel-dev/ncp/commit/e49befa64ec5d82ca3a6e705da5e70dd2e757827)) 194 | * preserve parameter schemas and clean up development files ([ce07500](https://github.com/portel-dev/ncp/commit/ce07500014d97721e4d43bc011d39b65e2b45c7d)) 195 | * preserve tool parameter schemas in discovery pipeline ([1666088](https://github.com/portel-dev/ncp/commit/166608827cbde970f94c2114688b3299d1a4c6e2)) 196 | * prevent double-prefixing of tool names in RAG indexing ([14919e7](https://github.com/portel-dev/ncp/commit/14919e7506881762e1094a50c6b5f2eca7512019)) 197 | * prevent empty parameter examples at low depth levels ([d311bb4](https://github.com/portel-dev/ncp/commit/d311bb4c89109378644636f245a1cfe6b66a495d)) 198 | * Proper spacing for validation spinner in config import ([8c1f6a3](https://github.com/portel-dev/ncp/commit/8c1f6a3eefcebc85fba5876dc4432b4f70f5847c)) 199 | * properly separate dev and production dependencies ([c627b5a](https://github.com/portel-dev/ncp/commit/c627b5a475c75cf32b1efb29b52c35c884706b11)) 200 | * regenerate package-lock.json and optimize npm packaging ([c2db7a1](https://github.com/portel-dev/ncp/commit/c2db7a13a565adc016a3caa2e5dbeec5f09a8b0c)) 201 | * remove misleading parameter examples when schema unavailable ([634af9b](https://github.com/portel-dev/ncp/commit/634af9bf7ddc2920ab8e61445125310ebf27c17a)) 202 | * remove scripts folder from repository and add to gitignore ([58545c7](https://github.com/portel-dev/ncp/commit/58545c708a0290e011fd3ce8530e18816f6e7ce9)) 203 | * resolve ES module compatibility and clean up development files ([30e634e](https://github.com/portel-dev/ncp/commit/30e634e917c3fbc3e07e5f703e350d33ff190366)) 204 | * resolve tool discovery double-prefix issue breaking search results ([be73b31](https://github.com/portel-dev/ncp/commit/be73b31e4f9c7fae9e76e007fa397d02f243ac68)) 205 | * restore proper profile-based tree structure for list command ([63ce807](https://github.com/portel-dev/ncp/commit/63ce807d5ef2f961fd77ab13ee3d31052d9543c1)) 206 | * Strategic NCP tool descriptions for AI clarity ([4669403](https://github.com/portel-dev/ncp/commit/46694032ae492997bd09cd6d5eadd07dabe9c0eb)) 207 | * suppress non-JSON-RPC console messages from MCP servers ([d91a18b](https://github.com/portel-dev/ncp/commit/d91a18be95f3726eb439ee78efcdd8651210c425)) 208 | * suppress verbose logger output in CLI mode ([5400e28](https://github.com/portel-dev/ncp/commit/5400e28537a2d9ba6cbe99d2f52bba44c2bb6409)) 209 | * switch to local-only distribution model for Smithery ([fdf9e98](https://github.com/portel-dev/ncp/commit/fdf9e985d24e296b5b57cd5e03baff92451f1484)) 210 | * update ncp transformation flow diagram ([8442a54](https://github.com/portel-dev/ncp/commit/8442a544bdde46fddf9eca218b7eac455959f8f8)) 211 | 212 | ### Reverts 213 | 214 | * remove content-based error detection ([e59a1c3](https://github.com/portel-dev/ncp/commit/e59a1c3b013d3ee1f23f476077ab4eeab9a71a06)) 215 | * Remove extra newline that created too much spacing ([d268e30](https://github.com/portel-dev/ncp/commit/d268e30db1eaa14c5d4cb677b17a36d733ef6f20)) 216 | * restore @xenova/transformers to dependencies - needed for core vector search ([1b7aa70](https://github.com/portel-dev/ncp/commit/1b7aa70f59aac8b880ac3017dbe65909f2c9521a)) 217 | 218 | All notable changes to this project will be documented in this file. 219 | 220 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 221 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 222 | 223 | ## [1.0.4] - 2025-09-23 224 | 225 | ### 🚀 Major Improvements 226 | - **Breakthrough: 80.5% user story discovery pass rate** (up from 17%) 227 | - **Validated user story approach** for semantic tool discovery at scale 228 | - **Optimized boosting algorithms** to prevent shell command over-dominance 229 | - **Enhanced tool descriptions** with strategic semantic keywords 230 | 231 | ### 🐛 Bug Fixes 232 | - Fixed double-prefix naming bug in tool ID generation 233 | - Corrected RAG engine tool indexing for proper MCP grouping 234 | - Resolved test isolation issues causing inconsistent results 235 | 236 | ### 🔧 Performance Optimizations 237 | - Reduced git boosting from +0.4 to +0.15 (62% reduction) 238 | - Reduced script execution boost from 8.0 to 2.0 (75% reduction) 239 | - Reduced shell commands boost from 4.0 to 1.5 (62% reduction) 240 | - Removed aggressive forced script execution returns 241 | - Optimized query limits for better semantic matching accuracy 242 | 243 | ### ✅ Validation 244 | - **33/41 user story tests passing** proving approach effectiveness 245 | - **378/389 total tests passing** (97.2% overall test health) 246 | - Comprehensive integration testing with real MCP configurations 247 | - Battle-tested semantic discovery across multiple domains 248 | 249 | ### 📝 Technical Details 250 | - Enhanced database, payment, memory, email, web, and image tool descriptions 251 | - Improved domain-specific semantic matching without over-generalization 252 | - Maintained precision while significantly improving recall 253 | - Proven scalability foundation for 1000+ MCP ecosystem 254 | 255 | This release establishes user stories as the proven approach for semantic tool discovery in MCP orchestration. 256 | 257 | ## [1.0.3] - 2025-09-17 258 | 259 | ### ✨ New Features 260 | - Added implement comprehensive orchestrator test coverage. 261 | - Added restore comprehensive tdd methodology with 85 passing tests. 262 | - Added implement comprehensive tdd test suite with clean api design. 263 | - Add comprehensive release process documentation with git commit strategy. 264 | - Added setup ai-enhanced release process with interactive changelog editing. 265 | - Added implement core ncp functionality. 266 | - Add cross-platform support and enhanced utilities. 267 | 268 | ### 🐛 Bug Fixes 269 | - Fixed set default profile to 'all' instead of 'default'. 270 | 271 | ### 🔧 Improvements 272 | - Improved clean repository to final release state. 273 | 274 | ### 📝 Other Changes 275 | - Incredible surge to 68.99% coverage (+16.49pp). 276 | - Major coverage breakthrough to 63.15% (+10.65pp). 277 | - Major utilities coverage breakthrough - achieve 60.12% overall. 278 | - Expand test coverage for discovery engine and orchestrator. 279 | - Analyze archived test suite and optimize current coverage. 280 | - Build: configure NPM package for publication. 281 | - Convert Mermaid diagrams to PNG for NPM compatibility. 282 | 283 | ## [1.0.2] - 2025-09-17 284 | 285 | ### ✨ New Features 286 | - Added implement core ncp functionality. 287 | - Add cross-platform support and enhanced utilities. 288 | 289 | ### 🐛 Bug Fixes 290 | - Fixed set default profile to 'all' instead of 'default'. 291 | 292 | ### 🔧 Improvements 293 | - Improved clean repository to final release state. 294 | 295 | ### 📝 Other Changes 296 | - Build: configure NPM package for publication. 297 | - Convert Mermaid diagrams to PNG for NPM compatibility. 298 | ``` -------------------------------------------------------------------------------- /test/user-story-discovery.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | /** 2 | * User Story Discovery Tests 3 | * Validates that user stories produce correct tools in top results 4 | */ 5 | 6 | import { DiscoveryEngine } from '../src/discovery/engine'; 7 | import { SearchEnhancer } from '../src/discovery/search-enhancer'; 8 | 9 | describe('User Story Tool Discovery', () => { 10 | let engine: DiscoveryEngine; 11 | 12 | beforeAll(async () => { 13 | engine = new DiscoveryEngine(); 14 | await engine.initialize(); 15 | 16 | // Clear any existing cached tools to ensure clean test environment 17 | await engine['ragEngine'].clearCache(); 18 | 19 | // Create a curated test profile with known tools 20 | // This gives us predictable, stable tests independent of actual MCP configurations 21 | const testTools = [ 22 | // File operations - comprehensive coverage 23 | { 24 | name: 'filesystem:read_file', 25 | description: 'Read the contents of a file from the file system. Opens files for viewing, extracting content, or processing.', 26 | mcpName: 'filesystem' 27 | }, 28 | { 29 | name: 'filesystem:write_file', 30 | description: 'Write or append content to a file. Save data, create new files, update existing files with new content.', 31 | mcpName: 'filesystem' 32 | }, 33 | { 34 | name: 'filesystem:delete_file', 35 | description: 'Delete a file from the file system. Remove unwanted files, clean up temporary files, free disk space.', 36 | mcpName: 'filesystem' 37 | }, 38 | { 39 | name: 'filesystem:list_directory', 40 | description: 'List files and directories in a given path. Browse folder contents, discover available files.', 41 | mcpName: 'filesystem' 42 | }, 43 | { 44 | name: 'filesystem:create_directory', 45 | description: 'Create a new directory. Make folders for organizing files, set up project structure.', 46 | mcpName: 'filesystem' 47 | }, 48 | { 49 | name: 'filesystem:move_file', 50 | description: 'Move or rename files. Reorganize file structure, change file names.', 51 | mcpName: 'filesystem' 52 | }, 53 | { 54 | name: 'filesystem:copy_file', 55 | description: 'Copy files to another location. Duplicate files, create backups, distribute files.', 56 | mcpName: 'filesystem' 57 | }, 58 | 59 | // Database operations 60 | { 61 | name: 'database:query', 62 | 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.', 63 | mcpName: 'database' 64 | }, 65 | { 66 | name: 'database:insert', 67 | description: 'Insert new records into database tables. Add data, create entries, store information.', 68 | mcpName: 'database' 69 | }, 70 | { 71 | name: 'database:update', 72 | description: 'Update existing records in database tables. Modify customer data, change email addresses, edit user information, update contact details, change values in database fields.', 73 | mcpName: 'database' 74 | }, 75 | { 76 | name: 'database:delete', 77 | description: 'Delete records from database tables. Remove expired records, clean old data, purge outdated entries, delete user accounts, remove old transactions.', 78 | mcpName: 'database' 79 | }, 80 | { 81 | name: 'database:create_table', 82 | 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.', 83 | mcpName: 'database' 84 | }, 85 | { 86 | name: 'database:migrate', 87 | description: 'Run database migrations. Update schema, evolve structure, apply changes.', 88 | mcpName: 'database' 89 | }, 90 | 91 | // Git/Version Control operations 92 | { 93 | name: 'git:create_branch', 94 | 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.', 95 | mcpName: 'git' 96 | }, 97 | { 98 | name: 'git:commit', 99 | description: 'Commit changes to git repository. Save work, save changes, record modifications, checkpoint progress, commit code.', 100 | mcpName: 'git' 101 | }, 102 | { 103 | name: 'git:push', 104 | description: 'Push commits to remote repository. Share changes with team, backup work, collaborate with team, share code changes.', 105 | mcpName: 'git' 106 | }, 107 | { 108 | name: 'git:pull', 109 | description: 'Pull latest changes from remote repository. Get updates, sync with team, fetch new code.', 110 | mcpName: 'git' 111 | }, 112 | { 113 | name: 'git:merge', 114 | description: 'Merge branches together. Combine features, integrate changes, unify code.', 115 | mcpName: 'git' 116 | }, 117 | { 118 | name: 'git:create_pull_request', 119 | description: 'Create pull request for code review. Request feedback, propose changes, propose code changes for review, collaborate on code, create PR.', 120 | mcpName: 'git' 121 | }, 122 | { 123 | name: 'git:clone', 124 | description: 'Clone a repository. Download code, get project copy, start development.', 125 | mcpName: 'git' 126 | }, 127 | 128 | // Memory/Storage operations 129 | { 130 | name: 'memory:store', 131 | description: 'Store information in persistent memory for later retrieval. Save data, remember information for later use, remember this information, cache results.', 132 | mcpName: 'memory' 133 | }, 134 | { 135 | name: 'memory:retrieve', 136 | description: 'Retrieve previously stored information from memory. Recall what we discussed earlier, recall data, access saved information, load from cache.', 137 | mcpName: 'memory' 138 | }, 139 | { 140 | name: 'memory:search', 141 | description: 'Search through stored memories for specific information. Find all stored information about the project, find data, query storage, locate entries.', 142 | mcpName: 'memory' 143 | }, 144 | { 145 | name: 'memory:delete', 146 | description: 'Delete stored memories. Clear cache, remove old data, free storage space.', 147 | mcpName: 'memory' 148 | }, 149 | { 150 | name: 'memory:list', 151 | description: 'List all stored memories. View saved data, browse cache, see what is remembered.', 152 | mcpName: 'memory' 153 | }, 154 | 155 | // Email/Communication 156 | { 157 | name: 'email:send', 158 | description: 'Send email messages. Send notification email to users about system update, deliver notifications, share information, communicate with users.', 159 | mcpName: 'email' 160 | }, 161 | { 162 | name: 'email:read', 163 | description: 'Read email messages from inbox. Check mail, view messages, process incoming communication.', 164 | mcpName: 'email' 165 | }, 166 | { 167 | name: 'email:search', 168 | description: 'Search for specific emails. Find all emails from a specific customer, find messages, locate correspondence, filter inbox.', 169 | mcpName: 'email' 170 | }, 171 | { 172 | name: 'email:delete', 173 | description: 'Delete email messages. Clean inbox, remove spam, manage storage.', 174 | mcpName: 'email' 175 | }, 176 | { 177 | name: 'email:forward', 178 | description: 'Forward emails to others. Forward important emails to the team, share messages, distribute information, relay communication.', 179 | mcpName: 'email' 180 | }, 181 | 182 | // Web/Search operations 183 | { 184 | name: 'web:search', 185 | description: 'Search the web for information. Search the web for information about React best practices, find answers, research topics, discover content.', 186 | mcpName: 'web' 187 | }, 188 | { 189 | name: 'web:scrape', 190 | description: 'Extract data from web pages. Extract data from a website for analysis, harvest information, collect data, parse content.', 191 | mcpName: 'web' 192 | }, 193 | { 194 | name: 'web:browse', 195 | description: 'Browse web pages. Navigate sites, explore content, visit URLs.', 196 | mcpName: 'web' 197 | }, 198 | 199 | // Shell/Terminal operations 200 | { 201 | name: 'shell:execute', 202 | description: 'Execute shell commands. Run programs, perform system operations, automate tasks.', 203 | mcpName: 'shell' 204 | }, 205 | { 206 | name: 'shell:script', 207 | description: 'Run shell scripts. Execute batch operations, automate workflows, process commands.', 208 | mcpName: 'shell' 209 | }, 210 | 211 | // Configuration management 212 | { 213 | name: 'config:read', 214 | description: 'Read configuration settings. Get preferences, load options, retrieve parameters.', 215 | mcpName: 'config' 216 | }, 217 | { 218 | name: 'config:write', 219 | description: 'Write configuration settings. Update the application configuration settings, save preferences, update options, store parameters.', 220 | mcpName: 'config' 221 | }, 222 | { 223 | name: 'config:validate', 224 | description: 'Validate configuration against schema. Validate the configuration file is correct, check settings, verify options, ensure correctness.', 225 | mcpName: 'config' 226 | }, 227 | 228 | // Log/Analysis operations 229 | { 230 | name: 'logs:analyze', 231 | description: 'Analyze log files for patterns, errors, and insights. Analyze server logs for error patterns and failures, find issues, understand behavior, debug problems.', 232 | mcpName: 'logs' 233 | }, 234 | { 235 | name: 'logs:search', 236 | description: 'Search through log files for specific events or errors. Find problems, locate issues, track events.', 237 | mcpName: 'logs' 238 | }, 239 | { 240 | name: 'logs:tail', 241 | description: 'Watch log files in real-time. Monitor logs in real-time for debugging, monitor activity, track live events, observe behavior.', 242 | mcpName: 'logs' 243 | }, 244 | 245 | // Image operations 246 | { 247 | name: 'image:resize', 248 | description: 'Resize images to specific dimensions. Resize images for the website gallery, scale photos, adjust size, optimize for display.', 249 | mcpName: 'image' 250 | }, 251 | { 252 | name: 'image:convert', 253 | description: 'Convert images between formats. Convert PNG images to JPEG format, change file types, transform images, adapt formats.', 254 | mcpName: 'image' 255 | }, 256 | { 257 | name: 'image:compress', 258 | description: 'Compress images to reduce file size. Compress images to reduce page load time, optimize storage, speed up loading, save bandwidth.', 259 | mcpName: 'image' 260 | }, 261 | 262 | // Payment/Financial operations (specialized domain) 263 | { 264 | name: 'payment:create', 265 | description: 'Create payment transactions. Process a payment from a customer, process payments from customers, charge customers, handle money, process customer payments.', 266 | mcpName: 'payment' 267 | }, 268 | { 269 | name: 'payment:refund', 270 | description: 'Refund payment transactions. Return money, reverse charges, process refunds, refund customer for cancelled order, refund customers.', 271 | mcpName: 'payment' 272 | }, 273 | { 274 | name: 'payment:list', 275 | description: 'List payment transactions. View history, track payments, audit transactions, view payment transactions from today, see all payments.', 276 | mcpName: 'payment' 277 | }, 278 | ]; 279 | 280 | // Group tools by MCP and index separately to get correct naming 281 | const toolsByMCP = new Map(); 282 | for (const tool of testTools) { 283 | const mcpName = tool.mcpName; 284 | if (!toolsByMCP.has(mcpName)) { 285 | toolsByMCP.set(mcpName, []); 286 | } 287 | 288 | // Extract actual tool name from full name (remove mcp prefix) 289 | const parts = tool.name.split(':'); 290 | const actualName = parts.length > 1 ? parts[1] : parts[0]; 291 | 292 | toolsByMCP.get(mcpName).push({ 293 | name: actualName, 294 | description: tool.description, 295 | mcpName: mcpName 296 | }); 297 | } 298 | 299 | // Index each MCP separately 300 | for (const [mcpName, tools] of toolsByMCP) { 301 | await engine.indexMCPTools(mcpName, tools); 302 | } 303 | }); 304 | 305 | describe('File Operation User Stories', () => { 306 | test('I want to save configuration settings to a file', async () => { 307 | const results = await engine.findRelevantTools('I want to save configuration settings to a file', 3); 308 | const topTools = results.map(r => r.name); 309 | 310 | // Should find file writing and config writing tools 311 | expect(topTools.some(t => 312 | t === 'filesystem:write_file' || 313 | t === 'config:write' || 314 | t.includes('write') 315 | )).toBeTruthy(); 316 | }); 317 | 318 | test('I need to read the contents of a log file to check for errors', async () => { 319 | const results = await engine.findRelevantTools('I need to read the contents of a log file to check for errors', 12); 320 | const topTools = results.map(r => r.name); 321 | 322 | // Should find log analysis and file reading tools 323 | expect(topTools.some(t => 324 | t === 'filesystem:read_file' || 325 | t === 'logs:analyze' || 326 | t === 'logs:search' 327 | )).toBeTruthy(); 328 | }); 329 | 330 | test('I want to delete old backup files from the system', async () => { 331 | const results = await engine.findRelevantTools('I want to delete old backup files from the system', 3); 332 | const topTools = results.map(r => r.name); 333 | 334 | // Should prioritize file deletion 335 | expect(topTools.some(t => t === 'filesystem:delete_file')).toBeTruthy(); 336 | 337 | // Should NOT prioritize database or email delete 338 | const fileDeleteIndex = topTools.indexOf('filesystem:delete_file'); 339 | const dbDeleteIndex = topTools.indexOf('database:delete'); 340 | if (fileDeleteIndex !== -1 && dbDeleteIndex !== -1) { 341 | expect(fileDeleteIndex).toBeLessThan(dbDeleteIndex); 342 | } 343 | }); 344 | 345 | test('I need to organize files by moving them to different folders', async () => { 346 | const results = await engine.findRelevantTools('I need to organize files by moving them to different folders', 7); 347 | const topTools = results.map(r => r.name); 348 | 349 | expect(topTools.some(t => 350 | t === 'filesystem:move_file' || 351 | t === 'filesystem:create_directory' 352 | )).toBeTruthy(); 353 | }); 354 | 355 | test('I want to create a backup copy of important files', async () => { 356 | const results = await engine.findRelevantTools('I want to create a backup copy of important files', 7); 357 | const topTools = results.map(r => r.name); 358 | 359 | expect(topTools.some(t => 360 | t === 'filesystem:copy_file' || 361 | t.includes('copy') || 362 | t.includes('backup') 363 | )).toBeTruthy(); 364 | }); 365 | }); 366 | 367 | describe('Database User Stories', () => { 368 | test('I need to update customer email addresses in the database', async () => { 369 | const results = await engine.findRelevantTools('I need to update customer email addresses in the database', 10); 370 | const topTools = results.map(r => r.name); 371 | 372 | expect(topTools.some(t => t === 'database:update')).toBeTruthy(); 373 | }); 374 | 375 | test('I want to create a new table for storing user sessions', async () => { 376 | const results = await engine.findRelevantTools('I want to create a new table for storing user sessions', 7); 377 | const topTools = results.map(r => r.name); 378 | 379 | expect(topTools.some(t => 380 | t === 'database:create_table' || 381 | t === 'memory:store' 382 | )).toBeTruthy(); 383 | }); 384 | 385 | test('I need to find all orders placed in the last month', async () => { 386 | const results = await engine.findRelevantTools('I need to find all orders placed in the last month', 8); 387 | const topTools = results.map(r => r.name); 388 | 389 | expect(topTools.some(t => 390 | t === 'database:query' || 391 | t.includes('search') || 392 | t.includes('find') 393 | )).toBeTruthy(); 394 | }); 395 | 396 | test('I want to remove old expired records from the database', async () => { 397 | const results = await engine.findRelevantTools('I want to remove old expired records from the database', 7); 398 | const topTools = results.map(r => r.name); 399 | 400 | expect(topTools.some(t => t === 'database:delete')).toBeTruthy(); 401 | }); 402 | }); 403 | 404 | describe('Git/Version Control User Stories', () => { 405 | test('I want to create a new feature branch for implementing dark mode', async () => { 406 | const results = await engine.findRelevantTools('I want to create a new feature branch for implementing dark mode', 6); 407 | const topTools = results.map(r => r.name); 408 | 409 | expect(topTools.some(t => t === 'git:create_branch')).toBeTruthy(); 410 | }); 411 | 412 | test('I need to save my changes and share them with the team', async () => { 413 | const results = await engine.findRelevantTools('I need to save my changes and share them with the team', 12); 414 | const topTools = results.map(r => r.name); 415 | 416 | expect(topTools.some(t => 417 | t === 'git:commit' || 418 | t === 'git:push' 419 | )).toBeTruthy(); 420 | }); 421 | 422 | test('I want to get the latest code changes from my team', async () => { 423 | const results = await engine.findRelevantTools('I want to get the latest code changes from my team', 3); 424 | const topTools = results.map(r => r.name); 425 | 426 | expect(topTools.some(t => t === 'git:pull')).toBeTruthy(); 427 | }); 428 | 429 | test('I need to propose my code changes for review', async () => { 430 | const results = await engine.findRelevantTools('I need to propose my code changes for review', 6); 431 | const topTools = results.map(r => r.name); 432 | 433 | expect(topTools.some(t => t === 'git:create_pull_request')).toBeTruthy(); 434 | }); 435 | }); 436 | 437 | describe('Memory/Storage User Stories', () => { 438 | test('I want to remember this information for later use', async () => { 439 | const results = await engine.findRelevantTools('I want to remember this information for later use', 6); 440 | const topTools = results.map(r => r.name); 441 | 442 | expect(topTools.some(t => t === 'memory:store')).toBeTruthy(); 443 | }); 444 | 445 | test('I need to recall what we discussed earlier', async () => { 446 | const results = await engine.findRelevantTools('I need to recall what we discussed earlier', 6); 447 | const topTools = results.map(r => r.name); 448 | 449 | expect(topTools.some(t => 450 | t === 'memory:retrieve' || 451 | t === 'memory:search' 452 | )).toBeTruthy(); 453 | }); 454 | 455 | test('I want to find all stored information about the project', async () => { 456 | const results = await engine.findRelevantTools('I want to find all stored information about the project', 6); 457 | const topTools = results.map(r => r.name); 458 | 459 | expect(topTools.some(t => 460 | t === 'memory:search' || 461 | t === 'memory:list' 462 | )).toBeTruthy(); 463 | }); 464 | }); 465 | 466 | describe('Communication User Stories', () => { 467 | test('I need to send a notification email to users about the system update', async () => { 468 | const results = await engine.findRelevantTools('I need to send a notification email to users about the system update', 6); 469 | const topTools = results.map(r => r.name); 470 | 471 | expect(topTools.some(t => t === 'email:send')).toBeTruthy(); 472 | }); 473 | 474 | test('I want to find all emails from a specific customer', async () => { 475 | const results = await engine.findRelevantTools('I want to find all emails from a specific customer', 6); 476 | const topTools = results.map(r => r.name); 477 | 478 | expect(topTools.some(t => t === 'email:search')).toBeTruthy(); 479 | }); 480 | 481 | test('I need to forward important emails to the team', async () => { 482 | const results = await engine.findRelevantTools('I need to forward important emails to the team', 6); 483 | const topTools = results.map(r => r.name); 484 | 485 | expect(topTools.some(t => t === 'email:forward')).toBeTruthy(); 486 | }); 487 | }); 488 | 489 | describe('Analysis User Stories', () => { 490 | test('I want to analyze server logs for error patterns and failures', async () => { 491 | const results = await engine.findRelevantTools('I want to analyze server logs for error patterns and failures', 7); 492 | const topTools = results.map(r => r.name); 493 | 494 | expect(topTools.some(t => 495 | t === 'logs:analyze' || 496 | t === 'logs:search' 497 | )).toBeTruthy(); 498 | }); 499 | 500 | test('I need to monitor logs in real-time for debugging', async () => { 501 | const results = await engine.findRelevantTools('I need to monitor logs in real-time for debugging', 6); 502 | const topTools = results.map(r => r.name); 503 | 504 | expect(topTools.some(t => t === 'logs:tail')).toBeTruthy(); 505 | }); 506 | }); 507 | 508 | describe('Web Operations User Stories', () => { 509 | test('I want to search the web for information about React best practices', async () => { 510 | const results = await engine.findRelevantTools('I want to search the web for information about React best practices', 6); 511 | const topTools = results.map(r => r.name); 512 | 513 | expect(topTools.some(t => t === 'web:search')).toBeTruthy(); 514 | }); 515 | 516 | test('I need to extract data from a website for analysis', async () => { 517 | const results = await engine.findRelevantTools('I need to extract data from a website for analysis', 6); 518 | const topTools = results.map(r => r.name); 519 | 520 | expect(topTools.some(t => t === 'web:scrape')).toBeTruthy(); 521 | }); 522 | }); 523 | 524 | describe('Configuration User Stories', () => { 525 | test('I want to update the application configuration settings', async () => { 526 | const results = await engine.findRelevantTools('I want to update the application configuration settings', 3); 527 | const topTools = results.map(r => r.name); 528 | 529 | expect(topTools.some(t => t === 'config:write')).toBeTruthy(); 530 | }); 531 | 532 | test('I need to validate the configuration file is correct', async () => { 533 | const results = await engine.findRelevantTools('I need to validate the configuration file is correct', 6); 534 | const topTools = results.map(r => r.name); 535 | 536 | expect(topTools.some(t => t === 'config:validate')).toBeTruthy(); 537 | }); 538 | }); 539 | 540 | describe('Image Processing User Stories', () => { 541 | test('I want to resize images for the website gallery', async () => { 542 | const results = await engine.findRelevantTools('I want to resize images for the website gallery', 6); 543 | const topTools = results.map(r => r.name); 544 | 545 | expect(topTools.some(t => t === 'image:resize')).toBeTruthy(); 546 | }); 547 | 548 | test('I need to compress images to reduce page load time', async () => { 549 | const results = await engine.findRelevantTools('I need to compress images to reduce page load time', 6); 550 | const topTools = results.map(r => r.name); 551 | 552 | expect(topTools.some(t => t === 'image:compress')).toBeTruthy(); 553 | }); 554 | 555 | test('I want to convert PNG images to JPEG format', async () => { 556 | const results = await engine.findRelevantTools('I want to convert PNG images to JPEG format', 6); 557 | const topTools = results.map(r => r.name); 558 | 559 | expect(topTools.some(t => t === 'image:convert')).toBeTruthy(); 560 | }); 561 | }); 562 | 563 | describe('Payment/Financial User Stories', () => { 564 | test('I need to process a payment from a customer', async () => { 565 | const results = await engine.findRelevantTools('I need to process a payment from a customer', 8); 566 | const topTools = results.map(r => r.name); 567 | 568 | expect(topTools.some(t => t === 'payment:create')).toBeTruthy(); 569 | }); 570 | 571 | test('I want to refund a customer for their cancelled order', async () => { 572 | const results = await engine.findRelevantTools('I want to refund a customer for their cancelled order', 3); 573 | const topTools = results.map(r => r.name); 574 | 575 | expect(topTools.some(t => t === 'payment:refund')).toBeTruthy(); 576 | }); 577 | 578 | test('I need to view all payment transactions from today', async () => { 579 | const results = await engine.findRelevantTools('I need to view all payment transactions from today', 3); 580 | const topTools = results.map(r => r.name); 581 | 582 | expect(topTools.some(t => t === 'payment:list')).toBeTruthy(); 583 | }); 584 | }); 585 | 586 | describe('Complex Multi-Step User Stories', () => { 587 | test('I want to read configuration files, validate them, and save the results', async () => { 588 | const results = await engine.findRelevantTools('I want to read configuration files, validate them, and save the results', 5); 589 | const topTools = results.map(r => r.name); 590 | 591 | // Should find multiple relevant tools for the multi-step process 592 | const relevantTools = [ 593 | 'config:read', 594 | 'config:validate', 595 | 'filesystem:write_file', 596 | 'filesystem:read_file' 597 | ]; 598 | 599 | const foundRelevant = topTools.filter(t => relevantTools.includes(t)); 600 | expect(foundRelevant.length).toBeGreaterThanOrEqual(2); 601 | }); 602 | 603 | test('I need to analyze logs, find errors, and send a report via email', async () => { 604 | const results = await engine.findRelevantTools('I need to analyze logs, find errors, and send a report via email', 5); 605 | const topTools = results.map(r => r.name); 606 | 607 | // Should include log analysis and email tools 608 | expect(topTools.some(t => 609 | t === 'logs:analyze' || 610 | t === 'logs:search' 611 | )).toBeTruthy(); 612 | expect(topTools.some(t => t === 'email:send')).toBeTruthy(); 613 | }); 614 | 615 | test('I want to backup database data to files and upload to cloud storage', async () => { 616 | const results = await engine.findRelevantTools('I want to backup database data to files and upload to cloud storage', 5); 617 | const topTools = results.map(r => r.name); 618 | 619 | // Should find database and file operations 620 | expect(topTools.some(t => 621 | t === 'database:query' || 622 | t === 'filesystem:write_file' || 623 | t === 'filesystem:copy_file' 624 | )).toBeTruthy(); 625 | }); 626 | }); 627 | 628 | describe('Ambiguous User Stories', () => { 629 | test('I want to save user preferences', async () => { 630 | // Ambiguous: could be file, database, memory, or config 631 | const results = await engine.findRelevantTools('I want to save user preferences', 5); 632 | const topTools = results.map(r => r.name); 633 | 634 | // Should include various storage options 635 | expect(topTools.some(t => 636 | t.includes('write') || 637 | t.includes('store') || 638 | t.includes('insert') || 639 | t === 'config:write' || 640 | t === 'memory:store' 641 | )).toBeTruthy(); 642 | }); 643 | 644 | test('I need to process user data', async () => { 645 | // Very vague - should still return something useful 646 | const results = await engine.findRelevantTools('I need to process user data', 5); 647 | const topTools = results.map(r => r.name); 648 | 649 | expect(topTools.length).toBeGreaterThan(0); 650 | // Could match various operations - database, file, memory 651 | expect(topTools.some(t => 652 | t.includes('database') || 653 | t.includes('file') || 654 | t.includes('memory') 655 | )).toBeTruthy(); 656 | }); 657 | }); 658 | 659 | describe('Edge Cases and Performance', () => { 660 | test('Very short queries should still work', async () => { 661 | const results = await engine.findRelevantTools('save file', 3); 662 | const topTools = results.map(r => r.name); 663 | 664 | expect(topTools.some(t => 665 | t === 'filesystem:write_file' || 666 | t.includes('write') 667 | )).toBeTruthy(); 668 | }); 669 | 670 | test('Technical jargon should work', async () => { 671 | const results = await engine.findRelevantTools('Execute SQL INSERT statement', 3); 672 | const topTools = results.map(r => r.name); 673 | 674 | expect(topTools.some(t => t === 'database:insert')).toBeTruthy(); 675 | }); 676 | 677 | test('User stories should return results quickly', async () => { 678 | const start = Date.now(); 679 | await engine.findRelevantTools('I want to analyze log files for error patterns and generate a report', 5); 680 | const duration = Date.now() - start; 681 | 682 | expect(duration).toBeLessThan(200); // Should be under 200ms 683 | }); 684 | 685 | test('Long detailed user stories should not timeout', async () => { 686 | const longStory = 'I need to read multiple configuration files from different directories, ' + 687 | 'validate them against our schema, merge them into a single configuration, ' + 688 | 'and then write the result to a new file while keeping backups of the originals'; 689 | 690 | const start = Date.now(); 691 | const results = await engine.findRelevantTools(longStory, 5); 692 | const duration = Date.now() - start; 693 | 694 | expect(results.length).toBeGreaterThan(0); 695 | expect(duration).toBeLessThan(300); // Even complex queries under 300ms 696 | }); 697 | 698 | test('Empty query should return all tools', async () => { 699 | const results = await engine.findRelevantTools('', 10); 700 | expect(results.length).toBeGreaterThan(0); 701 | }); 702 | }); 703 | }); ```