Skip to main content
Glama
ramuzes

MCP Server for Apache Jena

sparql-helper.ts8.64 kB
export interface ValidationResult { valid: boolean; errors: string[]; suggestions: string[]; queryType?: 'SELECT' | 'CONSTRUCT' | 'ASK' | 'DESCRIBE' | 'INSERT' | 'DELETE' | 'UNKNOWN'; } export class SparqlHelper { /** * Validates a SPARQL query and provides suggestions */ static validateQuery(query: string): ValidationResult { const errors: string[] = []; const suggestions: string[] = []; if (!query.trim()) { errors.push("Query cannot be empty"); return { valid: false, errors, suggestions }; } const upperQuery = query.toUpperCase(); const trimmedQuery = query.trim(); // Detect query type let queryType: ValidationResult['queryType'] = 'UNKNOWN'; if (upperQuery.includes('SELECT')) queryType = 'SELECT'; else if (upperQuery.includes('CONSTRUCT')) queryType = 'CONSTRUCT'; else if (upperQuery.includes('ASK')) queryType = 'ASK'; else if (upperQuery.includes('DESCRIBE')) queryType = 'DESCRIBE'; else if (upperQuery.includes('INSERT')) queryType = 'INSERT'; else if (upperQuery.includes('DELETE')) queryType = 'DELETE'; // Check for query form const hasQueryForm = ['SELECT', 'CONSTRUCT', 'ASK', 'DESCRIBE', 'INSERT', 'DELETE'].some(form => upperQuery.includes(form) ); if (!hasQueryForm) { errors.push("Query must include a query form: SELECT, CONSTRUCT, ASK, DESCRIBE, INSERT, or DELETE"); suggestions.push("Example: SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 10"); return { valid: false, errors, suggestions, queryType }; } // Check for WHERE clause in queries that typically need it if ((queryType === 'SELECT' || queryType === 'CONSTRUCT') && !upperQuery.includes('WHERE') && !upperQuery.includes('INSERT DATA') && !upperQuery.includes('DELETE DATA')) { errors.push("SELECT and CONSTRUCT queries typically require a WHERE clause"); suggestions.push("Add: WHERE { ?subject ?predicate ?object }"); } // Check for balanced braces const openBraces = (query.match(/{/g) || []).length; const closeBraces = (query.match(/}/g) || []).length; if (openBraces !== closeBraces) { errors.push(`Unbalanced braces: ${openBraces} opening braces, ${closeBraces} closing braces`); suggestions.push("Check that every { has a matching }"); } // Check for balanced parentheses const openParens = (query.match(/\(/g) || []).length; const closeParens = (query.match(/\)/g) || []).length; if (openParens !== closeParens) { errors.push(`Unbalanced parentheses: ${openParens} opening, ${closeParens} closing`); suggestions.push("Check that every ( has a matching )"); } // Check for missing semicolon after PREFIX declarations const prefixLines = query.split('\n').filter(line => line.trim().toUpperCase().startsWith('PREFIX') && !line.trim().endsWith('.') ); if (prefixLines.length > 0) { suggestions.push("PREFIX declarations should end with a dot (.)"); } // Suggest LIMIT for potentially large result sets if (queryType === 'SELECT' && !upperQuery.includes('LIMIT') && !upperQuery.includes('COUNT')) { suggestions.push("Consider adding LIMIT clause to prevent large result sets"); } // Check for common syntax issues if (query.includes('?') && !query.includes('WHERE')) { suggestions.push("Variables (starting with ?) typically appear in WHERE clauses"); } // Check for property path syntax issues if (query.includes('/') || query.includes('*') || query.includes('+')) { if (!upperQuery.includes('WHERE')) { suggestions.push("Property paths (/, *, +) should be used within WHERE clauses"); } } // Check for FILTER placement if (upperQuery.includes('FILTER') && !upperQuery.includes('WHERE')) { errors.push("FILTER clauses must be inside WHERE blocks"); } return { valid: errors.length === 0, errors, suggestions, queryType }; } /** * Provides enhanced error messages with SPARQL-specific guidance */ static enhanceErrorMessage(originalError: string, query: string): string { let enhancedMessage = originalError; const upperQuery = query.toUpperCase(); // Common error patterns and their solutions if (originalError.includes('400') || originalError.includes('Bad Request')) { enhancedMessage += "\n\n🔧 Common SPARQL syntax issues:"; enhancedMessage += "\n• Check PREFIX declarations end with dots (.)"; enhancedMessage += "\n• Ensure proper triple pattern syntax: ?subject ?predicate ?object"; enhancedMessage += "\n• Verify WHERE clause is properly formed with { }"; enhancedMessage += "\n• Check for missing closing braces }"; enhancedMessage += "\n• Validate property path syntax (/, *, +, ?, ^)"; if (upperQuery.includes('FILTER')) { enhancedMessage += "\n• FILTER clauses must be inside WHERE blocks"; } } if (originalError.includes('401') || originalError.includes('Unauthorized')) { enhancedMessage += "\n\n🔐 Authentication issue: Check your username and password"; } if (originalError.includes('404') || originalError.includes('Not Found')) { enhancedMessage += "\n\n🎯 Endpoint issue: Verify the dataset name and Fuseki URL"; } if (originalError.includes('timeout')) { enhancedMessage += "\n\n⏱️ Query timeout: Try adding LIMIT clause or simplifying the query"; } return enhancedMessage; } /** * Suggests query improvements based on content analysis */ static suggestImprovements(query: string): string[] { const suggestions: string[] = []; const upperQuery = query.toUpperCase(); // Performance suggestions if (upperQuery.includes('SELECT') && !upperQuery.includes('LIMIT') && !upperQuery.includes('COUNT')) { suggestions.push("Add LIMIT clause for better performance and testing"); } if (upperQuery.includes('?S ?P ?O') && !upperQuery.includes('LIMIT')) { suggestions.push("Querying all triples (?s ?p ?o) without LIMIT can be very slow"); } // Readability suggestions if (!query.includes('\n') && query.length > 100) { suggestions.push("Consider formatting the query with line breaks for better readability"); } if (upperQuery.includes('FILTER') && upperQuery.includes('REGEX')) { suggestions.push("REGEX filters can be slow; consider using more specific triple patterns when possible"); } // Best practices if (upperQuery.includes('OPTIONAL') && !upperQuery.includes('BOUND')) { suggestions.push("Consider using BOUND() function to check if OPTIONAL variables are bound"); } return suggestions; } /** * Generates example queries for learning */ static generateExampleQueries(): { [category: string]: string[] } { return { basic: [ "SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 10", "SELECT (COUNT(*) as ?count) WHERE { ?s ?p ?o }", "SELECT DISTINCT ?type WHERE { ?s a ?type }" ], propertyPaths: [ "SELECT ?person ?friend WHERE { ?person foaf:knows/foaf:knows ?friend }", "SELECT ?person ?connected WHERE { ?person foaf:knows* ?connected }", "SELECT ?child ?parent WHERE { ?child ^ex:hasParent ?parent }" ], filters: [ "SELECT ?person ?age WHERE { ?person ex:age ?age . FILTER(?age > 18) }", "SELECT ?person WHERE { ?person foaf:name ?name . FILTER(REGEX(?name, \"John\", \"i\")) }" ], optional: [ "SELECT ?person ?name ?email WHERE { ?person a foaf:Person . OPTIONAL { ?person foaf:name ?name } OPTIONAL { ?person foaf:mbox ?email } }" ], aggregation: [ "SELECT ?type (COUNT(?instance) as ?count) WHERE { ?instance a ?type } GROUP BY ?type ORDER BY DESC(?count)" ] }; } /** * Common vocabulary prefixes for convenience */ static getCommonPrefixes(): Record<string, string> { return { rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", rdfs: "http://www.w3.org/2000/01/rdf-schema#", owl: "http://www.w3.org/2002/07/owl#", foaf: "http://xmlns.com/foaf/0.1/", dcterms: "http://purl.org/dc/terms/", skos: "http://www.w3.org/2004/02/skos/core#", schema: "http://schema.org/", xsd: "http://www.w3.org/2001/XMLSchema#", geo: "http://www.w3.org/2003/01/geo/wgs84_pos#", time: "http://www.w3.org/2006/time#" }; } }

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/ramuzes/mcp-jena'

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