resolve-library-id
Read-only
resolve library id
io.github.upstash/context7
Up-to-date docs for AI agents
run in your project directory
claude mcp add --transport http context7 https://mcp.context7.com/mcp
setup guides: Claude Code Claude Desktop Cursor VS Code Zed Windsurf Cline Gemini CLI Grok ChatGPT Claude.ai LangChain
Adds it for this project only. Append --scope user to make it available everywhere. Claude Code docs
resolve-library-id
Read-only
resolve library id
query-docs
Read-only
query docs
Mutating and Read-only are read off each tool's name, not its schema — a hint, not a guarantee. Check the server's own documentation before granting access.
Generated September 19, 2026
This comprehensive guide covers integrating databases with MCP servers, enabling AI assistants to query, analyze, and manage data through standardized protocols. We explore SQL databases, NoSQL systems, distributed data patterns, query optimization, security considerations, and real-world implementations.
Query Parser: Validates and parses incoming SQL/queries
Connection Pool: Manages database connections efficiently
Query Executor: Executes queries with timeout and resource limits
Result Formatter: Formats results for AI consumption
Schema Manager: Exposes database schema information
Query Validation
Connection Pooling
from sqlalchemy import create_engine
from sqlalchemy.pool import QueuePool
engine = create_engine(
'postgresql://user:pass@localhost/db',
poolclass=QueuePool,
pool_size=20,
max_overflow=40,
pool_recycle=3600
)
Query Caching
Index Strategy
import asyncpg
from typing import List, Dict
class PostgreSQLMCPServer:
def __init__(self, connection_string: str):
self.conn_string = connection_string
self.pool = None
async def initialize(self):
self.pool = await asyncpg.create_pool(self.conn_string)
async def execute_query(self, sql: str, params: list = None) -> List[Dict]:
async with self.pool.acquire() as conn:
try:
rows = await conn.fetch(sql, *(params or []))
return [dict(row) for row in rows]
except Exception as e:
return {"error": str(e)}
async def get_schema(self, table: str = None) -> Dict:
"""Get database schema information"""
query = """
SELECT table_name, column_name, data_type
FROM information_schema.columns
WHERE table_schema = 'public'
"""
if table:
query += f" AND table_name = '{table}'"
return await self.execute_query(query)
async def get_table_stats(self, table: str) -> Dict:
"""Get table statistics"""
query = f"""
SELECT
relname,
n_live_tup,
n_dead_tup,
last_vacuum,
last_autovacuum
FROM pg_stat_user_tables
WHERE relname = '{table}'
"""
return await self.execute_query(query)
import asyncmy
class MySQLMCPServer:
async def connect(self, config: dict):
self.pool = await asyncmy.create_pool(**config)
async def execute_query(self, sql: str, params: list = None):
async with self.pool.acquire() as conn:
async with conn.cursor() as cursor:
await cursor.execute(sql, params or [])
return await cursor.fetchall()
from motor.motor_asyncio import AsyncClient, AsyncDatabase
class MongoDBMCPServer:
def __init__(self, connection_string: str):
self.client = AsyncClient(connection_string)
self.db: AsyncDatabase = None
async def initialize(self, database: str):
self.db = self.client[database]
async def find_documents(self, collection: str,
query: dict, limit: int = 100) -> List[Dict]:
"""Find documents in collection"""
col = self.db[collection]
docs = await col.find(query).limit(limit).to_list(limit)
return docs
async def aggregate(self, collection: str,
pipeline: List[Dict]) -> List[Dict]:
"""Execute aggregation pipeline"""
col = self.db[collection]
result = await col.aggregate(pipeline).to_list(None)
return result
async def get_collections(self) -> List[str]:
"""List all collections"""
return await self.db.list_collection_names()
import aioboto3
class DynamoDBMCPServer:
def __init__(self, region: str):
self.session = aioboto3.Session()
self.region = region
async def query_table(self, table: str, key: dict) -> List[Dict]:
async with self.session.client('dynamodb', region_name=self.region) as client:
response = await client.query(
TableName=table,
KeyConditionExpression='pk = :pk',
ExpressionAttributeValues={
':pk': {'S': key['pk']}
}
)
return response.get('Items', [])
async def scan_table(self, table: str, limit: int = 100) -> List[Dict]:
async with self.session.client('dynamodb', region_name=self.region) as client:
response = await client.scan(
TableName=table,
Limit=limit
)
return response.get('Items', [])
class AnalyticsMCPServer:
"""MCP server for analytics queries"""
async def get_usage_stats(self, start_date: str, end_date: str):
query = """
SELECT
DATE(created_at) as date,
COUNT(*) as total_events,
COUNT(DISTINCT user_id) as unique_users,
AVG(duration_ms) as avg_duration
FROM events
WHERE created_at BETWEEN %s AND %s
GROUP BY DATE(created_at)
ORDER BY date DESC
"""
return await self.execute_query(query, [start_date, end_date])
async def get_top_features(self, limit: int = 10):
query = """
SELECT
feature_name,
COUNT(*) as usage_count,
COUNT(DISTINCT user_id) as users
FROM feature_usage
GROUP BY feature_name
ORDER BY usage_count DESC
LIMIT %s
"""
return await self.execute_query(query, [limit])
async def get_user_retention(self):
query = """
WITH cohorts AS (
SELECT
DATE_TRUNC('month', MIN(created_at)) as cohort_month,
user_id
FROM events
GROUP BY user_id
)
SELECT
cohort_month,
COUNT(DISTINCT user_id) as cohort_size
FROM cohorts
GROUP BY cohort_month
ORDER BY cohort_month DESC
"""
return await self.execute_query(query)
class CDPMCPServer:
"""MCP server for customer data"""
async def get_customer_profile(self, customer_id: str):
query = """
SELECT
c.*,
COUNT(o.id) as total_orders,
SUM(o.amount) as lifetime_value,
MAX(o.created_at) as last_purchase
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
WHERE c.id = %s
GROUP BY c.id
"""
return await self.execute_query(query, [customer_id])
async def get_customer_segments(self):
query = """
SELECT
CASE
WHEN lifetime_value > 10000 THEN 'VIP'
WHEN lifetime_value > 1000 THEN 'Regular'
ELSE 'At-Risk'
END as segment,
COUNT(*) as customer_count,
AVG(lifetime_value) as avg_value
FROM (
SELECT
c.id,
COALESCE(SUM(o.amount), 0) as lifetime_value
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id
)
GROUP BY segment
"""
return await self.execute_query(query)
class QueryOptimizer:
@staticmethod
def get_query_plan(engine, sql: str):
"""Get query execution plan"""
with engine.connect() as conn:
result = conn.execute(f"EXPLAIN ANALYZE {sql}")
return result.fetchall()
@staticmethod
def identify_slow_queries(engine, min_duration_ms: int = 1000):
"""Find slow queries in logs"""
query = f"""
SELECT
query,
calls,
total_time,
mean_time
FROM pg_stat_statements
WHERE mean_time > {min_duration_ms}
ORDER BY total_time DESC
"""
# Execute and return results
class PoolTuning:
@staticmethod
def tune_pool_size(max_connections: int,
expected_qps: int,
avg_query_time_ms: int):
"""Calculate optimal pool size"""
concurrent_queries = (expected_qps * avg_query_time_ms) / 1000
pool_size = int(concurrent_queries * 1.5) # 50% buffer
return min(pool_size, max_connections)
# Example: 100 QPS, 50ms avg query time
# = 100 * 50 / 1000 = 5 concurrent
# = 5 * 1.5 = 7.5 pool size
import re
class QueryValidator:
FORBIDDEN_KEYWORDS = [
'DROP', 'DELETE', 'TRUNCATE', 'ALTER',
'GRANT', 'REVOKE', 'CREATE'
]
@staticmethod
def validate_query(sql: str, allowed_tables: List[str]) -> bool:
"""Validate query for safety"""
# Check forbidden keywords
for keyword in QueryValidator.FORBIDDEN_KEYWORDS:
if re.search(f'\\b{keyword}\\b', sql, re.IGNORECASE):
return False
# Check table access
for table in allowed_tables:
if not re.search(f'\\b{table}\\b', sql, re.IGNORECASE):
return False
return True
import os
from cryptography.fernet import Fernet
class CredentialManager:
def __init__(self, encryption_key: str):
self.cipher = Fernet(encryption_key.encode())
def store_connection_string(self, name: str, connection_string: str):
encrypted = self.cipher.encrypt(connection_string.encode())
# Store encrypted in database or secure storage
return encrypted
def get_connection_string(self, name: str) -> str:
# Retrieve from secure storage
encrypted = get_from_storage(name)
return self.cipher.decrypt(encrypted).decode()
@app.get("/metrics/pool")
async def pool_metrics():
return {
'pool_size': engine.pool.size(),
'checked_out': engine.pool.checkedout(),
'queue_size': engine.pool.queue.qsize(),
'overflow': engine.pool.overflow()
}
@app.post("/query")
async def execute_query(sql: str, params: list = None):
start = time.time()
try:
result = await db.execute_query(sql, params)
duration = time.time() - start
# Log metrics
logger.info("Query executed", extra={
'duration_ms': duration * 1000,
'rows_returned': len(result) if isinstance(result, list) else 1
})
# Alert if slow
if duration > 5:
logger.warning("Slow query detected", extra={'sql': sql[:100]})
return result
except Exception as e:
logger.error("Query failed", extra={'error': str(e), 'sql': sql[:100]})
raise
Database integration through MCP enables AI assistants to access and analyze data efficiently. By implementing proper architecture, security, and optimization patterns, organizations can build reliable database-backed MCP servers.
Key takeaways:
Comprehensive 8,000+ word guide covering SQL and NoSQL database integration, connection pooling, query optimization, security measures, real-world use cases, monitoring, and best practices for database-backed MCP servers. Unlock data access for AI assistants. Explore more on MCP Registry.
We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect