Disponibilizando recursos de banco de dados via MCP
Crie recursos MCP que disponibilizem dinamicamente o conteúdo do banco de dados, exponha ferramentas de consulta que o modelo possa chamar e implemente paginação para grandes conjuntos de resultados.
Disponibilizando recursos de banco de dados via MCP é uma aula grátis de AI Engineering Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de AI Engineering Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de AI Engineering Academy inclui 4 aulas no total.
Por que os bancos de dados precisam do MCP
Os bancos de dados estão entre as fontes de dados mais valiosas para assistentes de IA — mas o acesso direto ao banco de dados é perigoso demais para ser fornecido diretamente a um LLM. Um servidor MCP atua como um proxy controlado entre a IA e seu banco de dados, expondo apenas os dados e as operações que você autorizar explicitamente, com validação, limitação de taxa e auditoria incorporadas.
Projetando o esquema MCP do banco de dados
Antes de escrever o código, decida o que a IA poderá ver e fazer. Defina: quais tabelas poderão ser lidas, quais ferramentas realizarão operações de escrita, se houver, quais dados deverão ser filtrados pelo contexto do usuário e quais colunas deverão ser ocultadas, como senhas e PII. Essa etapa de projeto evita a exposição acidental de dados.
- Ferramentas de leitura:
list_records,get_record,search_records - Ferramentas de escrita (opcionais e restritas):
create_record,update_record - Proibido: execução de SQL bruto, alterações de esquema e tabelas do sistema
Configurando a conexão com o banco de dados
Use asyncpg (PostgreSQL) ou aiosqlite (SQLite) para o acesso assíncrono ao banco de dados no seu servidor MCP. Crie um pool de conexões na inicialização para que cada chamada de ferramenta não precise abrir uma nova conexão. Armazene a URL do banco de dados em uma variável de ambiente, nunca no código.
import asyncpg
import os
from mcp.server import Server
from mcp import types
import asyncio
app = Server('database-mcp-server')
pool = None # Global connection pool
async def get_pool():
global pool
if pool is None:
pool = await asyncpg.create_pool(
os.environ['DATABASE_URL'],
min_size=2,
max_size=10,
command_timeout=30
)
return pool
# Initialize pool at startup
async def startup():
await get_pool()
print('Database pool initialized', flush=False) # stderr only via loggerListando registros como uma ferramenta
Uma ferramenta list_records com filtragem e paginação permite que a IA navegue pelo conteúdo do banco de dados sem executar SQL arbitrário. Aceite parâmetros de filtro, crie uma consulta parametrizada e sempre aplique um limite de linhas. Retorne os resultados como uma string formatada que o modelo possa ler e usar como referência em sua resposta.
@app.list_tools()
async def list_tools():
return [
types.Tool(
name='list_products',
description='List products from the catalog with optional filtering by category and price range.',
inputSchema={
'type': 'object',
'properties': {
'category': {'type': 'string', 'description': 'Filter by product category.'},
'max_price': {'type': 'number', 'description': 'Maximum price in USD.'},
'limit': {'type': 'integer', 'default': 20, 'maximum': 100}
},
'required': []
}
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict):
if name == 'list_products':
return await list_products_handler(arguments)
async def list_products_handler(args: dict):
pool = await get_pool()
query = 'SELECT id, name, category, price, stock FROM products WHERE 1=1'
params = []
if 'category' in args:
params.append(args['category'])
query += f' AND category = ${len(params)}'
if 'max_price' in args:
params.append(args['max_price'])
query += f' AND price <= ${len(params)}'
limit = min(args.get('limit', 20), 100)
params.append(limit)
query += f' ORDER BY name LIMIT ${len(params)}'
async with pool.acquire() as conn:
rows = await conn.fetch(query, *params)
if not rows:
return [types.TextContent(type='text', text='No products found matching your criteria.')]
lines = ['id | name | category | price | stock']
lines += [f'{r["id"]} | {r["name"]} | {r["category"]} | ${r["price"]} | {r["stock"]}' for r in rows]
return [types.TextContent(type='text', text='\n'.join(lines))]Expondo linhas do banco de dados como recursos
Registros individuais do banco de dados podem ser expostos como recursos MCP usando URIs estruturadas, como db://products/42. Os recursos permitem que o cliente de IA armazene em cache e faça referência a registros específicos sem chamar uma ferramenta a cada vez. Implemente list_resources para retornar os registros mais relevantes e read_resource para buscar um registro por URI.
@app.list_resources()
async def list_resources():
pool = await get_pool()
async with pool.acquire() as conn:
# List most recently updated products as resources
rows = await conn.fetch(
'SELECT id, name FROM products ORDER BY updated_at DESC LIMIT 50'
)
return [
types.Resource(
uri=f'db://products/{row["id"]}',
name=row['name'],
description=f'Product record for {row["name"]}',
mimeType='application/json'
)
for row in rows
]
@app.read_resource()
async def read_resource(uri: str) -> str:
import json
if uri.startswith('db://products/'):
product_id = int(uri.split('/')[-1])
pool = await get_pool()
async with pool.acquire() as conn:
row = await conn.fetchrow(
'SELECT id, name, category, price, description, stock FROM products WHERE id = $1',
product_id
)
if row is None:
raise ValueError(f'Product {product_id} not found')
return json.dumps(dict(row), default=str)
raise ValueError(f'Unknown resource URI: {uri}')Paginação para grandes conjuntos de resultados
As tabelas de bancos de dados frequentemente têm milhares de linhas. Implemente paginação baseada em cursor nas suas ferramentas para que a IA possa solicitar a próxima página de resultados. Use last_id como um cursor estável, mais confiável do que a paginação baseada em deslocamento, que pode ignorar linhas quando há inserções.
async def paginated_list(table: str, limit: int = 20, after_id: int = 0) -> tuple:
'''Returns (rows, has_more, next_cursor).'''
pool = await get_pool()
async with pool.acquire() as conn:
rows = await conn.fetch(
f'SELECT * FROM {table} WHERE id > $1 ORDER BY id LIMIT $2',
after_id,
limit + 1 # Fetch one extra to detect if more pages exist
)
has_more = len(rows) > limit
rows = rows[:limit]
next_cursor = rows[-1]['id'] if rows else None
return rows, has_more, next_cursor
# In your tool result, include pagination info:
# 'Showing 20 products. To see the next page, call list_products with after_id=120'Ferramenta de pesquisa de texto completo
Adicione uma ferramenta de pesquisa que use a pesquisa de texto completo do PostgreSQL, permitindo que a IA encontre registros por meio de consultas em linguagem natural, em vez de correspondências exatas de campos. Isso é especialmente útil para catálogos de produtos, documentação e bases de conhecimento.
@app.list_tools()
async def list_tools():
return [
types.Tool(
name='search_products',
description='Full-text search across product names and descriptions. Use for finding products by natural language, not by category filter.',
inputSchema={
'type': 'object',
'properties': {
'query': {'type': 'string', 'description': 'Natural language search query.'},
'limit': {'type': 'integer', 'default': 10, 'maximum': 50}
},
'required': ['query']
}
)
]
async def search_products_handler(args: dict):
pool = await get_pool()
async with pool.acquire() as conn:
rows = await conn.fetch(
'''SELECT id, name, price, ts_rank(search_vector, query) AS rank
FROM products, plainto_tsquery('english', $1) AS query
WHERE search_vector @@ query
ORDER BY rank DESC
LIMIT $2''',
args['query'],
args.get('limit', 10)
)
if not rows:
return [types.TextContent(type='text', text=f'No products found matching "{args["query"]}". ')]
lines = [f'{r["id"]}: {r["name"]} — ${r["price"]}' for r in rows]
return [types.TextContent(type='text', text='\n'.join(lines))]Operações de escrita com confirmação
Se o seu servidor MCP permitir operações de escrita, implemente um padrão de confirmação em duas etapas. A primeira chamada da ferramenta retorna uma prévia do que será alterado. Uma segunda ferramenta confirm_action(action_id) executa a alteração. Isso impede que a IA faça alterações irreversíveis a partir de um único prompt sem o conhecimento do usuário.
import uuid
pending_actions = {} # In-memory store; use Redis in production
async def create_order_preview(args: dict) -> list:
action_id = str(uuid.uuid4())[:8]
pending_actions[action_id] = {'type': 'create_order', 'data': args}
return [types.TextContent(
type='text',
text=f'Preview: Create order for customer {args["customer_id"]} with {len(args["items"])} items, '
f'total ${args["total"]}. Confirm with: confirm_action(action_id="{action_id}")')
]
async def confirm_action_handler(args: dict) -> list:
action_id = args.get('action_id')
action = pending_actions.pop(action_id, None)
if not action:
return [types.TextContent(type='text', text='Action expired or not found.')]
# Execute the pending action
result = await execute_action(action)
return [types.TextContent(type='text', text=f'Done: {result}')]Filtrando dados confidenciais
Nunca exponha colunas confidenciais, como senhas, chaves de API ou PII, a menos que o servidor precise explicitamente delas para o caso de uso empresarial. Use cláusulas SELECT que excluam colunas confidenciais e aplique filtragem em nível de linha com base nas permissões do usuário autenticado. Seu servidor MCP é a última linha de defesa antes que os dados cheguem à IA.
async def get_safe_customer(customer_id: int) -> dict:
'''Fetch customer data with sensitive fields excluded.'''
pool = await get_pool()
async with pool.acquire() as conn:
row = await conn.fetchrow(
'''SELECT id, name, created_at, country, tier
FROM customers
WHERE id = $1
-- NEVER select: password_hash, api_key, full_address, payment_method_id
''',
customer_id
)
return dict(row) if row else {}Registro de auditoria do acesso ao banco de dados
Todo acesso ao banco de dados por meio do seu servidor MCP deve ser registrado para fins de segurança e conformidade. Registre a ferramenta chamada, os argumentos fornecidos, as linhas retornadas, o horário e qualquer contexto de sessão ou usuário disponível. Essa trilha de auditoria permite detectar padrões anômalos e atender aos requisitos de conformidade.
import logging
import sys
import time
audit_logger = logging.getLogger('mcp.audit')
audit_logger.setLevel(logging.INFO)
handler = logging.StreamHandler(sys.stderr)
audit_logger.addHandler(handler)
async def audited_tool_call(name: str, arguments: dict, session_id: str = None) -> list:
start = time.time()
result = await call_tool(name, arguments)
elapsed_ms = round((time.time() - start) * 1000)
audit_logger.info({
'event': 'tool_call',
'tool': name,
'args': {k: v for k, v in arguments.items() if k != 'password'},
'result_count': len(result),
'elapsed_ms': elapsed_ms,
'session_id': session_id
})
return resultTestando seu servidor MCP de banco de dados
Teste seu servidor MCP de banco de dados em três níveis: testes unitários para funções de consulta individuais, usando um banco de dados de teste ou simulações; testes de integração que iniciem o servidor MCP completo e chamem ferramentas por meio do cliente do SDK; e testes de ponta a ponta que verifiquem se o Claude Desktop vê as ferramentas e as chama corretamente. Os testes automatizados detectam regressões antes que elas cheguem à IA.
Verificação rápida
Teste sua compreensão sobre a exposição de recursos de banco de dados por meio do MCP.
Recapitulação da lição
Nesta lição, você aprendeu que: os servidores MCP de banco de dados atuam como proxies controlados, com ferramentas de consulta explícitas e operações somente de leitura por padrão; as linhas do banco de dados podem ser expostas como recursos MCP com URIs estruturadas para que a IA faça referência a elas; e as operações de escrita devem usar um padrão de prévia seguida de confirmação para impedir alterações não intencionais. A seguir, protegeremos nosso servidor MCP com autenticação OAuth 2.0 e validação de entradas.
Aprenda Python com um tutor de IA — grátis
Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.
- Cursos
- 30
- Aulas
- 120
Perguntas Frequentes
A aula “Disponibilizando recursos de banco de dados via MCP” é grátis?
Sim — o texto completo de “Disponibilizando recursos de banco de dados via MCP” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de AI Engineering Academy, atualize para CoddyKit PRO. O curso de AI Engineering Academy inclui 4 aulas no total.
O que vou aprender em “Disponibilizando recursos de banco de dados via MCP”?
Crie recursos MCP que disponibilizem dinamicamente o conteúdo do banco de dados, exponha ferramentas de consulta que o modelo possa chamar e implemente paginação para grandes conjuntos de resultados. Você pratica AI Engineering Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar AI Engineering Academy?
Nenhuma experiência prévia é necessária. AI Engineering Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.
Quanto tempo leva a aula “Disponibilizando recursos de banco de dados via MCP”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de AI Engineering Academy?
Sim. Cada aula de AI Engineering Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- O que é MCP e por que ele importa
- Criando seu primeiro servidor MCP
- Disponibilizando recursos de banco de dados via MCP
- Segurança e autenticação no MCP