Introduction
As developers, we spend a significant amount of time writing SQL queries. Whether it’s a simple SELECT statement or a complex JOIN with multiple aggregations, remembering the exact syntax can be tedious. What if you could just describe what you want in plain English and get production-ready SQL?
That’s exactly what I built for the HNG Stage 3 Backend Task - a Message-to-SQL AI Agent that translates natural language into SQL queries using Mastra AI and integrated with Telex.im.
The Problem
Every developer faces these challenges:
- Syntax Complexity: SQL has different dialects (PostgreSQL, MySQL, SQLite, etc.) with subtle differences
- Best Practices: It’s easy to write working SQL that performs poorly or has security issues
- Context Switching: …
Introduction
As developers, we spend a significant amount of time writing SQL queries. Whether it’s a simple SELECT statement or a complex JOIN with multiple aggregations, remembering the exact syntax can be tedious. What if you could just describe what you want in plain English and get production-ready SQL?
That’s exactly what I built for the HNG Stage 3 Backend Task - a Message-to-SQL AI Agent that translates natural language into SQL queries using Mastra AI and integrated with Telex.im.
The Problem
Every developer faces these challenges:
- Syntax Complexity: SQL has different dialects (PostgreSQL, MySQL, SQLite, etc.) with subtle differences
- Best Practices: It’s easy to write working SQL that performs poorly or has security issues
- Context Switching: Moving between thinking about business logic and remembering SQL syntax breaks flow
- Security: SQL injection vulnerabilities are still one of the most common security issues
The Solution
I built an AI agent that:
- Converts natural language to SQL queries
- Validates syntax and suggests best practices
- Explains complex queries in plain English
- Optimizes queries for better performance
- Works with multiple SQL dialects
- Integrates seamlessly with Telex.im for team collaboration
Technical Architecture
Tech Stack
- Framework: Mastra AI - A powerful TypeScript framework for building AI agents
- LLM: Grok llama-3.1-8b-instant for natural language understanding
- Integration Platform: Telex.im using A2A protocol
- Language: TypeScript with Node.js
- Storage: LibSQL for agent memory and observability
Why Mastra?
Mastra made this project straightforward because it provides:
- Built-in Agent Framework: No need to build from scratch
- Tool System: Easy to create specialized tools for SQL operations
- Workflow Management: Clean way to orchestrate multi-step processes
- Memory & Context: Built-in conversation memory
- Observability: Out-of-the-box tracing and monitoring
- Scoring System: Quality evaluation for agent outputs
Building the Agent
Step 1: Project Setup
First, I initialized the project with Mastra:
npm create mastra@latest -y
The project structure:
src/mastra/
├── agents/
│ └── sql-agent.ts # Main agent definition
├── tools/
│ └── sql-tool.ts # SQL tools
├── scorers/
│ └── sql-scorer.ts # Quality evaluation
├── workflows/
│ └── sql-workflow.ts # Workflow orchestration
└── index.ts # Mastra configuration
Step 2: Creating SQL Tools
I built four specialized tools to enhance the agent’s capabilities:
1. SQL Validator Tool
export const sqlValidatorTool = createTool({
id: "sql-validator",
description: "Validates and formats SQL queries",
inputSchema: z.object({
sql: z.string(),
dialect: z
.enum(["postgresql", "mysql", "sqlite", "mssql", "oracle"])
.optional(),
}),
outputSchema: z.object({
isValid: z.boolean(),
formatted: z.string(),
warnings: z.array(z.string()),
suggestions: z.array(z.string()),
}),
execute: async ({ context }) => {
return validateSQL(context.sql, context.dialect || "postgresql");
},
});
This tool checks for:
- SQL syntax correctness
- Dangerous patterns (missing WHERE in UPDATE/DELETE)
- Best practice violations (SELECT *)
- Security vulnerabilities
2. Schema Info Tool
Provides common database patterns and examples:
export const schemaInfoTool = createTool({
id: "schema-info",
description: "Provides common database schema patterns",
inputSchema: z.object({
tableType: z.string(),
}),
outputSchema: z.object({
commonColumns: z.array(z.string()),
relationships: z.array(z.string()),
examples: z.array(z.string()),
}),
execute: async ({ context }) => {
return getSchemaInfo(context.tableType);
},
});
3. SQL Explainer Tool
Breaks down complex queries into understandable components:
export const sqlExplainerTool = createTool({
id: "sql-explainer",
description: "Explains what a SQL query does in plain English",
inputSchema: z.object({
sql: z.string(),
}),
outputSchema: z.object({
explanation: z.string(),
components: z.array(
z.object({
part: z.string(),
description: z.string(),
})
),
}),
execute: async ({ context }) => {
return explainSQL(context.sql);
},
});
4. SQL Optimizer Tool
Suggests performance improvements:
export const sqlOptimizerTool = createTool({
id: "sql-optimizer",
description: "Suggests optimizations for SQL queries",
// ... implementation
});
Step 3: Defining the Agent
The agent ties everything together with clear instructions:
export const sqlAgent = new Agent({
name: "SQL Generator Agent",
instructions: `
You are an expert SQL assistant that helps developers translate
natural language queries into SQL statements.
Guidelines:
- Ask for clarification on table/column names if not specified
- Default to PostgreSQL syntax unless specified
- Use explicit JOIN syntax
- Avoid SELECT * in production queries
- Include security warnings for dangerous patterns
- Format with proper indentation
`,
model: "openai/gpt-4o-mini",
tools: {
sqlValidatorTool,
schemaInfoTool,
sqlExplainerTool,
sqlOptimizerTool,
},
scorers: {
sqlCorrectness: {
/* ... */
},
intentMatch: {
/* ... */
},
readability: {
/* ... */
},
},
memory: new Memory({
storage: new LibSQLStore({ url: "file:../mastra.db" }),
}),
});
Step 4: Quality Scoring
I implemented three scorers to ensure high-quality outputs:
SQL Correctness Scorer
Uses groq/llama-3.1-8b-instant as a judge to evaluate:
- Syntax correctness
- Best practices adherence
- Security issues
export const sqlCorrectnessScorer = createScorer({
name: "SQL Correctness",
type: "agent",
judge: {
model: "groq/llama-3.1-8b-instant",
instructions: "Evaluate SQL for syntax, best practices, and security...",
},
})
.preprocess(({ run }) => {
/* Extract SQL */
})
.analyze({
/* Analyze quality */
})
.generateScore(({ results }) => {
/* Calculate score */
})
.generateReason(({ results, score }) => {
/* Explain score */
});
Intent Match Scorer
Ensures the generated SQL matches what the user actually wanted:
export const intentMatchScorer = createScorer({
name: "Intent Match",
// Checks if SELECT query was generated for retrieval intent, etc.
});
Readability Scorer
Evaluates formatting and documentation:
export const readabilityScorer = createScorer({
name: "SQL Readability",
// Checks formatting, indentation, comments
});
Step 5: Creating the Workflow
The workflow orchestrates the entire process:
const generateSQLStep = createStep({
id: "generate-sql",
description: "Generates SQL query from natural language",
execute: async ({ inputData, mastra }) => {
const agent = mastra?.getAgent("sqlAgent");
const response = await agent.stream([
{ role: "user", content: inputData.message },
]);
let responseText = "";
for await (const chunk of response.textStream) {
responseText += chunk;
}
return { sql: extractSQL(responseText), explanation: responseText };
},
});
const sqlWorkflow = createWorkflow({
id: "sql-workflow",
inputSchema: z.object({
message: z.string(),
dialect: z
.enum(["postgresql", "mysql", "sqlite", "mssql", "oracle"])
.optional(),
}),
}).then(generateSQLStep);
Integrating with Telex.im
Setting Up the Integration
Create an AI Co-Worker in Telex In your Telex dashboard, navigate to the AI Co-Workers section and create a new co-worker. 1.
Deploy the Agent I deployed to Render for easy hosting 1.
Create Workflow JSON
{
"active": true,
"category": "productivity",
"description": "A project manager bot that tracks and summarizes tasks for Telex users.",
"id": "unique_id_generated",
"long_description": "You help users manage tasks and summarize chat history. Recognize project deadlines, remind users, and provide friendly nudges. Use clear, concise language and ask for missing details.",
"name": "project_helper_agent",
"nodes": [
{
"id": "task_agent_node",
"name": "Project helper bot",
"parameters": {},
"position": [100, 100],
"type": "a2a/mastra-a2a-node",
"typeVersion": 1,
"url": "https://your-domain.com/a2a/agent/sqlAgent"
}
],
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"short_description": "Task/project manager for Telex"
}
- Import to Telex
- Upload workflow JSON to Telex
- Activate the workflow
Testing the Integration
The agent works beautifully in Telex! Here’s a real conversation:
Me: “Get all users who registered in the last 30 days”
Agent:
SELECT id, username, email, created_at
FROM users
WHERE created_at >= CURRENT_DATE - INTERVAL '30 days'
ORDER BY created_at DESC;
Explanation: This query retrieves user information for accounts created in the last 30 days, sorted by most recent first. It uses explicit column selection (avoiding SELECT *) and includes proper date arithmetic.
Security Note: Use parameterized queries to prevent SQL injection when implementing this.
Conclusion
Building this SQL agent taught me:
- AI agents are powerful when combined with specialized tools
- Mastra makes building production-ready agents surprisingly straightforward
- Quality scoring is essential for reliable outputs
- Integration platforms like Telex make agents accessible to teams
Try It Yourself
The project is open source! Check it out:
- GitHub: hng-stage-3
- Telex Integration: instructions-link
Resources
Acknowledgments
Thanks to:
- @mastra for the excellent AI framework
- @teleximapp for the integration platform
- @hnginternship for this challenging task
What would you build with AI agents? Let me know in the comments!
If you found this helpful, please share it with other developers building AI agents.