Building Custom MCP Servers for Proprietary SQL Databases in Python/TypeScript

Introduction: The Problem of the Walled Data Garden

The most valuable data in any enterprise often resides not in a modern vector store or a clean REST API, but in a battle-tested, proprietary SQL database. An AI agent that cannot access this data—be it customer history, product inventory, or financial records—is fundamentally limited in its usefulness. However, directly connecting a fleet of AI agents to a production database is an architectural anti-pattern. It would require every agent to manage database drivers, handle credentials securely, and be trusted to write safe, efficient SQL queries.

This creates a significant engineering challenge: how does a platform team safely and efficiently expose its complex SQL databases to AI agents, without forcing every agent developer to become a database administrator?

The Engineering Solution: The Custom MCP Server as a Secure Wrapper

The Model Context Protocol (MCP) solves this by promoting the creation of custom MCP Servers. A custom MCP Server is a dedicated microservice that acts as an intelligent and secure wrapper around a single data source, like a proprietary SQL database.

Its role is to be the single, trusted gateway to the database: 1. It Abstracts Complexity: The server translates high-level, semantic invoke calls from an agent (e.g., get_user_by_email) into precise, safe, and optimized SQL queries. The agent never knows the underlying table names, column structures, or SQL dialect. 2. It Centralizes Logic: It owns the logic for connecting to the database, managing connection pools, and handling database-specific errors. 3. It Provides a Standard Interface: It exposes a standard /mcp.json manifest that advertises its "tools"—the specific functions it has safely mapped to SQL queries. Any MCP-compliant agent can then discover and use these tools without custom code.

This architecture creates a clean, secure boundary between the AI agents and the core data infrastructure.

+-------+ MCP invoke() +------------------+ SQL Query +-------------+ | Agent |----------------->| Custom MCP Server|<------------->| SQL Database| +-------+ +------------------+ +-------------+ (Manages credentials & query logic)

Implementation Details

Building an MCP Server is straightforward using official libraries like mcp-server-py (for Python) and mcp-server-ts (for TypeScript), which handle the protocol boilerplate and allow developers to focus on the data logic.

Python Implementation (with mcp-server-py and SQLAlchemy)

This example wraps a PostgreSQL database using Python's leading ORM, SQLAlchemy.

Snippet 1: The Database Models First, define the data model. This is standard SQLAlchemy. ```python

database.py

from sqlalchemy import create_engine, Column, Integer, String from sqlalchemy.orm import declarative_base

Base = declarative_base()

class User(Base): tablename = 'users' id = Column(Integer, primary_key=True) name = Column(String) email = Column(String, unique=True) ```

Snippet 2: The Custom MCP Server The server class imports the model and uses the @tool decorator to expose a database function.

```python

server.py

from mcp_server_py import McpServer, tool, McpError from sqlalchemy.orm import sessionmaker from database import User, create_engine

Database connection is managed only within the server

engine = create_engine("postgresql://user:password@host:port/db") Session = sessionmaker(bind=engine)

class SqlMcpServer(McpServer): @tool(description="Fetches a single user by their email address.") def get_user_by_email(self, email: str) -> dict: """Translates the MCP call into a safe SQLAlchemy query.""" with Session() as session: user = session.query(User).filter_by(email=email).first() if not user: raise McpError(code=404, message="User not found") return {"id": user.id, "name": user.name, "email": user.email}

The library handles serving /mcp.json and the /invoke endpoint

server = SqlMcpServer(name="customer-sql-db") server.serve(port=8001) ```

TypeScript Implementation (with mcp-server-ts and Sequelize)

The same pattern applies in the Node.js ecosystem with a library like Sequelize.

Snippet 1: The Database Model ```typescript // models/User.ts import { Model, DataTypes } from 'sequelize'; import sequelize from '../database';

class User extends Model { public id!: number; public name!: string; public email!: string; }

User.init({ ... }, { sequelize, modelName: 'User' }); export default User; **Snippet 2: The Custom `MCP Server`**typescript // server.ts import { McpServer, tool, McpError } from 'mcp-server-ts'; import User from './models/User';

class SqlMcpServer extends McpServer { @tool({ description: "Fetches a single user by their email address." }) public async getUser(params: { email: string }): Promise { const user = await User.findOne({ where: { email: params.email } }); if (!user) { throw new McpError(404, "User not found"); } return user.toJSON(); } }

const server = new SqlMcpServer('customer-sql-db'); server.serve(8001); ```

Performance & Security Considerations

Performance: * Connection Pooling: A critical responsibility of the MCP Server is to manage a persistent database connection pool. This is a standard feature in libraries like SQLAlchemy and Sequelize and is essential for performance, as it avoids the high overhead of establishing a new database connection for every incoming agent request. * Centralized Caching & Optimization: The MCP Server is the ideal layer to implement caching. Results of frequent and expensive queries can be cached in memory (e.g., using Redis), dramatically reducing load on the database. This is also where data engineers can centrally optimize query performance, with all agents benefiting instantly.

Security: This architecture provides a robust, multi-layer security model. * Credential Isolation: The database connection string, username, and password exist only within the secure environment of the MCP Server. They are never exposed to the AI agents or the MCP Client. * SQL Injection Prevention: This is the most critical security benefit. By using a mature ORM, the MCP Server inherently uses parameterized queries. The agent sends structured data (e.g., { "email": "user@example.com" }), not raw SQL strings. This makes traditional SQL injection attacks impossible. * Curated API Surface: The @tool decorator pattern allows developers to expose only a specific, safe subset of database operations. You can create a tool for get_user but deliberately not create one for delete_all_users, providing a much safer, curated API surface than direct database access would.

Conclusion: The ROI of a Database Wrapper

Building a custom MCP Server is the standard, enterprise-grade pattern for connecting AI agents to legacy and proprietary SQL databases. It creates a powerful abstraction layer that delivers a clear return on investment.

This pattern provides a robust, secure, and scalable bridge between an organization's foundational data assets and its most advanced AI capabilities.