Skip to content

Kaique-010/mcp_postgres

Repository files navigation

MCP Agent DB Uma ferramenta poderosa para consulta de bases de dados usando linguagem natural com agente inteligente.

Características

  • Agente Inteligente: Converte perguntas em linguagem natural para SQL

  • Consultas Avancadas: Suporte para consultas complexas com joins e agregacoes

  • Geracao de Graficos: Integracao com ferramentas MCP para visualizacao

  • Multi-empresa: Suporte para multiplas empresas e filiais

  • Cache Inteligente: Sistema de cache para otimizar performance

  • Memoria de Conversacao: Mantem contexto entre consultas

🌐 API REST: Endpoints prontos para integracao web e mobile

📦 Instalacao bash Copiar Editar pip install mcp-agent-db 🔧 Configuracao Configure as variaveis de ambiente:

.env (criar um arquivo .env na raiz )

DATABASE_URL=postgresql://user:password@localhost:5432/database GOOGLE_API_KEY=sua_chave_google_ai MCP_SERVER_URL=http://localhost:3000 Configure o banco de dados no settings.py:

python com django DATABASES = { 'casaa': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'seu_banco', 'USER': 'usuario', 'PASSWORD': 'senha', 'HOST': 'localhost', 'PORT': '5432', } }

PAra gerar o schema do banco de dados python

python gerar_schema.py

gerar_schema.py

import psycopg2 import sqlite3 import os import json from typing import Dict, List, Any

SCHEMA_DIR = "schemas"

Configurações de exemplo para diferentes tipos de banco

DATABASES = { "casaa": { "tipo": "postgres", "host": "localhost", "port": 5432, "user": "postgres", "password": "@spartacus201@", "dbname": "casaa" }, "spartacus": { "tipo": "postgres", "host": "localhost", "port": 5432, "user": "postgres", "password": "postgres", "dbname": "spartacus" }, "cliente_sqlite": { "tipo": "sqlite", "caminho": "exemplo.db" }, "cliente_teste": { "tipo": "sqlite", "caminho": "teste.db" }, "cliente_mysql": { "tipo": "mysql", "host": "localhost", "port": 3306, "user": "root", "password": "senha", "dbname": "cliente_db" } }

def conectar_db(config: Dict[str, Any]): """Conecta ao banco baseado no tipo configurado""" tipo = config.get("tipo", "postgres")

if tipo == "postgres":
    return psycopg2.connect(
        host=config["host"],
        port=config["port"],
        user=config["user"],
        password=config["password"],
        dbname=config["dbname"]
    )
elif tipo == "sqlite":
    return sqlite3.connect(config["caminho"])
elif tipo == "mysql":
    try:
        import mysql.connector
        return mysql.connector.connect(
            host=config["host"],
            port=config["port"],
            user=config["user"],
            password=config["password"],
            database=config["dbname"]
        )
    except ImportError:
        raise ImportError("mysql-connector-python não instalado. Execute: pip install mysql-connector-python")
else:
    raise ValueError(f"Tipo de banco não suportado: {tipo}")

def extrair_schema_postgres(conexao): """Extrai schema do PostgreSQL""" cursor = conexao.cursor() cursor.execute(""" SELECT t.table_name, c.column_name, c.data_type, c.is_nullable, c.column_default, CASE WHEN pk.column_name IS NOT NULL THEN true ELSE false END as is_primary_key FROM information_schema.tables t JOIN information_schema.columns c ON t.table_name = c.table_name LEFT JOIN ( SELECT ku.table_name, ku.column_name FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage ku ON tc.constraint_name = ku.constraint_name WHERE tc.constraint_type = 'PRIMARY KEY' ) pk ON c.table_name = pk.table_name AND c.column_name = pk.column_name WHERE t.table_schema = 'public' AND t.table_type = 'BASE TABLE' ORDER BY t.table_name, c.ordinal_position; """) return cursor.fetchall()

def extrair_schema_sqlite(conexao): """Extrai schema do SQLite""" cursor = conexao.cursor()

# Obter lista de tabelas
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
tabelas = cursor.fetchall()

resultado = []
for (tabela,) in tabelas:
    # Obter informações das colunas
    cursor.execute(f"PRAGMA table_info({tabela});")
    colunas = cursor.fetchall()

    for col in colunas:
        cid, nome, tipo, notnull, default_value, pk = col
        resultado.append((
            tabela,
            nome,
            tipo,
            'NO' if notnull else 'YES',  # is_nullable
            default_value,
            bool(pk)  # is_primary_key
        ))

return resultado

def extrair_schema(conexao, tipo_banco="postgres"): """Extrai schema baseado no tipo de banco""" if tipo_banco == "postgres": dados = extrair_schema_postgres(conexao) elif tipo_banco == "sqlite": dados = extrair_schema_sqlite(conexao) else: raise ValueError(f"Tipo de banco não suportado para extração: {tipo_banco}")

schema = {}
for row in dados:
    table_name, column_name, data_type, is_nullable, column_default, is_primary_key = row

    if table_name not in schema:
        schema[table_name] = {
            "colunas": [],
            "descricao": f"Tabela {table_name}"
        }

    schema[table_name]["colunas"].append({
        "nome": column_name,
        "tipo": data_type,
        "nullable": is_nullable == 'YES',
        "default": column_default,
        "primary_key": is_primary_key
    })

return schema

def salvar_schema(slug, schema): os.makedirs(SCHEMA_DIR, exist_ok=True) caminho = os.path.join(SCHEMA_DIR, f"{slug}.json") with open(caminho, "w", encoding="utf-8") as f: json.dump(schema, f, indent=2, ensure_ascii=False) print(f"✅ Schema salvo em {caminho}")

Dicionário de campos chave por contexto

CAMPOS_CHAVE = { "clientes": { "tabelas": ["entidades"], "campos_identificacao": ["enti_clie", "enti_nome", "enti_fant"], "campos_contato": ["enti_fone", "enti_celu", "enti_emai"], "campos_endereco": ["enti_ende", "enti_cida", "enti_esta", "enti_cep"], "campos_documento": ["enti_cnpj", "enti_cpf"] }, "vendedores": { "tabelas": ["entidades"], "campos_identificacao": ["pedi_vend", "enti_nome", "enti_titpo_enti"],

},
"produtos": {
    "tabelas": ["produtos"],
    "campos_identificacao": ["prod_codi", "prod_nome"],
    "campos_preco": ["prod_prec", "prod_cust"],
    "campos_estoque": ["sapr_sald"]
},
"pedidos": {
    "tabelas": ["pedidosvenda"],
    "campos_identificacao": ["pedi_nume", "pedi_data"],
    "campos_cliente": ["pedi_forn"],
    "campos_vendedor": ["pedi_vend"],
    "campos_valor": ["pedi_tota"]
}

}

def adicionar_metadados_schema(schema_dict): """Adiciona metadados sobre campos chave ao schema""" schema_dict["_metadados"] = { "campos_chave": CAMPOS_CHAVE, "descricao": "Metadados para facilitar consultas do agente", "exemplos_consultas": { "clientes": [ "Campos principais: enti_clie (código), enti_nome (nome), enti_fant (fantasia)", "IMPORTANTE: Para pedidos/vendas, NÃO filtrar por enti_tipo_enti = 'CL'" ], "vendedores": [

            "USAR: SELECT enti_nome FROM entidades WHERE enti_tipo_enti = 'VE'",
            "IMPORTANTE: Para pedidos/vendas, NÃO filtrar por enti_tipo_enti = 'VE'"
        ],
        "produtos": [
            "Para buscar produtos: SELECT prod_nome FROM produtos",
            "Para estoque: SELECT sapr_sald FROM saldosprodutos"
        ],
        "pedidos": [
            "Para buscar pedidos: SELECT pedi_nume, pedi_data FROM pedidosvenda",
            "Relacionar com cliente: JOIN entidades ON pedi_forn = enti_clie",
            "Relacionar com produto: JOIN produtos ON pedi_prod = prod_codi",
            "Relacionar com vendedor: JOIN entidades ON pedi_vend = enti_clie",
            "CRÍTICO: NUNCA filtrar por enti_tipo_enti em consultas de pedidos",
            "SEMPRE incluir enti_tipo_enti no SELECT para mostrar o tipo da entidade",
            "Uma entidade pode ser CL (cliente), FO (fornecedor), VE (vendedor) ou AM (ambos)"
        ],
        "regras_filtros": [
            "NUNCA usar WHERE enti_tipo_enti = 'CL' em consultas de pedidos/vendas",
            "NUNCA usar WHERE enti_tipo_enti = 'FO' em consultas de pedidos/vendas",
            "SEMPRE incluir enti_tipo_enti no SELECT para discriminar tipos",
            "Entidades podem ter múltiplos tipos simultaneamente",
            "Para agrupar por tipo, usar GROUP BY enti_tipo_enti (sem WHERE)"
        ]
    }
}
return schema_dict

def gerar_schema(slug): """Gera o schema para um banco específico""" print(f"🔄 Gerando schema para: {slug}")

try:
    # Conectar ao banco - CORRIGIDO
    config = DATABASES.get(slug)
    if not config:
        print(f"❌ Configuração não encontrada para slug: {slug}")
        return None

    conn = conectar_db(config)
    if not conn:
        return None

    # Extrair schema
    schema = extrair_schema(conn, config.get("tipo", "postgres"))

    # Adicionar metadados
    schema = adicionar_metadados_schema(schema)

    # Salvar schema
    salvar_schema(slug, schema)

    conn.close()
    print(f"✅ Schema gerado com sucesso: {slug}")
    return schema  # Retornar o schema em vez de True

except Exception as e:
    print(f"❌ Erro ao gerar schema: {e}")
    return None

def criar_banco_exemplo_sqlite(): """Cria um banco SQLite de exemplo para testar""" print("🛠️ Criando banco SQLite de exemplo...")

conn = sqlite3.connect("exemplo.db")
cursor = conn.cursor()

# Criar tabelas de exemplo
cursor.executescript("""
    DROP TABLE IF EXISTS pedidos;
    DROP TABLE IF EXISTS clientes;
    DROP TABLE IF EXISTS produtos;

    CREATE TABLE clientes (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        nome TEXT NOT NULL,
        email TEXT UNIQUE,
        data_cadastro DATETIME DEFAULT CURRENT_TIMESTAMP
    );

    CREATE TABLE produtos (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        nome TEXT NOT NULL,
        preco DECIMAL(10,2),
        categoria TEXT,
        ativo BOOLEAN DEFAULT 1
    );

    CREATE TABLE pedidos (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        cliente_id INTEGER,
        produto_id INTEGER,
        quantidade INTEGER DEFAULT 1,
        valor_total DECIMAL(10,2),
        data_pedido DATETIME DEFAULT CURRENT_TIMESTAMP,
        status TEXT DEFAULT 'pendente'
    );

    -- Dados de exemplo
    INSERT INTO clientes (nome, email) VALUES
        ('João Silva', 'joao@email.com'),
        ('Maria Santos', 'maria@email.com');

    INSERT INTO produtos (nome, preco, categoria) VALUES
        ('Notebook', 2500.00, 'eletrônicos'),
        ('Mouse', 50.00, 'acessórios');

    INSERT INTO pedidos (cliente_id, produto_id, quantidade, valor_total) VALUES
        (1, 1, 1, 2500.00),
        (2, 2, 2, 100.00);
""")

conn.commit()
conn.close()
print("✅ Banco SQLite criado: exemplo.db")

def testar_sistema_completo(): """Testa o sistema completo de geração de schema""" print("🚀 TESTANDO SISTEMA DE GERAÇÃO DE SCHEMA") print("=" * 50)

# Criar banco de exemplo
criar_banco_exemplo_sqlite()

# Gerar schema
schema = gerar_schema("casaa")

if not schema:
    print("❌ Falha ao gerar schema")
    return None

# Mostrar resultado
print("\n📋 SCHEMA GERADO:")
tabelas_mostradas = 0
for tabela, info in schema.items():
    if tabela.startswith('_'):  # Pular metadados na exibição
        continue

    if tabelas_mostradas >= 5:  # Mostrar apenas as primeiras 5 tabelas
        total_tabelas = len([t for t in schema.keys() if not t.startswith('_')])
        print(f"\n... e mais {total_tabelas - 5} tabelas")
        break

    print(f"\n🔹 {tabela}:")
    colunas = info.get('colunas', [])
    for i, coluna in enumerate(colunas):
        if i >= 5:  # Mostrar apenas as primeiras 5 colunas
            print(f"  ... e mais {len(colunas) - 5} colunas")
            break
        pk = " (PK)" if coluna['primary_key'] else ""
        nullable = "NULL" if coluna['nullable'] else "NOT NULL"
        print(f"  - {coluna['nome']}: {coluna['tipo']} {nullable}{pk}")

    tabelas_mostradas += 1

# Mostrar metadados se existirem
if '_metadados' in schema:
    print("\n🔑 METADADOS INCLUÍDOS:")
    metadados = schema['_metadados']
    campos_chave = metadados.get('campos_chave', {})
    for contexto, info in campos_chave.items():
        print(f"  📋 {contexto}: {', '.join(info['tabelas'])}")
    print(f"  ✅ Total de {len(campos_chave)} contextos com metadados")

return schema

if name == "main": # Testar sistema completo testar_sistema_completo()

Iniciar servidor

uvicorn.run(app, host="0.0.0.0", port=8000) 💻 Uso Basico Como Biblioteca Python python from mcp_agent_db import consultar_banco_dados

Consulta simples sempre com o slug do banco

resultado = consultar_banco_dados("TOP 10 clientes que mais compraram", "casaa") print(resultado)

Consulta com grafico

resultado = consultar_banco_dados("Grafico de vendas por mes", "casaa") print(resultado) Como API REST python Copiar Editar from mcp_agent_db.main import app import uvicorn

Iniciar servidor

uvicorn.run(app, host="0.0.0.0", port=8000) 🌐 API Endpoints POST /api/consulta Realizar consultas em linguagem natural

{ "pergunta": "TOP 10 clientes que mais compraram", "slug": "casaa" } GET /api/schemas Listar schemas disponiveis

POST /api/grafico Gerar graficos a partir dos dados

{ "pergunta": "Grafico de vendas por vendedor", "tipo_grafico": "bar", "slug": "casaa" } 📱 Integracao React Native Instalacao no React Native

npm install axios Exemplo de uso

import axios from 'axios'

const API_BASE_URL = 'http://seu-servidor:8000'

// Servico para consultas export const consultarDados = async (pergunta, slug = 'casaa') => { try { const response = await axios.post(${API_BASE_URL}/api/consulta, { pergunta, slug, }) return response.data } catch (error) { throw new Error(Erro na consulta: ${error.message}) } }

// Servico para graficos export const gerarGrafico = async ( pergunta, tipoGrafico = 'bar', slug = 'casaa' ) => { try { const response = await axios.post(${API_BASE_URL}/api/grafico, { pergunta, tipo_grafico: tipoGrafico, slug, }) return response.data } catch (error) { throw new Error(Erro ao gerar grafico: ${error.message}) } }

// Componente React Native import React, { useState } from 'react' import { View, Text, TextInput, TouchableOpacity, ScrollView, } from 'react-native'

const ConsultaScreen = () => { const [pergunta, setPergunta] = useState('') const [resultado, setResultado] = useState('') const [loading, setLoading] = useState(false)

const handleConsulta = async () => { if (!pergunta.trim()) return

setLoading(true)
try {
  const response = await consultarDados(pergunta)
  setResultado(response.resultado || response)
} catch (error) {
  setResultado(`Erro: ${error.message}`)
} finally {
  setLoading(false)
}

}

return ( <View style={{ flex: 1, padding: 20 }}> <Text style={{ fontSize: 24, fontWeight: 'bold', marginBottom: 20 }}> Consulta Inteligente

  <TextInput
    style={{
      borderWidth: 1,
      borderColor: '#ccc',
      padding: 10,
      marginBottom: 10,
      borderRadius: 5,
    }}
    placeholder="Digite sua pergunta..."
    value={pergunta}
    onChangeText={setPergunta}
    multiline
  />

  <TouchableOpacity
    style={{
      backgroundColor: '#007bff',
      padding: 15,
      borderRadius: 5,
      marginBottom: 20,
    }}
    onPress={handleConsulta}
    disabled={loading}>
    <Text
      style={{ color: 'white', textAlign: 'center', fontWeight: 'bold' }}>
      {loading ? 'Consultando...' : 'Consultar'}
    </Text>
  </TouchableOpacity>

  <ScrollView style={{ flex: 1 }}>
    <Text style={{ fontSize: 16 }}>{resultado}</Text>
  </ScrollView>
</View>

) }

export default ConsultaScreen

🔧 Desenvolvimento Estrutura do Projeto Executar em desenvolvimento

cd mcp_agent_db python main.py 📄 Licenca MIT License - veja o arquivo LICENSE para detalhes.

🤝 Contribuicao Contribuicoes sao bem-vindas! Por favor, abra uma issue ou pull request.

📞 Suporte Email: leokaique7@gmail.com

GitHub: https://github.com/leokaique/mcp_postgres

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published