Natural Language Query
Convert natural language queries into actionable insights for project and task management within the Project Tracker MCP Server, enabling users to retrieve and organize data efficiently.
Instructions
Process natural language queries with enhanced entity discovery and intelligent analysis
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| prompt | Yes | Natural language query (e.g., "Show me John's overdue tasks") |
Implementation Reference
- Complete MCP tool definition including handler, schema, name, and description for 'Natural Language Query'. The handler instantiates the processor and executes the query.export const naturalLanguageQueryTool = { name: 'Natural Language Query', description: 'Process natural language queries with enhanced entity discovery and intelligent analysis', parameters: z.object({ prompt: z .string() .min(5, 'Prompt must be at least 5 characters') .max(500, 'Prompt must be less than 500 characters') .describe( 'Natural language query (e.g., "Show me Sarah\'s overdue tasks", "Analyze project health")', ), }), handler: async ({ prompt }: { prompt: string }) => { const processor = new NaturalLanguageQueryProcessor(process.env.MCP_DEBUG_MODE === 'true'); const result = await processor.processQuery(prompt); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; }, };
- Core handler logic in NaturalLanguageQueryProcessor.processQuery: parses the prompt using PromptEngine, executes based on intent (tasks, workload, risk), generates insights and recommendations, handles errors gracefully.async processQuery(prompt: string, context?: QueryContext): Promise<QueryResponse> { const startTime = Date.now(); try { this.debug(`Processing natural language query: "${prompt}"`); // Stage 1: Parse query with dynamic entity discovery const parsedQuery = await this.promptEngine.parseQuery(prompt); this.debug('Query parsed successfully:', parsedQuery); // CRITICAL FIX: Pre-execution validation for meaningless queries if (parsedQuery.confidence < 0.3 || this.isMeaninglessQuery(prompt)) { const availablePeople = await this.getAvailablePeople(); const recommendations = [ 'Please provide a clear query about tasks, people, or projects', 'Try asking about specific people like "show me Alice tasks"', 'Or ask about general task status like "show me all tasks"', ]; if (availablePeople.length > 0) { recommendations.push( `Available people: ${availablePeople.slice(0, 5).join(', ')}${availablePeople.length > 5 ? '...' : ''}`, ); } return { query: prompt, success: false, error: 'Invalid or unclear query', data: [], analysis: { intent_recognized: parsedQuery.intent, confidence_score: Math.max(0.1, parsedQuery.confidence - 0.3), // Further reduce confidence entities_found: { people: [], projects: [], conditions: {}, }, filters_applied: {}, processing_time: Date.now() - startTime, reasoning: ['Query too unclear or meaningless to process'], debugInfo: parsedQuery.metadata.debugInfo, }, insights: ['The query could not be understood'], recommendations, }; } // Stage 2: Execute query based on intent and filters let data: any[] = []; let insights: string[] = []; let recommendations: string[] = []; switch (parsedQuery.intent) { case 'query_tasks': data = await this.executeTaskQuery(parsedQuery); // SAFETY: Ensure data is always an array data = Array.isArray(data) ? data : []; insights = this.generateTaskInsights(data); recommendations = this.generateTaskRecommendations(data); break; case 'analyze_workload': if (parsedQuery.filters.assigneeName) { const workloadAnalysis = await this.apiClient.getWorkloadAnalysis( parsedQuery.filters.assigneeName, ); data = workloadAnalysis ? [workloadAnalysis] : []; insights = workloadAnalysis?.insights || []; recommendations = workloadAnalysis?.recommendations || []; } else { throw new Error('Workload analysis requires a person name'); } break; case 'assess_risk': if (parsedQuery.filters.projectId) { const riskAssessment = await this.apiClient.getRiskAssessment( parsedQuery.filters.projectId, ); data = riskAssessment ? [riskAssessment] : []; insights = riskAssessment?.insights || []; recommendations = riskAssessment?.recommendations || []; } else { throw new Error('Risk assessment requires a project identifier'); } break; case 'general_query': // EXPANSION: Intelligent fallback processing data = await this.executeGeneralQuery(parsedQuery); // SAFETY: Ensure data is always an array data = Array.isArray(data) ? data : []; insights = this.generateGeneralInsights(data); recommendations = this.generateGeneralRecommendations(data); break; default: throw new Error(`Unsupported intent: ${parsedQuery.intent}`); } // SAFETY: Final check to ensure data is always an array data = Array.isArray(data) ? data : []; const processingTime = Date.now() - startTime; // Stage 3: Build comprehensive response const response: QueryResponse = { query: prompt, success: true, data, analysis: { intent_recognized: parsedQuery.intent, confidence_score: parsedQuery.confidence, entities_found: { people: parsedQuery.entities.people, projects: parsedQuery.entities.projects, conditions: parsedQuery.entities.conditions, }, filters_applied: parsedQuery.filters, processing_time: processingTime, reasoning: parsedQuery.metadata.reasoning, debugInfo: parsedQuery.metadata.debugInfo, }, insights, recommendations, }; // ENHANCEMENT: Add better feedback for unknown entities if (parsedQuery.confidence < 0.5 && parsedQuery.entities.people.length === 0) { // Person was requested but not found - provide helpful feedback response.success = false; response.error = 'Person not found'; response.data = []; // Don't return all tasks when person not found response.insights = ['The requested person was not found in the system']; // DYNAMIC: Get available people from database const availablePeople = await this.getAvailablePeople(); const recommendations = [ "Check the spelling of the person's name", 'Try using the exact name as it appears in the system', ]; if (availablePeople.length > 0) { recommendations.push( `Available people: ${availablePeople.slice(0, 5).join(', ')}${availablePeople.length > 5 ? '...' : ''}`, ); } response.recommendations = recommendations; } // Stage 4: Add contextual suggestions if available if (context) { response.suggestions = this.generateContextualSuggestions(parsedQuery, data); } this.debug(`Query processed successfully in ${processingTime}ms`); return response; } catch (error) { const processingTime = Date.now() - startTime; this.debug(`Query processing failed: ${error}`); // ENTERPRISE: Graceful error handling with helpful suggestions return { query: prompt, success: false, error: (error as Error).message, analysis: { intent_recognized: 'error', confidence_score: 0, entities_found: { people: [], projects: [], conditions: {} }, filters_applied: {}, processing_time: processingTime, reasoning: [`Error: ${(error as Error).message}`], }, insights: ['Query processing encountered an error'], recommendations: [ 'Try being more specific with names and project references', 'Use queries like: "Show Sarah\'s overdue tasks" or "Analyze project health"', 'Check that referenced people and projects exist in the system', ], }; } }
- Zod validation schema for the tool's input parameter 'prompt'.parameters: z.object({ prompt: z .string() .min(5, 'Prompt must be at least 5 characters') .max(500, 'Prompt must be less than 500 characters') .describe( 'Natural language query (e.g., "Show me Sarah\'s overdue tasks", "Analyze project health")', ), }),
- src/mcp/tools/index.ts:16-16 (registration)Registers 'naturalLanguageQueryTool' (Natural Language Query) in the mcpTools array exported for use by the MCP server.export const mcpTools = [naturalLanguageQueryTool, workloadAnalysisTool, riskAssessmentTool];
- src/mcp/server.ts:76-88 (schema)JSON Schema provided for 'Natural Language Query' tool in the MCP server's list tools response.case 'Natural Language Query': return { type: 'object', properties: { prompt: { type: 'string', description: 'Natural language query (e.g., "Show me John\'s overdue tasks")', minLength: 5, maxLength: 500, }, }, required: ['prompt'], };