Skip to main content
Glama
portel-dev

NCP - Natural Context Provider

by portel-dev
usage-tips-generator.ts8.87 kB
/** * Shared service for generating usage tips and guidance * Provides contextual help for tool discovery and execution */ import { ParameterPredictor } from '../utils/parameter-predictor.js'; import { ToolSchemaParser } from './tool-schema-parser.js'; import { ToolContextResolver } from './tool-context-resolver.js'; import { updater } from '../utils/updater.js'; export interface UsageTipsOptions { depth: number; page: number; totalPages: number; limit: number; totalResults: number; description: string; mcpFilter: string | null; results?: any[]; includeUpdateTip?: boolean; } export class UsageTipsGenerator { /** * Generate comprehensive usage tips based on context */ static async generate(options: UsageTipsOptions): Promise<string> { const { depth, page, totalPages, limit, totalResults, description, mcpFilter, results = [], includeUpdateTip = true } = options; let tips = '\n💡 **Usage Tips**:\n'; // Depth guidance tips += this.generateDepthTips(depth); // Pagination guidance tips += this.generatePaginationTips(page, totalPages, limit, totalResults); // Search guidance tips += this.generateSearchTips(description, mcpFilter); // Tool execution guidance tips += this.generateExecutionTips(results, depth); // Update tip (non-blocking) if (includeUpdateTip) { try { const updateTip = await updater.getUpdateTip(); if (updateTip) { tips += `• ${updateTip}\n`; } } catch { // Fail silently - don't let update checks break the command } } return tips; } /** * Generate depth-related tips */ private static generateDepthTips(depth: number): string { if (depth === 0) { return `• **See descriptions**: Use \`--depth 1\` for descriptions, \`--depth 2\` for parameters\n`; } else if (depth === 1) { return `• **See parameters**: Use \`--depth 2\` for parameter details (recommended for AI)\n` + `• **Quick scan**: Use \`--depth 0\` for just tool names\n`; } else { return `• **Less detail**: Use \`--depth 1\` for descriptions only or \`--depth 0\` for names only\n`; } } /** * Generate pagination-related tips */ private static generatePaginationTips(page: number, totalPages: number, limit: number, totalResults: number): string { let tips = ''; if (totalPages > 1) { tips += `• **Navigation**: `; if (page < totalPages) { tips += `\`--page ${page + 1}\` for next page, `; } if (page > 1) { tips += `\`--page ${page - 1}\` for previous, `; } tips += `\`--limit ${Math.min(limit * 2, 50)}\` for more per page\n`; } else if (totalResults > limit) { tips += `• **See more**: Use \`--limit ${Math.min(totalResults, 50)}\` to see all ${totalResults} results\n`; } else if (limit > 10 && totalResults < limit) { tips += `• **Smaller pages**: Use \`--limit 5\` for easier browsing\n`; } return tips; } /** * Generate search-related tips */ private static generateSearchTips(description: string, mcpFilter: string | null): string { let tips = ''; if (!description) { tips = `• **Search examples**: \`ncp find "filesystem"\` (MCP filter) or \`ncp find "write file"\` (cross-MCP search)\n`; } else if (mcpFilter) { tips = `• **Broader search**: Remove MCP name from query for cross-MCP results\n`; } else { tips = `• **Filter to MCP**: Use MCP name like \`ncp find "filesystem"\` to see only that MCP's tools\n`; } // Add confidence threshold guidance for search queries if (description) { tips += `• **Precision control**: \`--confidence_threshold 0.1\` (show all), \`0.5\` (strict), \`0.7\` (very precise)\n`; } return tips; } /** * Generate tool execution tips with examples */ private static generateExecutionTips(results: any[], depth: number): string { if (results.length === 0) { return `• **Run tools**: Use \`ncp run <mcp> <tool>\` to execute (interactive prompts for parameters)\n`; } if (depth >= 2) { // Only show parameter examples when depth >= 2 (when schemas are available) const exampleTool = this.findToolWithParameters(results); const exampleParams = this.generateExampleParams(exampleTool); // Generate Code-Mode example const codeModeExample = this.generateCodeModeExample(results.slice(0, 3)); // Use space-separated syntax for CLI examples const cmdName = exampleTool.toolName.replace(':', ' '); if (exampleParams === '{}') { return `• **Run tools**: Use \`ncp run ${cmdName}\` to execute (no parameters needed)\n` + `• **Code-Mode** (fastest): ${codeModeExample}\n`; } else { return `• **Run tools**: Use \`ncp run ${cmdName}\` (interactive prompts) or add arguments like \`--arg value\`\n` + `• **Code-Mode** (fastest): ${codeModeExample}\n`; } } else { // At depth 0-1, use interactive prompting return `• **Run tools**: Use \`ncp run ${results[0].toolName}\` to execute (interactive prompts for parameters)\n`; } } /** * Generate Code-Mode usage example from discovered tools */ private static generateCodeModeExample(tools: any[]): string { if (tools.length === 0) return ''; // Pick the first tool with parameters as example const exampleTool = this.findToolWithParameters(tools); const [mcpName, toolName] = exampleTool.toolName.split(':'); // Generate example parameters based on schema const exampleParams = this.generateExampleParamsForCode(exampleTool); if (exampleParams) { return `\`const result = await ${mcpName}.${toolName}(${exampleParams})\``; } else { return `\`const result = await ${mcpName}.${toolName}()\``; } } /** * Generate example parameters formatted for Code-Mode (inline object) */ private static generateExampleParamsForCode(tool: any): string { if (!tool.schema || !tool.schema.properties) return ''; const params = ToolSchemaParser.parseParameters(tool.schema); const requiredParams = params.filter(p => p.required); // Only show required params in example, max 2 for brevity const paramsToShow = requiredParams.length > 0 ? requiredParams.slice(0, 2) : params.slice(0, 2); if (paramsToShow.length === 0) return ''; const predictor = new ParameterPredictor(); const paramPairs = paramsToShow.map(param => { const value = predictor.predictValue(param.name, param.type, param.description || ''); return `${param.name}: ${JSON.stringify(value)}`; }); return `{ ${paramPairs.join(', ')} }`; } /** * Find a tool with parameters for better examples, fallback to first tool */ private static findToolWithParameters(results: any[]): any { if (results.length === 0) return null; let exampleTool = results[0]; let exampleParams = this.generateExampleParams(exampleTool); // If first tool has no parameters, try to find one that does if (exampleParams === '{}' && results.length > 1) { for (let i = 1; i < results.length; i++) { const candidateParams = this.generateExampleParams(results[i]); if (candidateParams !== '{}') { exampleTool = results[i]; break; } } } return exampleTool; } /** * Generate example parameters for a tool */ private static generateExampleParams(tool: any): string { if (!tool?.schema) { return '{}'; } const params = ToolSchemaParser.parseParameters(tool.schema); const requiredParams = params.filter(p => p.required); const optionalParams = params.filter(p => !p.required); const predictor = new ParameterPredictor(); const toolContext = ToolContextResolver.getContext(tool.toolName); const exampleObj: any = {}; // Always include required parameters for (const param of requiredParams) { exampleObj[param.name] = predictor.predictValue( param.name, param.type, toolContext, param.description, tool.toolName ); } // If no required parameters, show 1-2 optional parameters as examples if (requiredParams.length === 0 && optionalParams.length > 0) { const exampleOptionals = optionalParams.slice(0, 2); // Show up to 2 optional params for (const param of exampleOptionals) { exampleObj[param.name] = predictor.predictValue( param.name, param.type, toolContext, param.description, tool.toolName ); } } return Object.keys(exampleObj).length > 0 ? JSON.stringify(exampleObj) : '{}'; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/portel-dev/ncp'

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