Create an MCP Server that connects to SQLite—the model queries and manipulates data using natural language.
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.
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.
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.
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.
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.
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.
Create a criar_banco.py file that imports sqlite3 and defines the database initialization function with the produtos table.
Use INSERT INTO with executemany() to insert multiple products at once. Include a range of prices, categories, and stock quantities.
Run the script (python criar_banco.py), verify that the .db file was created, and run a test SELECT to confirm the data.
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.
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.
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.
f"SELECT * WHERE nome = '{input}'"cursor.execute("SELECT * WHERE nome = ?", (input,))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.
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.
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.
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.
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.
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 ?.
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.
f"SELECT * FROM produtos WHERE nome = '{nome}'""INSERT INTO produtos VALUES ('" + nome + "')"query = "DELETE WHERE id = " + str(id)cursor.execute("SELECT * WHERE nome = ?", (nome,))cursor.execute("INSERT INTO ... VALUES (?, ?)", (n, p))cursor.execute("DELETE WHERE id = ?", (id,))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.
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.
"Which products cost more than R$50?" — Claude should call the query tool and format the list of products priced above 50.
"Add a product called 'Mechanical Keyboard' in the Electronics category, costing R$299,90 with 15 units in stock." — Should call inserir_produto.
"What's the total inventory by category?" — Claude should query the data and present an aggregate summary by category.
"Update the price of product ID 3 to R$89,90." — Should call atualizar_produto with the correct parameters.
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.
Next Module: 4.3 — File Assistant