PTENES
MODULE 4.2

🗄️ Database Consultant

Create an MCP Server that connects to SQLite—the model queries and manipulates data using natural language.

6
Topics
45
Minutes
Intermediate
Level
Practical
Type
1

🗄️ SQLite and Python

O SQLite is an embedded relational database—it doesn’t need a separate server running, nor does it require installation or configuration. The entire database lives in a single file .db. Python already includes the sqlite3 in the standard library, which means zero external dependencies. It's the perfect choice for learning and local projects.

💎 Core Concept

SQLite is a database serverless — unlike PostgreSQL or MySQL, it doesn't run as a separate service. Just run import sqlite3 and use sqlite3.connect("meu_banco.db") to create or open a database. All SQL operations work as usual: CREATE TABLE, INSERT, SELECT, UPDATE, DELETE. The file .db is portable—you can copy, version, and share it. For our MCP Server, this means we don’t need to configure any extra infrastructure.

📊 Data and Research

SQLite is the the most widely deployed database in the world — is present in every smartphone (Android and iOS use it internally), every browser (Chrome, Firefox, Safari), and billions of IoT devices. Although it's "lite," it supports databases up to 281 terabytes and processes millions of lines without issues. For local projects and prototypes, it’s more than enough.

💡 Practical Tip

To check that everything is working, open the Python terminal and test: import sqlite3; conn = sqlite3.connect(":memory:"); print("SQLite OK!"). O :memory: creates a temporary database in memory—perfect for quick tests. If it runs without errors, you have everything you need for this module.

2

📊 Creating the Sample Database

Before building the MCP tools, we need a database with sample data to query. Let's create a Python script that initializes a table of products with practical fields and inserts realistic data. This database will be the "playground" Claude explores using natural language.

💎 Core Concept

The table products will have 5 columns: id (INTEGER PRIMARY KEY AUTOINCREMENT), nome (TEXT NOT NULL), preco (REAL NOT NULL), categoria (TEXT NOT NULL) and estoque (INTEGER DEFAULT 0). The creation SQL: CREATE TABLE IF NOT EXISTS produtos (...). Use IF NOT EXISTS so the script can run multiple times without errors. Insert at least 10 sample products with varied categories (Electronics, Books, Clothing, etc.) so the queries are interesting.

📋 Steps to Create the Database
Step 1: Create the script

Create a criar_banco.py file that imports sqlite3 and defines the database initialization function with the produtos table.

Step 2: Insert sample data

Use INSERT INTO with executemany() to insert multiple products at once. Include a range of prices, categories, and stock quantities.

Step 3: Run and verify

Run the script (python criar_banco.py), verify that the .db file was created, and run a test SELECT to confirm the data.

💡 Practical Tip

Always use conn.commit() after write operations (INSERT, UPDATE, DELETE) and conn.close() when finished. Use the context manager with sqlite3.connect("banco.db") as conn: to ensure the connection is closed automatically even if an error occurs. This prevents data corruption.

3

🔍 Tool: Query Data

The query tool is the heart of our Database Consultant. The user describes what they want to know in natural language—"which products cost more than R$50?"—and Claude translates that into a tool call. The tool receives a description of the query, builds the appropriate SQL SELECT, and returns formatted results.

💎 Core Concept

The tool consultar(query_descricao: str) receives a natural-language description of what the user wants to query. A safe approach is to offer predefined queries: list all products, filter by category, search by price range, check inventory. For each type of query, the tool builds a safe SQL SELECT with parameters. The results are formatted as a readable table with the name, price, category, and inventory of each product found.

❌ NEVER do this
  • •Concatenate user input directly into SQL
  • •f"SELECT * WHERE nome = '{input}'"
  • •Run arbitrary SQL without validation
  • •Trust that the model always generates safe SQL
✅ Always do this
  • •Use parameterized queries with placeholders (?)
  • •cursor.execute("SELECT * WHERE nome = ?", (input,))
  • •Validate and sanitize any input before using it
  • •Limit operations to SELECT (read-only)
4

📝 Tool: Insert and Update

In addition to querying, our server will also let Claude add new products e update existing data. These write tools need rigorous data validation before performing any database operation, and should always confirm the operation’s result.

💎 Core Concept

The tool inserir_produto(nome: str, preco: float, categoria: str, estoque: int = 0) validates that the price is positive and the name isn't empty, then executes a parameterized INSERT. The tool atualizar_produto(id: int, nome: str = None, preco: float = None, categoria: str = None, estoque: int = None) looks up the product by ID, updates only the fields provided, and confirms the operation. Both use conn.commit() to persist the changes and return a clear success or error message.

💡 Practical Tip

Always validate the data before before executing the SQL. Check: Is the price greater than zero? Is the name nonempty? Does the ID exist in the database? Is the stock level nonnegative? These simple validations prevent inconsistent data and return helpful error messages for Claude to show the user, instead of cryptic database exceptions.

🚨 Alert

Write operations database write (INSERT, UPDATE, DELETE) are irreversible after commit. Unlike JSON, where you can easily restore a previous version, changes to SQLite are permanent. Consider implementing a confirmation mechanism: the tool first shows what it will do and only executes after the user confirms. This isn't critical for learning projects, but it's a good practice.

5

🛡️ Security: SQL Injection

SQL injection is one of the most dangerous vulnerabilities in applications that use databases—and in MCP servers, the risk is even greater because the AI model may inadvertently generate inputs that exploit this vulnerability. This topic is absolutely critical: understanding and preventing SQL Injection is mandatory for any developer who works with databases.

💎 Core Concept

SQL Injection occurs when data provided by the user (or the model) is inserted directly into an SQL string, allowing malicious SQL commands to be executed. In an MCP Server, the AI model receives user input and passes it to the tools — if the model sends something like "; DROP TABLE produtos; -- as a product name, and the tool concatenates it directly into the SQL, the result is the destruction of the entire table. The defense is simple and absolute: ALWAYS use parameterized queries with placeholders ?.

🚨 CRITICAL — Read Carefully

NEVER, under no circumstances concatenate strings directly into SQL queries. It doesn’t matter if it "seems safe" or "the model wouldn’t send that" — AI models are unpredictable and can generate any text, including SQL injection payloads. An MCP Server with SQL injection is a open port for data destruction. Always use cursor.execute("SELECT * FROM produtos WHERE nome = ?", (nome,)) — the placeholder ? ensures the value is treated as data, never as SQL code.

❌ VULNERABLE — Never do this
  • •f"SELECT * FROM produtos WHERE nome = '{nome}'"
  • •"INSERT INTO produtos VALUES ('" + nome + "')"
  • •query = "DELETE WHERE id = " + str(id)
  • •Any form of string concatenation with SQL
✅ SAFE — Always do this
  • •cursor.execute("SELECT * WHERE nome = ?", (nome,))
  • •cursor.execute("INSERT INTO ... VALUES (?, ?)", (n, p))
  • •cursor.execute("DELETE WHERE id = ?", (id,))
  • •Always use placeholders (?) + a tuple of values
6

🧪 Queries via Claude

Now comes the most satisfying part: testing the Database Consultant with natural-language questions. Claude will automatically translate user requests into calls to the right tools and present the results clearly and helpfully. This is where the magic of MCP really shines.

💎 Core Concept

Configure the server in Claude Desktop and start chatting naturally. Claude will analyze each request, decide which tool to call and with which parameters, run the database query, and present the results in natural language. You don't need to know SQL—the model translates it for you. It's all thanks to MCP, which connects the model's language capabilities to the power of the database.

📋 Suggested Test Scenarios
Simple query

"Which products cost more than R$50?" — Claude should call the query tool and format the list of products priced above 50.

Product entry

"Add a product called 'Mechanical Keyboard' in the Electronics category, costing R$299,90 with 15 units in stock." — Should call inserir_produto.

Aggregate query

"What's the total inventory by category?" — Claude should query the data and present an aggregate summary by category.

Update

"Update the price of product ID 3 to R$89,90." — Should call atualizar_produto with the correct parameters.

💡 Practical Tip

Try increasingly complex questions to test your server’s limits: "What is the average price of products in the Electronics category?", "Which products have fewer than 5 units in stock?", "Compare the average price across categories". Each question will reveal whether your tools are expressive enough or need more features. This is iterative development in practice.

📝 Module Summary

  • ✓ SQLite is an embedded, serverless database included with Python—perfect for local MCP projects.
  • ✓ We created a sample database with a products table containing varied data for interesting queries.
  • ✓ We implemented query tools that translate natural language into safe SQL queries.
  • ✓ We added tools for inserting and updating data, with validation before writing.
  • ✓ We learned about SQL injection and the absolute importance of using parameterized queries with placeholders (?).
  • ✓ We tested the complete server with natural language queries via Claude Desktop.

Next Module: 4.3 — File Assistant