Building a Database MCP Server: The Ultimate Code Walkthrough - Part 4
- ganesh90
- Jul 4
- 6 min read
Updated: Jul 7
Prerequisite: This is a continuation of the blog Part 2: Building a Database MCP Server: The Ultimate Code Walkthrough - Part 3

The Connection Traffic Controller - list_database_connections
@mcp.tool()
async def list_database_connections() -> str:
"""List all active database connections"""
connections = {}
for key, manager in connection_pool.items():
db_type, db_path = key.split(':', 1)
connections[key] = {
'database_path': db_path,
'database_type': db_type,
'is_sample_database': is_sample_database(db_path),
'is_connected': manager.is_connected(),
'connection_initialized': manager.is_initialized
}
return json.dumps({
'status': 'success',
'active_connections': connections,
'total_connections': len(connections),
'sample_database_path': SAMPLE_DB_PATH
}, indent=2)The Connection Pool Inspector! 🏊♂️
This function is like having a dashboard that shows all your open database connections with their health status and details.
Step 1: Connection Dictionary Initialization
connections = {}The Results Container:
Empty dictionary: Will be populated with connection details
Key-value structure: Each connection gets a unique entry
Flexible storage: Can handle any number of active connections
Step 2: Connection Pool Iteration
for key, manager in connection_pool.items():The Pool Walker:
connection_pool.items(): Gets both keys and manager objects
Key format: "sqlite:/path/to/db.db" or "postgresql:database_name"
Manager object: Our DatabaseManager instance with connection details
Step 3: Connection Key Parsing
db_type, db_path = key.split(':', 1)The Key Decoder:
split(':', 1): Splits only on the FIRST colon
Why limit to 1? Database paths might contain colons (Windows: "C:/path/db.db")
Result examples:
"sqlite:/data/app.db" → db_type="sqlite", db_path="/data/app.db"
"postgresql:host:port/dbname" → db_type="postgresql", db_path="host:port/dbname"
Step 4: Connection Health Assessment
connections[key] = {
'database_path': db_path,
'database_type': db_type,
'is_sample_database': is_sample_database(db_path),
'is_connected': manager.is_connected(),
'connection_initialized': manager.is_initialized
}The Health Check Compilation:
database_path: The actual file path or connection identifier
database_type: "sqlite" or "postgresql"
is_sample_database(db_path): Boolean flag for sample database detection
manager.is_connected(): Live health check - actually tests the connection!
manager.is_initialized: Whether the connection was properly set up
Step 5: Summary Report Generation
return json.dumps({
'status': 'success',
'active_connections': connections,
'total_connections': len(connections),
'sample_database_path': SAMPLE_DB_PATH
}, indent=2)The Dashboard Assembly:
active_connections: Complete dictionary of all connection details
total_connections: Quick count for monitoring
sample_database_path: Reference path for the sample database
Consistent formatting: Same JSON structure as other tools
Output Example:
{
"status": "success",
"active_connections": {
"sqlite:./data/sample_database.db": {
"database_path": "./data/sample_database.db",
"database_type": "sqlite",
"is_sample_database": true,
"is_connected": true,
"connection_initialized": true
},
"sqlite:./production/app.db": {
"database_path": "./production/app.db",
"database_type": "sqlite",
"is_sample_database": false,
"is_connected": true,
"connection_initialized": true
},
"postgresql:my_enterprise_db": {
"database_path": "my_enterprise_db",
"database_type": "postgresql",
"is_sample_database": false,
"is_connected": false,
"connection_initialized": true
}
},
"total_connections": 3,
"sample_database_path": "./data/sample_database.db"
}Why monitor connections? Like keeping track of open apps on your phone - you want to know what's using resources and whether everything is healthy!
The Cleanup Specialist! 🧹 - close_database_connection
@mcp.tool()
async def close_database_connection(db_path: str, db_type: str = "sqlite") -> str:
"""Close a specific database connection"""
connection_key = f"{db_type}:{db_path}"
if connection_key in connection_pool:
manager = connection_pool[connection_key]
manager.close()
del connection_pool[connection_key]
return json.dumps({
'status': 'success',
'message': f'Closed connection to {db_path}',
'database_path': db_path,
'database_type': db_type
}, indent=2)
else:
return json.dumps({
'status': 'info',
'message': f'No active connection found for {db_path}',
'database_path': db_path,
'database_type': db_type
}, indent=2)This function is like having a professional janitor who knows exactly how to clean up database connections safely and thoroughly.
Step 1: Connection Key Reconstruction
connection_key = f"{db_type}:{db_path}"The Key Builder:
Format matching: Creates the same key format used throughout the system
Consistency: Must exactly match how keys are stored in connection_pool
Examples:
"sqlite:./data/app.db"
"postgresql:enterprise_database"
Step 2: Connection Existence Check
if connection_key in connection_pool:The "Is It There?" Verification:
Dictionary lookup: Fast O(1) check for key existence
Prevents errors: Won't try to close non-existent connections
Branching logic: Different handling for found vs. not found connections
Step 3: Safe Connection Closure (Success Path)
manager = connection_pool[connection_key]
manager.close()
del connection_pool[connection_key]The Three-Step Cleanup:
manager = connection_pool[connection_key]: Get the DatabaseManager instance
manager.close(): Properly close the database connection
del connection_pool[connection_key]: Remove from pool to prevent reuse
Why this order matters:
Get first: Ensures we have a valid manager object
Close second: Properly releases database resources (file locks, sockets, etc.)
Delete last: Removes the dead reference from our pool
Step 4: Success Response Construction
return json.dumps({
'status': 'success',
'message': f'Closed connection to {db_path}',
'database_path': db_path,
'database_type': db_type
}, indent=2)The Confirmation Receipt:
Clear success status: Confirms the operation completed
Descriptive message: Human-readable confirmation
Context details: Database path and type for logging/debugging
Consistent format: Matches response structure of other tools
Step 5: Graceful "Not Found" Handling
else:
return json.dumps({
'status': 'info',
'message': f'No active connection found for {db_path}',
'database_path': db_path,
'database_type': db_type
}, indent=2)The "Nothing to Clean" Response:
Status 'info': Not an error, just informational
Helpful message: Explains why nothing was closed
Same context: Includes database details for consistency
No exception: Gracefully handles the "not found" case
What manager.close() Actually Does
def close(self):
"""Close database connection"""
if self.connection:
self.connection.close()
self.is_initialized = False
logger.info("Database connection closed")The Internal Cleanup Process:
Connection check: Only closes if connection exists
self.connection.close(): Calls the database driver's close method
State reset: Marks manager as uninitialized
Logging: Records the closure for debugging
Resource cleanup includes:
File locks released (SQLite)
Network sockets closed (PostgreSQL)
Memory buffers freed
Transaction rollbacks (if needed)
The Grand Finale - Server Startup and Configuration
if __name__ == "__main__":
print("Starting Path-Based Database Query MCP Server...")
print("Key Features:")
print("- Path-based database operations (no global state)")
print("- Connection pooling per database path")
print("- Automatic sample database detection")
print("- Support for multiple concurrent databases")
print("- PostgreSQL support (if psycopg2 installed)")
print("\nCore Tools:")
print("- query_database(db_path, query) - Query any database")
print("- insert_into_database(db_path, table, data) - Insert into any database")
print("- update_database(db_path, table, data, where) - Update any database")
print("- delete_from_database(db_path, table, where) - Delete from any database")
print("- get_database_tables(db_path) - List tables in any database")
print("- get_table_schema(db_path, table) - Get table structure")
print("- list_database_connections() - See active connections")
print("\nUsage: Always specify db_path in each function call")
print("Sample DB: './data/sample_database.db'")
print("Your DB: '/path/to/your/database.db'")
mcp.run()The Server Launch Sequence! 🚀
When you run this script, it becomes a fully-featured MCP server that other applications (including AI systems) can connect to and use.
The startup messages serve multiple purposes:
User education: Shows exactly what capabilities are available
Quick reference: Lists all tools with their basic syntax
Usage guidance: Explains the path-based approach
Examples: Provides sample database path and your own database path format
mcp.run(): This is where the magic happens! It starts the MCP server and begins listening for tool requests from AI systems or other clients.
Bringing It All Together
This is the full code for server:
This is requirements.txt file which has all the required libraries to run this code.
Output
MCP Set-Up
First, we will save the files in a folder. In our case, it is named database_mcp. We will have our database_mcp.py file, requirements.txt file, and a folder that contains all the database files.

There are some database files stored in the data folder.

Now we will open the database_mcp directory in the terminal, create a virtual environment, and then activate it.

To create a virtual environment:
On Windows:
python -m venv myenvOn Linux and macOS:
python3 -m venv myenvNow we will install uv using,
pip install uvWe will also upgrade pip using:
python.exe -m pip install --upgrade pipuv is an extremely fast Python package and project manager. It is recommended in the documentation of MCP, and we might need it while running the app in development mode.

Now we will install mcp using,
pip install mcpIt will help us create the MCP server.

Now we will install all the required libraries using,
pip install -r requirements.txt
Now we can run our code in developer mode using:
mcp dev database_mcp.pyIt will provide us with a link that we can open in the browser.

After this, we will click on Connect to connect to the server.

We can go to Tools to view all our tools and perform operations from there.

That is for the development mode. Now we will run it on the Claude Desktop. First, open Claude Desktop and go to the Developer Settings by navigating to: Menu → File → Settings → Developer.

Open the claude_desktop_config.json file.

And now add this configuration, making sure to change the paths according to your environment:
{
"mcpServers": {
"database-mcp": {
"command": "E:/workspace/python/mcp/database_mcp/myenv/Scripts/python.exe",
"args": [
"E:/workspace/python/mcp/database_mcp/database_mcp.py"
],
"env": {
"PYTHONPATH": "E:/workspace/python/mcp/database_mcp/myenv/Lib/site-packages"
}
}
}
}After doing that, close the Claude app and reopen it. Then open the Developer Settings again; the MCP server should be running. If it is not running, terminate the Claude Desktop app from the Task Manager and reopen it.

Now go to Search and Tools to check if the MCP server and its associated tools are available.

In this case, all the tools are available.

We will now perform some queries.



As we can see from the result, our MCP server is working.
Transform Your Projects with Codersarts
Enterprise-grade database servers with advanced security and performance optimization
Multi-database integration systems supporting SQLite, PostgreSQL, MySQL, and cloud databases
AI-powered query optimization and intelligent database management tools
Production-ready deployment with monitoring, logging, and automated backup systems
Don't let complex implementations slow down your innovation. Connect with Codersarts today and turn your ideas into reality!
Ready to get started? Visit Codersarts.com or reach out to our team to discuss your next project. The future of intelligent automation is here – let's build it together!




Comments